@agiledigital/serverless-parameter-subscriber
Advanced tools
Comparing version 0.0.11 to 0.1.1
@@ -7,4 +7,4 @@ { | ||
"dependencies": { | ||
"@agiledigital/serverless-parameter-subscriber": "^0.0.1" | ||
"@agiledigital/serverless-parameter-subscriber": "^0.0.11" | ||
} | ||
} |
import { Lambda, SSM } from 'aws-sdk'; | ||
import { ParameterList } from 'aws-sdk/clients/ssm'; | ||
import { Parameter } from 'aws-sdk/clients/ssm'; | ||
@@ -46,3 +46,28 @@ interface CloudWatchRuleEvent { | ||
interface ItemPage<T> { | ||
readonly position?: string; | ||
readonly items?: readonly T[]; | ||
} | ||
/** | ||
* Some AWS endpoints will return a list of items and a position indicator from | ||
* which you can fetch more items. This is a utility function to fetch all items | ||
* from that endpoint. | ||
* | ||
* @param fetch function used to fetch items from a certain position | ||
* @param previousPosition the position returned by the previous request | ||
*/ | ||
export const allFromPaged = async <T>( | ||
fetch: (position?: string) => Promise<ItemPage<T>>, | ||
previousPosition?: string | ||
): Promise<readonly T[]> => { | ||
const { position, items } = await fetch(previousPosition); | ||
const itemArray = items === undefined ? [] : items; | ||
return position === undefined || position === null | ||
? itemArray | ||
: itemArray.concat(await allFromPaged(fetch, position)); | ||
}; | ||
/** | ||
* Updates the Lambda Functions environment variables value to the updated | ||
@@ -55,3 +80,3 @@ * parameter value. The environment variable to update is specified in the | ||
const updateLambdaFunctions = (updatedParamValue: string) => async ( | ||
parameter: SSM.Parameter | ||
parameter: Parameter | ||
): Promise<string> => { | ||
@@ -97,28 +122,23 @@ console.log( | ||
* Fetches the SSM parameters by its path. | ||
* @param subscriberParamPath subscriber parameter's path of the updated parameter. | ||
* @param nextToken for fetching next batch of parameters. | ||
* | ||
* @param subscriberParamPath subscriber parameter's path for the updated parameter. | ||
* @returns a list of subscriber parameters for an updated parameter | ||
*/ | ||
const fetchParametersByPath = async ( | ||
subscriberParamPath: string, | ||
nextToken?: string | ||
): Promise<ParameterList> => { | ||
const parameterResult = await ssm | ||
.getParametersByPath({ | ||
Path: subscriberParamPath, | ||
NextToken: nextToken, | ||
Recursive: true, | ||
}) | ||
.promise(); | ||
subscriberParamPath: string | ||
): Promise<ReadonlyArray<Parameter>> => { | ||
return allFromPaged<Parameter>(async (nextToken?: string) => { | ||
const { Parameters, NextToken } = await ssm | ||
.getParametersByPath({ | ||
Path: subscriberParamPath, | ||
NextToken: nextToken, | ||
Recursive: true, | ||
}) | ||
.promise(); | ||
const parameters = parameterResult.Parameters ?? []; | ||
if (parameterResult.NextToken === undefined) { | ||
return parameters; | ||
} else { | ||
const nextBatchParams = await fetchParametersByPath( | ||
subscriberParamPath, | ||
parameterResult.NextToken | ||
); | ||
return [...parameters, ...nextBatchParams]; | ||
} | ||
return { | ||
items: Parameters, | ||
position: NextToken, | ||
}; | ||
}); | ||
}; | ||
@@ -142,2 +162,5 @@ | ||
* on the subscriber parameter key path: /subscriber/myParam. | ||
* | ||
* @param event the CloudWatch Parameter update event | ||
* @returns promise resolving when the event has been handled. | ||
*/ | ||
@@ -144,0 +167,0 @@ module.exports.handler = async (event: CloudWatchRuleEvent) => { |
@@ -11,2 +11,7 @@ 'use strict'; | ||
/** | ||
* The Lambda we create will need access to all the parameters in the parameter | ||
* store. If this is a security issue for you then this plugin probably isn't | ||
* for you at this stage. | ||
*/ | ||
const policyDocument = JSON.stringify({ | ||
@@ -42,2 +47,5 @@ Version: '2012-10-17', | ||
/** | ||
* Standard assume-role policy document for an event handler. | ||
*/ | ||
const assumeRolePolicyDocument = JSON.stringify({ | ||
@@ -56,2 +64,7 @@ Version: '2012-10-17', | ||
/** | ||
* The lambda code is stored with the plugin to be pushed when it is first | ||
* loaded. It is compiled typescript but still short enough and readable enough | ||
* to be easily audited. | ||
*/ | ||
const lambdaCode = fs.readFileSync(`${__dirname}/../lambda/subscriber.zip`); | ||
@@ -78,4 +91,6 @@ | ||
/** | ||
* The Serverless Parameter Subscriber plugin allows you to specify parameters | ||
* in your lambda functions. See the README for more info about how to use this. | ||
*/ | ||
module.exports = class ServerlessParameterStoreSubscriber { | ||
module.exports = class ServerlessParameterSubscriber { | ||
constructor(serverless, options) { | ||
@@ -107,3 +122,21 @@ this.serverless = serverless; | ||
async getParam(key) { | ||
/** | ||
* Log a message to the CLI if verbose is set to true (i.e. the flag -v or | ||
* --verbose is passed to serverless). | ||
* | ||
* @param {string} message message to log | ||
*/ | ||
info(message) { | ||
if (this.options.verbose) { | ||
this.serverless.cli.log(message); | ||
} | ||
} | ||
/** | ||
* Get the value of a paramter from the parameter store by the key. | ||
* | ||
* @param {string} key the key of the parameter in the parameter store | ||
* @returns {string} the value of the parameter or '' if not found or not permitted | ||
*/ | ||
async paramValue(key) { | ||
try { | ||
@@ -118,2 +151,4 @@ const param = await this.ssm.getParameter({ Name: key }).promise(); | ||
/** | ||
* Add parameter subscribers for each function that includes parameters. This | ||
* function will mutate the template. See +addParamSubscriber+ for more info. | ||
*/ | ||
@@ -141,2 +176,10 @@ addParamSubscribers() { | ||
/** | ||
* Adds "subscription" parameters to the parameter store so that we can | ||
* keep a record of which Lambdas subscribe to which parameters. | ||
* | ||
* @param {object} template CloudFormation template to mutate | ||
* @param {string} paramName name of parameter to subscribe to | ||
* @param {string} envName name of environment variable to set in the lambda | ||
* @param {string} funcKey key of function in the serverless config | ||
* @param {string} funcName full name of lambda in AWS | ||
*/ | ||
@@ -158,2 +201,9 @@ addParamSubscriber(template, paramName, envName, funcKey, funcName) { | ||
/** | ||
* Add environment variables to the CloudFormation template during deployment | ||
* so that the function will be configured with the latest version of the | ||
* parameters it is subscribed to. | ||
* | ||
* @returns {Promise<void>} resolves when function env vars have been added | ||
*/ | ||
async addFunctionEnvVars() { | ||
@@ -175,5 +225,5 @@ const functions = this.serverless.service.functions; | ||
} | ||
funcConfig.Environment.Variables[envName] = await this.getParam( | ||
paramName | ||
); | ||
funcConfig.Environment.Variables[ | ||
envName | ||
] = await this.paramValue(paramName); | ||
} | ||
@@ -187,2 +237,9 @@ ) | ||
/** | ||
* Create the role required by the CloudWatch parameter update event | ||
* subscriber (lambda) so that it will have access to the parameter store and | ||
* the lambdas that subscribe to it. | ||
* | ||
* @returns {Promise<object>} the lambda role | ||
*/ | ||
async createLambdaRole() { | ||
@@ -201,2 +258,8 @@ console.info('Creating new role.'); | ||
/** | ||
* Return the Lambda Role used by the CloudWatch parameter update event | ||
* subscriber. | ||
* | ||
* @returns {Promise<object>} the lambda role | ||
*/ | ||
async lambdaRole() { | ||
@@ -209,7 +272,8 @@ return this.iam | ||
info(message) { | ||
if (this.options.verbose) { | ||
this.serverless.cli.log(message); | ||
} | ||
} | ||
/** | ||
* Create the Lambda that subscribes to the CloudWatch parameter update | ||
* events. | ||
* | ||
* @returns {Promise<object>} the Lambda config | ||
*/ | ||
async createFunction() { | ||
@@ -235,2 +299,8 @@ this.info(`Creating the Lambda required by ${pluginName}`); | ||
/** | ||
* Update the code of the Lambda that subscribes to the CloudWatch parameter | ||
* update events. | ||
* | ||
* @returns {Promise<object>} the Lambda config | ||
*/ | ||
async updateFunctionCode() { | ||
@@ -250,2 +320,7 @@ this.info(`Updating the Lambda required by ${pluginName}`); | ||
/** | ||
* Update the policy used by the CloudWatch parameter update event subscriber. | ||
* | ||
* @returns {Promise<void>} resolves when the policy has been updated | ||
*/ | ||
async updateLambdaPolicy() { | ||
@@ -262,2 +337,8 @@ this.info(`Updating the Lambda policy required by ${pluginName}`); | ||
/** | ||
* Get the Lambda that subscribes to the CloudWatch parameter update events or | ||
* reject the request. | ||
* | ||
* @returns {Promise<object>} the Lambda config | ||
*/ | ||
async subscriberLambda() { | ||
@@ -271,2 +352,9 @@ return this.lambda | ||
/** | ||
* Subscribe the given function to the CloudWatch events for updates to the | ||
* parameters store. | ||
* | ||
* @param {string} funcArn the ARN of the subscriber function | ||
* @returns {Promise<void>} resolves when the function has been subscribed | ||
*/ | ||
async updateEventRule(funcArn) { | ||
@@ -314,2 +402,7 @@ this.info(`Updating the CloudWatch Events rules required by ${pluginName}`); | ||
/** | ||
* Make sure the log group exists for the subscriber function. | ||
* | ||
* @returns {Promise<void>} resolves when the log group has been created | ||
*/ | ||
async ensureLogGroupExists() { | ||
@@ -326,2 +419,7 @@ return this.cloudWatchLogs | ||
/** | ||
* Deploy the CloudWatch Events parameter store update subscriber. | ||
* | ||
* @returns {Promise<void>} resolves when the subscriber has been deployed | ||
*/ | ||
async deployParamStoreSubscriber() { | ||
@@ -328,0 +426,0 @@ await this.updateLambdaPolicy(); |
@@ -10,3 +10,3 @@ { | ||
"name": "@agiledigital/serverless-parameter-subscriber", | ||
"version": "0.0.11", | ||
"version": "0.1.1", | ||
"description": "serverless plugin to make allow Lambdas to included dynamically updated parameters from the paramter store.", | ||
@@ -18,3 +18,4 @@ "scripts": { | ||
"pre-commit": "yarn test", | ||
"release": "yarn build && yarn publish" | ||
"release": "yarn build && yarn publish", | ||
"semantic-release": "semantic-release" | ||
}, | ||
@@ -34,2 +35,5 @@ "repository": { | ||
"devDependencies": { | ||
"@commitlint/cli": "^8.2.0", | ||
"@commitlint/config-angular": "^8.2.0", | ||
"@commitlint/config-conventional": "^8.2.0", | ||
"@types/aws-lambda": "^8.10.37", | ||
@@ -41,2 +45,3 @@ "@types/jest": "^24.0.23", | ||
"prettier": "^1.19.1", | ||
"semantic-release": "^15.13.31", | ||
"serverless-plugin-typescript": "^1.1.9", | ||
@@ -47,3 +52,4 @@ "typescript": "^3.7.3" | ||
"hooks": { | ||
"pre-commit": "yarn pre-commit" | ||
"pre-commit": "yarn pre-commit", | ||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS" | ||
} | ||
@@ -50,0 +56,0 @@ }, |
## serverless-parameter-subscriber | ||
[![CircleCI](https://circleci.com/gh/agiledigital/serverless-parameter-subscriber.svg?style=svg)](https://circleci.com/gh/agiledigital/serverless-parameter-subscriber) | ||
[![npm version](https://badge.fury.io/js/%40agiledigital%2Fserverless-parameter-subscriber.svg)](https://badge.fury.io/js/%40agiledigital%2Fserverless-parameter-subscriber) | ||
Not working yet! | ||
@@ -4,0 +7,0 @@ |
Sorry, the diff of this file is not supported yet
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
26974
15
628
23
12
1