Last active
April 15, 2021 07:45
-
-
Save aackerman/93d86b780ef7e951b59351dcc99af1b1 to your computer and use it in GitHub Desktop.
Parameter Store Config
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict' | |
const EventEmitter = require('events') | |
// default expiry 3 mins | |
const DEFAULT_EXPIRY = 3 * 60 * 1000 | |
const create = ({ keys, ssm, keyPrefix = '', expiryMs = DEFAULT_EXPIRY }) => { | |
let isRefreshing = false | |
if (!keys || !Array.isArray(keys) || keys.length === 0) { | |
throw new Error('Provide a non-empty array of config keys') | |
} | |
if (expiryMs <= 0) { | |
throw new Error('Specify an expiry (ms) greater than 0') | |
} | |
let cache = { | |
expiration: new Date(0), | |
items: {} | |
} | |
let eventEmitter = new EventEmitter() | |
let validate = (keys, params) => { | |
let missing = keys.filter(k => params[k] === undefined) | |
if (missing.length > 0) { | |
throw new Error(`Missing SSM Parameter Store keys: ${missing}`) | |
} | |
} | |
let fetchParameters = async () => { | |
if (process.env.NODE_ENV === 'production') { | |
let req = { | |
Names: keys.map(k => `${keyPrefix}${k}`), | |
WithDecryption: true | |
} | |
let { | |
Parameters | |
} = await ssm.getParameters(req).promise() | |
let params = {} | |
for (let p of Parameters) { | |
let name = p.Name.replace(keyPrefix, '') | |
params[name] = p.Value | |
} | |
return params | |
} else { | |
let params = {} | |
for (let key of keys) { | |
params[key] = process.env[key] | |
} | |
return params | |
} | |
} | |
let refresh = async () => { | |
if (isRefreshing) { return } | |
isRefreshing = true | |
console.log(`Refreshing SSM Parameter Store keys: ${keys}`) | |
try { | |
let params = await fetchParameters() | |
validate(keys, params) | |
console.log(`Successfully refreshed SSM Parameter Store keys: ${keys}`) | |
let now = new Date() | |
cache.expiration = new Date(now.getTime() + expiryMs) | |
cache.items = params | |
eventEmitter.emit('refresh') | |
} finally { | |
isRefreshing = false | |
} | |
} | |
let getValue = (key) => { | |
let now = new Date() | |
if (now >= cache.expiration) { | |
refresh() | |
} | |
return cache.items[key] | |
} | |
let config = { | |
refresh: refresh, | |
onRefresh: listener => eventEmitter.addListener('refresh', listener), | |
onRefreshError: listener => eventEmitter.addListener('refreshError', listener) | |
} | |
let defaultKeys = Object.keys(config) | |
for (let key of keys) { | |
key = key.replace(keyPrefix, '') | |
Object.defineProperty(config, key, { | |
get() { return getValue(key) }, | |
enumerable: true, | |
configurable: false | |
}) | |
} | |
// catch usage of undefined keys | |
if (process.env.NODE_ENV !== 'production') { | |
return new Proxy(config, { | |
get(target, key) { | |
if (keys.includes(key) === false && defaultKeys.includes(key) === false) { | |
throw new Error(`Key '${key}' is not defined in config keys`) | |
} | |
return target[key] | |
} | |
}) | |
} | |
return config | |
} | |
module.exports = { | |
create | |
} |
Are you okay to explain how to utilise this config? I'm using the following but it doesn't seem to work:
const AWS = require('aws-sdk');
const configClient = require('./lib/configClient.js'
const ssm = new AWS.SSM();
module.exports.handler = async (event) => {
const keys = ['foo'];
const config = await configClient.create({keys, ssm);
const keySecret = config.foo;
}
can you please explain how will you resolve the promise inside getValue
https://gist.github.com/aackerman/93d86b780ef7e951b59351dcc99af1b1#file-gistfile1-js-L78
Thanks a lot!, your code works great.
does this really worked for you on production?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks a lot!, your code works great.