Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(credstash): Add credstash support #14

Merged
merged 6 commits into from
Apr 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Jest",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}/node_modules/jest-cli/bin/jest.js",
"stopOnEntry": false,
"args": [
"--runInBand",
"${file}"
],
"cwd": "${workspaceRoot}",
"protocol": "legacy",
"runtimeArgs": [
"--nolazy"
],
"sourceMaps": true,
"outFiles": [
"${workspaceRoot}/dist/**/*.js"
]
},
{
"name": "TS-Node",
"type": "node",
"request": "launch",
"args": [
"${relativeFile}"
],
"runtimeArgs": [
"--nolazy",
"-r",
"ts-node/register"
],
"sourceMaps": true,
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"runtimeVersion": "8.10.0"
}
]
}
18 changes: 18 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"prettier.printWidth": 100,
"prettier.singleQuote": true,
"typescript.tsdk": "./node_modules/typescript/lib",
"search.exclude": {
"dist/": true
},
"[javascript]": {
"editor.formatOnSave": true
},
"[json]": {
"editor.formatOnSave": true
},
"[typescript]": {
"editor.formatOnSave": true
}
}

8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Define your configuration in a yaml file at the root of your application:
default_env: &default_env
SERVICE_URL: ${cft:my-stack.ServiceURL} ## Reference to an AWS CFT stack output
SOME_ENV_VARIABLE: ${env:SOME_ENV_VARIABLE} ## Reference to an external environment variable
SOME_CREDSTASH_VARIABLE: ${cred:SOME_CREDSTASH_VARIABLE} ## Reference to a credstash key
SOME_CONSTANT: SOME_CONSTANT

development:
Expand All @@ -36,15 +37,20 @@ staging:
<<: *default_env

