Skip to content

Commit afce4eb

Browse files
authored
Merge pull request #8 from gunnargrosch/develop
Blacklist failure feature
2 parents a45dec3 + e2cf89a commit afce4eb

File tree

6 files changed

+146
-14
lines changed

6 files changed

+146
-14
lines changed

README.md

+14-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Description
44

5-
`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`, `statuscode` or `diskspace`. You control your failure injection using SSM Parameter Store.
5+
`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`, `blacklist`, `diskspace` or `statuscode`. You control your failure injection using SSM Parameter Store.
66

77
## How to install
88

@@ -22,10 +22,10 @@ exports.handler = failureLambda(async (event, context) => {
2222
```
2323
4. Create a parameter in SSM Parameter Store.
2424
```json
25-
{"isEnabled": false, "failureMode": "latency", "rate": 1, "minLatency": 100, "maxLatency": 400, "exceptionMsg": "Exception message!", "statusCode": 404, "diskSpace": 100}
25+
{"isEnabled": false, "failureMode": "latency", "rate": 1, "minLatency": 100, "maxLatency": 400, "exceptionMsg": "Exception message!", "statusCode": 404, "diskSpace": 100, "blacklist": ["s3.*.amazonaws.com", "dynamodb.*.amazonaws.com"]}
2626
```
2727
```bash
28-
aws ssm put-parameter --region eu-north-1 --name failureLambdaConfig --type String --overwrite --value "{\"isEnabled\": false, \"failureMode\": \"latency\", \"rate\": 1, \"minLatency\": 100, \"maxLatency\": 400, \"exceptionMsg\": \"Exception message!\", \"statusCode\": 404, \"diskSpace\": 100}"
28+
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, \"blacklist\": [\"s3.*.amazonaws.com\", \"dynamodb.*.amazonaws.com\"]}"
2929
```
3030
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.
3131
6. Try it out!
@@ -36,12 +36,13 @@ Edit the values of your parameter in SSM Parameter Store to use the failure inje
3636

3737
* `isEnabled: true` means that failure is injected into your Lambda function.
3838
* `isEnabled: false` means that the failure injection module is disabled and no failure is injected.
39-
* `failureMode` selects which failure you want to inject. The options are `latency`, `exception` or `statuscode` as explained below.
39+
* `failureMode` selects which failure you want to inject. The options are `latency`, `exception`, `blacklist`, `diskspace` or `statuscode` as explained below.
4040
* `rate` controls the rate of failure. 1 means that failure is injected on all invocations and 0.5 that failure is injected on about half of all invocations.
4141
* `minLatency` and `maxLatency` is the span of latency in milliseconds injected into your function when `failureMode` is set to `latency`.
4242
* `exceptionMsg` is the message thrown with the exception created when `failureMode` is set to `exception`.
4343
* `statusCode` is the status code returned by your function when `failureMode` is set to `statuscode`.
4444
* `diskSpace` is size in MB of the file created in tmp when `failureMode` is set to `diskspace`.
45+
* `blacklist` is an array of regular expressions, if a connection is made to a host matching one of the regular expressions it will be blocked.
4546

4647
## Example
4748

@@ -57,9 +58,14 @@ Inspired by Yan Cui's articles on latency injection for AWS Lambda (https://hack
5758

5859
## Changelog
5960

61+
### 2020-02-17 v0.2.0
62+
63+
* Added blacklist failure.
64+
* Updated example application to retrive file from S3 based on URL from DynamoDB.
65+
6066
### 2020-02-13 v0.1.1
6167

62-
* Fixed issue with exception injection not throwing the exception. Thanks to [Jason Barto](https://github.com/jpbarto)!
68+
* Fixed issue with exception injection not throwing the exception.
6369

6470
### 2019-12-30 v0.1.0
6571

@@ -70,6 +76,8 @@ Inspired by Yan Cui's articles on latency injection for AWS Lambda (https://hack
7076

7177
* Initial release
7278

73-
## Authors
79+
## Contributors
7480

7581
**Gunnar Grosch** - [GitHub](https://github.com/gunnargrosch) | [Twitter](https://twitter.com/gunnargrosch) | [LinkedIn](https://www.linkedin.com/in/gunnargrosch/)
82+
83+
**Jason Barto** - [GitHub](https://github.com/jpbarto) | [Twitter](https://twitter.com/Jason_Barto) | [LinkedIn](https://www.linkedin.com/in/jasonbarto)

example/index.js

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,40 @@
11
'use strict'
22
const failureLambda = require('failure-lambda')
33
const fs = require('fs')
4+
const AWS = require('aws-sdk')
5+
const s3 = new AWS.S3()
6+
const dynamoDb = new AWS.DynamoDB.DocumentClient()
47
let response
58

69
exports.handler = failureLambda(async (event, context) => {
710
try {
8-
fs.writeFile('/tmp/example-' + Date.now() + '.tmp', 'Contents', (err) => {
11+
let fileName = Date.now() + '.tmp'
12+
let contents = 'Hello failureLambda!'
13+
fs.writeFile('/tmp/' + fileName, contents, (err) => {
14+
if (err) throw err
15+
})
16+
let s3Params = {
17+
Bucket: process.env.FAILURE_INJECTION_BUCKET,
18+
Key: fileName,
19+
Body: contents
20+
}
21+
s3.upload(s3Params, (err) => {
22+
if (err) throw err
23+
})
24+
let ddbParams = {
25+
TableName: process.env.FAILURE_INJECTION_TABLE,
26+
Item: {
27+
id: Date.now(),
28+
contents: contents
29+
}
30+
}
31+
dynamoDb.put(ddbParams, (err) => {
932
if (err) throw err
1033
})
1134
response = {
1235
statusCode: 200,
1336
body: JSON.stringify({
14-
message: 'Hello failureLambda!'
37+
message: contents
1538
})
1639
}
1740
} catch (err) {

example/serverless.yml

+59-3
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,53 @@ provider:
99
Action:
1010
- ssm:GetParameters
1111
- ssm:GetParameter
12-
Resource: "arn:aws:ssm:${opt:region, self:provider.region}:*:parameter/${self:service}-${opt:stage, self:provider.stage}-failureLambdaExample"
12+
Resource:
13+
Fn::Join:
14+
- ''
15+
- - 'arn:aws:ssm:${opt:region, self:provider.region}:*:parameter/'
16+
- Ref: failureLambdaParameter
17+
- Effect: Allow
18+
Action:
19+
- s3:ListBucket
20+
Resource:
21+
Fn::Join:
22+
- ''
23+
- - 'arn:aws:s3:::'
24+
- Ref: failureLambdaBucket
25+
- Effect: Allow
26+
Action:
27+
- s3:GetObject
28+
- s3:PutObject
29+
Resource:
30+
Fn::Join:
31+
- ''
32+
- - 'arn:aws:s3:::'
33+
- Ref: failureLambdaBucket
34+
- '/*'
35+
- Effect: Allow
36+
Action:
37+
- dynamodb:Query
38+
- dynamodb:Scan
39+
- dynamodb:GetItem
40+
- dynamodb:PutItem
41+
- dynamodb:UpdateItem
42+
- dynamodb:DeleteItem
43+
Resource:
44+
Fn::Join:
45+
- ''
46+
- - 'arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/'
47+
- Ref: failureLambdaTable
1348
functions:
1449
failureLambdaExample:
1550
handler: index.handler
1651
timeout: 3
1752
environment:
1853
FAILURE_INJECTION_PARAM:
1954
Ref: failureLambdaParameter
55+
FAILURE_INJECTION_BUCKET:
56+
Ref: failureLambdaBucket
57+
FAILURE_INJECTION_TABLE:
58+
Ref: failureLambdaTable
2059
events:
2160
- http:
2261
path: failureLambdaExample/
@@ -27,9 +66,26 @@ resources:
2766
failureLambdaParameter:
2867
Type: 'AWS::SSM::Parameter'
2968
Properties:
30-
Name: ${self:service}-${opt:stage, self:provider.stage}-failureLambdaExample
3169
Type: String
32-
Value: '{"isEnabled": false, "failureMode": "latency", "rate": 1, "minLatency": 100, "maxLatency": 400, "exceptionMsg": "Exception message!", "statusCode": 404, "diskSpace": 100}'
70+
Value: '{"isEnabled": false, "failureMode": "latency", "rate": 1, "minLatency": 100, "maxLatency": 400, "exceptionMsg": "Exception message!", "statusCode": 404, "diskSpace": 100, "blacklist": ["s3.*.amazonaws.com", "dynamodb.*.amazonaws.com"]}'
71+
failureLambdaBucket:
72+
Type: 'AWS::S3::Bucket'
73+
Properties:
74+
VersioningConfiguration:
75+
Status: Suspended
76+
failureLambdaTable:
77+
Type: 'AWS::DynamoDB::Table'
78+
DeletionPolicy: Delete
79+
Properties:
80+
AttributeDefinitions:
81+
-
82+
AttributeName: id
83+
AttributeType: N
84+
KeySchema:
85+
-
86+
AttributeName: id
87+
KeyType: HASH
88+
BillingMode: PAY_PER_REQUEST
3389
package:
3490
exclude:
3591
- .vscode

lib/failure.js

+22
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
const aws = require('aws-sdk')
33
const ssm = new aws.SSM()
44
const childProcess = require('child_process')
5+
const Mitm = require('mitm')
56

67
async function getConfig() {
78
try {
@@ -36,6 +37,27 @@ var injectFailure = function (fn) {
3637
} else if (config.failureMode === 'diskspace') {
3738
console.log('Injecting disk space: ' + config.diskSpace + ' MB')
3839
childProcess.spawnSync('dd', ['if=/dev/zero', 'of=/tmp/diskspace-failure-' + Date.now() + '.tmp', 'count=1000', 'bs=' + config.diskSpace * 1000])
40+
} else if (config.failureMode === 'blacklist') {
41+
console.log('Injecting dependency failure through a network blackhole for blacklisted sites: ' + config.blacklist)
42+
let mitm = Mitm()
43+
let blRegexs = []
44+
config.blacklist.forEach(function (regexStr) {
45+
blRegexs.push(new RegExp(regexStr))
46+
})
47+
mitm.on('connect', function (socket, opts) {
48+
let block = false
49+
blRegexs.forEach(function (blRegex) {
50+
if (blRegex.test(opts.host)) {
51+
console.log('Intercepted network connection to ' + opts.host)
52+
block = true
53+
}
54+
})
55+
if (block) {
56+
socket.end()
57+
} else {
58+
socket.bypass()
59+
}
60+
})
3961
}
4062
}
4163
return fn.apply(this, arguments)

package-lock.json

+22-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "failure-lambda",
3-
"version": "0.1.1",
3+
"version": "0.2.0",
44
"description": "Module for failure injection into AWS Lambda",
55
"main": "./lib/failure.js",
66
"scripts": {
@@ -33,5 +33,7 @@
3333
"eslint-plugin-promise": "^4.2.1",
3434
"eslint-plugin-standard": "^4.0.1"
3535
},
36-
"dependencies": {}
36+
"dependencies": {
37+
"mitm": "^1.7.0"
38+
}
3739
}

0 commit comments

Comments
 (0)