Pull secrets from AWS
'use strict';
// Run with e.g. `node env.js npm test`
/* eslint-disable require-jsdoc */
/* eslint-disable no-sync */
/* eslint-disable no-console */
/* eslint-disable no-process-exit */
const {spawn} = require('child_process');
const fs = require('fs');
const _ = require('lodash');
const AWS = require('aws-sdk');
const debug = require('debug')('displace:env');
const yaml = require('js-yaml');
AWS.config.update({region: 'us-east-1'});
const ssm = new AWS.SSM();
const ENV = process.env.NODE_ENV === 'prod' || process.env.NODE_ENV === 'production' ? 'production' : 'dev';
const raw = yaml.safeLoad(fs.readFileSync('./secrets.yml'));
const secrets = raw[ENV];
if (!secrets) {
throw new Error(`No keys found for environement "${ENV}"`);
}
function getChunk(names) {
return new Promise((resolve, reject) => {
debug(`request ${names.join(', ')} from AWS SSM`);
ssm.getParameters({Names: names}, (err, data) => {
if (err) {
reject(err);
return;
}
debug(`received ${data.Parameters.length} params from AWS SSM`);
resolve(data);
});
});
}
function getParameters(names) {
const chunkSize = 10;
debug(`Requesting ${names.length} parameters from AWS SSM in ${chunkSize} item chunks`);
return Promise.all(_.chunk(names, chunkSize)
.map(getChunk))
.then((chunks) => _.mergeWith(...chunks, (obj, src) => _.isArray(obj) && obj.concat(src) || undefined))
.then((data) => {
for (const invalid of data.InvalidParameters) {
console.warn(`Could not retrieve parameter named "${invalid}"`);
}
debug(`resolving with ${data.Parameters.length} parameters from AWS SSM`);
return data.Parameters;
});
}
Promise.resolve()
.then(() => getParameters(Object.values(secrets)))
.then((results) => results.reduce((acc, result) => {
acc[result.Name] = result.Value;
return acc;
}, {}))
.then((results) => {
const env = {};
for (const [
envVar,
keyPath
] of Object.entries(secrets)) {
env[envVar] = results[keyPath];
}
return env;
})
.then((results) => new Promise((resolve) => {
const [
cmd,
...args
] = process.argv.slice(2);
const child = spawn(cmd, args, {
env: Object.assign({}, results, process.env),
stdio: 'inherit'
});
child.on('close', (code) => {
if (code) {
debug(`Child process exited with code ${code}`);
process.exit(code);
}
resolve();
});
}))
.catch((err) => {
console.error(err);
process.exit(64);
});
production:
AUTH0_AUDIENCE: /displace/production/auth0_audience
AUTH0_CONNECTION: /displace/production/auth0_connection
AUTH0_BACKEND_CLIENT_ID: /displace/production/auth0_backend_client_id
AUTH0_BACKEND_CLIENT_SECRET: /displace/production/auth0_backend_client_secret
AUTH0_DOMAIN: /displace/production/auth0_domain
AUTH0_FRONTEND_CLIENT_ID: /displace/production/auth0_frontend_client_id
AUTH0_TEST_SUITE_CLIENT_ID: /displace/production/auth0_test_suite_client_id
AUTH0_TEST_SUITE_CLIENT_SECRET: /displace/production/auth0_test_suite_client_secret
GITHUB_CLIENT_ID: /displace/production/github_client_id
GITHUB_CLIENT_SECRET: /displace/production/github_client_secret
GOOGLE_ANALYTICS_ID: /displace/production/google_analytics_id
SENTRY_AUTH_TOKEN: /displace/production/sentry_auth_token
SENTRY_DSN: /displace/production/sentry_dsn
SENTRY_DSN_PUBLIC: /displace/production/sentry_dsn_public
SENTRY_ORGANIZATION: /displace/production/sentry_organization
SENTRY_PROJECT: /displace/production/sentry_project
dev:
AUTH0_AUDIENCE: /displace/dev/auth0_audience
AUTH0_CONNECTION: /displace/dev/auth0_connection
AUTH0_BACKEND_CLIENT_ID: /displace/dev/auth0_backend_client_id
AUTH0_BACKEND_CLIENT_SECRET: /displace/dev/auth0_backend_client_secret
AUTH0_DOMAIN: /displace/dev/auth0_domain
AUTH0_FRONTEND_CLIENT_ID: /displace/dev/auth0_frontend_client_id
AUTH0_TEST_SUITE_CLIENT_ID: /displace/dev/auth0_test_suite_client_id
AUTH0_TEST_SUITE_CLIENT_SECRET: /displace/dev/auth0_test_suite_client_secret
GITHUB_CLIENT_ID: /displace/dev/github_client_id
GITHUB_CLIENT_SECRET: /displace/dev/github_client_secret
GOOGLE_ANALYTICS_ID: /displace/dev/google_analytics_id
SENTRY_AUTH_TOKEN: /displace/dev/sentry_auth_token
SENTRY_DSN: /displace/dev/sentry_dsn
SENTRY_DSN_PUBLIC: /displace/dev/sentry_dsn_public
SENTRY_ORGANIZATION: /displace/dev/sentry_organization
SENTRY_PROJECT: /displace/dev/sentry_project
SAUCE_ACCESS_KEY: /displace/dev/sauce_access_key
SAUCE_USERNAME: /displace/dev/sauce_username