production:
SOME_CONSTANT: OVERRIDE_FOR_PRODUCTION
<<: *default_env
SOME_CONSTANT: OVERRIDE_FOR_PRODUCTION
```

Then, run `yarn dotenvi -s <stage>` to generate a `.env` file for the stage desired (e.g., development, staging, production, etc...). Use the generated `.env` file in your normal processes using [dotenv](https://github.com/motdotla/dotenv).

Note that stages are not required in your yaml file - you can also define it without stages, in which case you should not specify a stage with the `-s` option when you run `dotenvi`.


## Configuration

Note that any AWS references (cred, cft, etc...) are currently hard-coded to us-east-1.


## Discussion

The main design goals of dotenvi are as follows:
Expand Down
1 change: 1 addition & 0 deletions env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ default_env: &default_env
IA_FARMAPI_ENV: ${env:IA_FARMAPI_ENV}
FOO: ${cft:forms-api-fargate-stack.ServiceURL}
BAZ: BAR
CRED: ${cred:IA_VPC_PRIVATE_SUBNET1_ID}

sandbox:
<<: *default_env
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"aws-sdk": "^2.224.1",
"jest": "^22.4.3",
"js-yaml": "^3.11.0",
"nodecredstash": "^2.0.2",
"ts-jest": "^22.4.4",
"typescript": "^2.8.1"
},
Expand Down
36 changes: 19 additions & 17 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ArgumentParser } from 'argparse';
import { Rewriter } from './rewriter';
import { ResolverMap, Document } from './types';
import { resolvers } from './resolvers';
import { validateDocument, writeFile } from './utils';

const parser = new ArgumentParser();
parser.addArgument(['-s', '--stage'], {
Expand All @@ -15,30 +16,31 @@ parser.addArgument(['-s', '--stage'], {
});
const args = parser.parseArgs();

function writeFile(document: { [name: string]: string }) {
let output = '';
const variables = Object.keys(document);
for (const variable of variables) {
output += `${variable}=${document[variable]}\n`;
}
fs.writeFileSync('.env', output);
}

let document;
try {
// TODO Load external resolvers

let document = yaml.safeLoad(fs.readFileSync('./env.yml', 'utf8')) as Document;
document = yaml.safeLoad(fs.readFileSync('./env.yml', 'utf8')) as Document;
if (args.stage) {
document = (document as any)[args.stage];
if (!document) {
throw new Error(`Could not locate stage ${args.stage} in file ${args.file}`);
}
}
const rewriter = new Rewriter(resolvers);
rewriter.rewrite(document).then(result => {
console.info(`Writing .env file to ${process.cwd()}/.env`);
writeFile(result);
});
} catch (e) {
console.error(`Could not load yaml ${e}`);

const errors = validateDocument(document);
if (errors.length) {
throw new Error(`Validation errors found while loading document. Did you forget to specify -s?: \n${errors.join("\n")}`);
}
} catch (error) {
console.error(`Could not load yaml ${error.stack}`);
process.exit(1);
}
const rewriter = new Rewriter(resolvers);
rewriter.rewrite(document).then(result => {
console.info(`Writing .env file to ${process.cwd()}/.env`);
writeFile(result);
}).catch((error: Error) => {
console.error(`Could not write .env file: ${error.stack}`);
process.exit(1);
});
11 changes: 11 additions & 0 deletions src/resolvers.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import * as AWS from 'aws-sdk';
import { CloudFormation, Config } from 'aws-sdk';
import { DescribeStacksOutput } from 'aws-sdk/clients/cloudformation';
import { promisify } from 'util';

import { ResolverMap } from './types';

const Credstash = require('nodecredstash');

export const resolvers: ResolverMap = {
cft: async (argument: string) => {

if (!AWS.config.region) {
AWS.config.update({ region: 'us-east-1' });
}
Expand Down Expand Up @@ -37,5 +40,13 @@ export const resolvers: ResolverMap = {
},
constant: async (argument: string) => {
return argument;
},
cred: async (argument: string) => {
const credstash = new Credstash({ awsOpts: { region: 'us-east-1' } });
const promisified = promisify(credstash.getSecret);
return promisified({ name: argument })
.catch((error: Error) => {
throw new Error(`Could not load value ${argument} from credstash: ${error.stack}`);
});
}
};
21 changes: 21 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as fs from 'fs';

export function writeFile(document: { [name: string]: string }) {
let output = '';
const keys = Object.keys(document);
for (const key of keys) {
output += `${key}=${document[key]}\n`;
}
fs.writeFileSync('.env', output);
}

export function validateDocument(document: any): string[] {
const errors = [];
const keys = Object.keys(document);
for (const key of keys) {
if (typeof document[key] !== 'string') {
errors.push(`${key} has an invalid value ${JSON.stringify(document[key])}`);
}
}
return errors;
}
27 changes: 27 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ acorn@^5.0.0, acorn@^5.3.0:
version "5.5.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9"

aes-js@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.1.tgz#89fd1f94ae51b4c72d62466adc1a7323ff52f072"

agent-base@4, agent-base@^4.1.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.0.tgz#9838b5c3392b962bad031e6a4c5e1024abec45ce"
Expand Down Expand Up @@ -417,6 +421,21 @@ aws-sdk@*, aws-sdk@^2.224.1:
xml2js "0.4.17"
xmlbuilder "4.2.1"

aws-sdk@^2.171.0:
version "2.225.1"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.225.1.tgz#6ec6994a6337f2b553b16f6576df34ea1ac314d8"
dependencies:
buffer "4.9.1"
events "1.1.1"
ieee754 "1.1.8"
jmespath "0.15.0"
querystring "0.2.0"
sax "1.2.1"
url "0.10.3"
uuid "3.1.0"
xml2js "0.4.17"
xmlbuilder "4.2.1"

aws-sign2@~0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
Expand Down Expand Up @@ -4300,6 +4319,14 @@ node-uuid@~1.4.7:
version "1.4.8"
resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907"

nodecredstash@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/nodecredstash/-/nodecredstash-2.0.2.tgz#2a36ae12534c4e975aafde1deafdd85773eb5dcb"
dependencies:
aes-js "^3.1.0"
aws-sdk "^2.171.0"
debug "^3.1.0"

noop-logger@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2"
Expand Down