From 9734f544ab5c5bb8183e702d7145a79e54ce7df0 Mon Sep 17 00:00:00 2001 From: Gunnar Grosch Date: Sun, 25 Oct 2020 11:08:22 +0100 Subject: [PATCH] Added support for AppConfig and updated readme --- README.md | 42 ++++++++++++++++++++++++++++++++++++++---- lib/failure.js | 42 +++++++++++++++++++++++++++++++----------- package-lock.json | 7 ++++++- package.json | 5 +++-- 4 files changed, 78 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 73c4288..435601d 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ ## Description -`failure-lambda` is a small Node module for injecting failure into AWS Lambda (https://aws.amazon.com/lambda). It offers a simple failure injection wrapper for your Lambda handler where you then can choose to inject failure by setting the `failureMode` to `latency`, `exception`, `denylist`, `diskspace` or `statuscode`. You control your failure injection using SSM Parameter Store. +`failure-lambda` is a small Node module for injecting failure into AWS Lambda (https://aws.amazon.com/lambda). It offers a simple failure injection wrapper for your Lambda handler where you then can choose to inject failure by setting the `failureMode` to `latency`, `exception`, `denylist`, `diskspace` or `statuscode`. You control your failure injection using SSM Parameter Store or [AWS AppConfig](https://docs.aws.amazon.com/appconfig/latest/userguide/what-is-appconfig.html). -## How to install +## How to install with parameter in SSM Parameter Store 1. Install `failure-lambda` module using NPM. ```bash @@ -28,11 +28,40 @@ exports.handler = failureLambda(async (event, context) => { aws ssm put-parameter --region eu-west-1 --name failureLambdaConfig --type String --overwrite --value "{\"isEnabled\": false, \"failureMode\": \"latency\", \"rate\": 1, \"minLatency\": 100, \"maxLatency\": 400, \"exceptionMsg\": \"Exception message!\", \"statusCode\": 404, \"diskSpace\": 100, \"denylist\": [\"s3.*.amazonaws.com\", \"dynamodb.*.amazonaws.com\"]}" ``` 5. Add an environment variable to your Lambda function with the key FAILURE_INJECTION_PARAM and the value set to the name of your parameter in SSM Parameter Store. -6. Try it out! +6. Add permissions to the parameter for your Lambda function. +7. Try it out! + +## How to install with hosted configuration in AWS AppConfig + +1. Install `failure-lambda` module using NPM. +```bash +npm install failure-lambda +``` +2. Add the module to your Lambda function code. +```js +const failureLambda = require('failure-lambda') +``` +3. Wrap your handler. +```js +exports.handler = failureLambda(async (event, context) => { + ... +}) +``` +4. Create Application, Environment, Configuration Profile, and Hosted Configuration in AppConfig console. +5. Deploy a version of the configuration. +6. Add the AWS AppConfig layer for Lambda extensions to your Lambda function. [See details](https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-integration-lambda-extensions.html). +7. Add environment variables to your Lambda function. +```bash +FAILURE_APPCONFIG_APPLICATION: YOUR APPCONFIG APPLICATION +FAILURE_APPCONFIG_ENVIRONMENT: YOUR APPCONFIG ENVIRONMENT +FAILURE_APPCONFIG_CONFIGURATION: YOUR APPCONFIG CONFIGURATION PROFILE +``` +8. Add permissions to the AppConfig Application, Environment, and Configuration Profile for your Lambda function. +9. Try it out! ## Usage -Edit the values of your parameter in SSM Parameter Store to use the failure injection module. +Edit the values of your parameter in SSM Parameter Store or hosted configuration in AWS AppConfig to use the failure injection module. * `isEnabled: true` means that failure is injected into your Lambda function. * `isEnabled: false` means that the failure injection module is disabled and no failure is injected. @@ -58,6 +87,11 @@ Inspired by Yan Cui's articles on latency injection for AWS Lambda (https://hack ## Changelog +### 2020-10-25 v0.4.0 + +* Added optional support for AWS AppConfig, allowing to validate failure configuration, deploy configuration using gradual or non-gradual deploy strategy, monitor deployed configuration with automatical rollback if CloudWatch Alarms is configured, and caching of configuration. +* Hardcoded default configuration with `isEnabled: false`, to use if issues loading configuration from Parameter Store or AppConfig. + ### 2020-10-21 v0.3.1 * Change mitm mode back to connect to fix issue with all connections being blocked. diff --git a/lib/failure.js b/lib/failure.js index 8b5ec29..c5eda90 100644 --- a/lib/failure.js +++ b/lib/failure.js @@ -1,26 +1,46 @@ 'use strict' const aws = require('aws-sdk') const ssm = new aws.SSM() +const fetch = require('node-fetch') const childProcess = require('child_process') const Mitm = require('mitm') -async function getConfig() { - try { - let params = { - Name: process.env.FAILURE_INJECTION_PARAM +async function getConfig () { + const defaults = { + isEnabled: false + } + if (process.env.FAILURE_APPCONFIG_CONFIGURATION) { + try { + const url = 'http://localhost:2772/applications/' + process.env.FAILURE_APPCONFIG_APPLICATION + '/environments/' + process.env.FAILURE_APPCONFIG_ENVIRONMENT + '/configurations/' + process.env.FAILURE_APPCONFIG_CONFIGURATION + const response = await fetch(url) + const json = await response.json() + return json + } catch (err) { + console.error(err) + return defaults + } + } else if (process.env.FAILURE_INJECTION_PARAM) { + try { + let params = { + Name: process.env.FAILURE_INJECTION_PARAM + } + let response = await ssm.getParameter(params).promise() + let json = JSON.parse(response.Parameter.Value) + return json + } catch (err) { + console.error(err) + return defaults } - let request = await ssm.getParameter(params).promise() - return request.Parameter.Value - } catch (err) { - console.error(err) - throw err + } else { + return defaults } } var injectFailure = function (fn) { return async function () { try { - let configResponse = await getConfig() - let config = JSON.parse(configResponse) + // let configResponse = await getConfig() + // let config = JSON.parse(configResponse) + let config = await getConfig() if (config.isEnabled === true && Math.random() < config.rate) { if (config.failureMode === 'latency') { let latencyRange = config.maxLatency - config.minLatency diff --git a/package-lock.json b/package-lock.json index 02c0a6e..5b8b1d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "failure-lambda", - "version": "0.3.1", + "version": "0.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1093,6 +1093,11 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", diff --git a/package.json b/package.json index baaeea8..632a0e7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "failure-lambda", - "version": "0.3.1", + "version": "0.4.0", "description": "Module for failure injection into AWS Lambda", "main": "./lib/failure.js", "scripts": { @@ -34,6 +34,7 @@ "eslint-plugin-standard": "^4.0.1" }, "dependencies": { - "mitm": "^1.7.1" + "mitm": "^1.7.1", + "node-fetch": "^2.6.1" } }