|
1 |
| -'use strict' |
2 |
| -const aws = require('aws-sdk') |
3 |
| -const ssm = new aws.SSM() |
4 |
| -const fetch = require('node-fetch') |
5 |
| -const childProcess = require('child_process') |
6 |
| -const Mitm = require('mitm') |
| 1 | +"use strict"; |
| 2 | +const aws = require("aws-sdk"); |
| 3 | +const ssm = new aws.SSM(); |
| 4 | +const fetch = require("node-fetch"); |
| 5 | +const childProcess = require("child_process"); |
| 6 | +const Mitm = require("mitm"); |
7 | 7 |
|
8 |
| -async function getConfig () { |
9 |
| - const defaults = { |
10 |
| - isEnabled: false |
| 8 | +var mitm = null; |
| 9 | + |
| 10 | +function clearMitm() { |
| 11 | + if (mitm != null) { |
| 12 | + mitm.disable(); |
11 | 13 | }
|
| 14 | +} |
| 15 | + |
| 16 | +async function getConfig() { |
| 17 | + const defaults = { |
| 18 | + isEnabled: false, |
| 19 | + }; |
12 | 20 | if (process.env.FAILURE_APPCONFIG_CONFIGURATION) {
|
13 | 21 | try {
|
14 | 22 | if (process.env.AWS_APPCONFIG_EXTENSION_HTTP_PORT) {
|
15 |
| - var appconfigPort = process.env.AWS_APPCONFIG_EXTENSION_HTTP_PORT |
| 23 | + var appconfigPort = process.env.AWS_APPCONFIG_EXTENSION_HTTP_PORT; |
16 | 24 | } else {
|
17 |
| - var appconfigPort = 2772 |
| 25 | + var appconfigPort = 2772; |
18 | 26 | }
|
19 |
| - const url = 'http://localhost:' + appconfigPort + '/applications/' + process.env.FAILURE_APPCONFIG_APPLICATION + '/environments/' + process.env.FAILURE_APPCONFIG_ENVIRONMENT + '/configurations/' + process.env.FAILURE_APPCONFIG_CONFIGURATION |
20 |
| - const response = await fetch(url) |
21 |
| - const json = await response.json() |
22 |
| - return json |
| 27 | + const url = |
| 28 | + "http://localhost:" + |
| 29 | + appconfigPort + |
| 30 | + "/applications/" + |
| 31 | + process.env.FAILURE_APPCONFIG_APPLICATION + |
| 32 | + "/environments/" + |
| 33 | + process.env.FAILURE_APPCONFIG_ENVIRONMENT + |
| 34 | + "/configurations/" + |
| 35 | + process.env.FAILURE_APPCONFIG_CONFIGURATION; |
| 36 | + const response = await fetch(url); |
| 37 | + const json = await response.json(); |
| 38 | + return json; |
23 | 39 | } catch (err) {
|
24 |
| - console.error(err) |
25 |
| - return defaults |
| 40 | + console.error(err); |
| 41 | + return defaults; |
26 | 42 | }
|
27 | 43 | } else if (process.env.FAILURE_INJECTION_PARAM) {
|
28 | 44 | try {
|
29 | 45 | let params = {
|
30 |
| - Name: process.env.FAILURE_INJECTION_PARAM |
31 |
| - } |
32 |
| - let response = await ssm.getParameter(params).promise() |
33 |
| - let json = JSON.parse(response.Parameter.Value) |
34 |
| - return json |
| 46 | + Name: process.env.FAILURE_INJECTION_PARAM, |
| 47 | + }; |
| 48 | + let response = await ssm.getParameter(params).promise(); |
| 49 | + let json = JSON.parse(response.Parameter.Value); |
| 50 | + return json; |
35 | 51 | } catch (err) {
|
36 |
| - console.error(err) |
37 |
| - return defaults |
| 52 | + console.error(err); |
| 53 | + return defaults; |
38 | 54 | }
|
39 | 55 | } else {
|
40 |
| - return defaults |
| 56 | + return defaults; |
41 | 57 | }
|
42 | 58 | }
|
43 | 59 | var injectFailure = function (fn) {
|
44 | 60 | return async function () {
|
45 | 61 | try {
|
46 |
| - let config = await getConfig() |
| 62 | + let config = await getConfig(); |
| 63 | + |
| 64 | + if (config.isEnabled === false || config.failureMode != "denylist") { |
| 65 | + clearMitm(); |
| 66 | + } |
| 67 | + |
47 | 68 | if (config.isEnabled === true && Math.random() < config.rate) {
|
48 |
| - if (config.failureMode === 'latency') { |
49 |
| - let latencyRange = config.maxLatency - config.minLatency |
50 |
| - let setLatency = Math.floor(config.minLatency + Math.random() * latencyRange) |
51 |
| - console.log('Injecting ' + setLatency + ' ms latency.') |
52 |
| - await new Promise(resolve => setTimeout(resolve, setLatency)) |
53 |
| - } else if (config.failureMode === 'exception') { |
54 |
| - console.log('Injecting exception message: ' + config.exceptionMsg) |
55 |
| - throw new Error(config.exceptionMsg) |
56 |
| - } else if (config.failureMode === 'statuscode') { |
57 |
| - console.log('Injecting status code: ' + config.statusCode) |
58 |
| - let response = { statusCode: config.statusCode } |
59 |
| - return response |
60 |
| - } else if (config.failureMode === 'diskspace') { |
61 |
| - console.log('Injecting disk space: ' + config.diskSpace + ' MB') |
62 |
| - childProcess.spawnSync('dd', ['if=/dev/zero', 'of=/tmp/diskspace-failure-' + Date.now() + '.tmp', 'count=1000', 'bs=' + config.diskSpace * 1000]) |
63 |
| - } else if (config.failureMode === 'denylist') { |
64 |
| - console.log('Injecting dependency failure through a network block for denylisted sites: ' + config.denylist) |
65 |
| - let mitm = Mitm() |
66 |
| - let blRegexs = [] |
| 69 | + if (config.failureMode === "latency") { |
| 70 | + let latencyRange = config.maxLatency - config.minLatency; |
| 71 | + let setLatency = Math.floor( |
| 72 | + config.minLatency + Math.random() * latencyRange |
| 73 | + ); |
| 74 | + console.log("Injecting " + setLatency + " ms latency."); |
| 75 | + await new Promise((resolve) => setTimeout(resolve, setLatency)); |
| 76 | + } else if (config.failureMode === "exception") { |
| 77 | + console.log("Injecting exception message: " + config.exceptionMsg); |
| 78 | + throw new Error(config.exceptionMsg); |
| 79 | + } else if (config.failureMode === "statuscode") { |
| 80 | + console.log("Injecting status code: " + config.statusCode); |
| 81 | + let response = { statusCode: config.statusCode }; |
| 82 | + return response; |
| 83 | + } else if (config.failureMode === "diskspace") { |
| 84 | + console.log("Injecting disk space: " + config.diskSpace + " MB"); |
| 85 | + childProcess.spawnSync("dd", [ |
| 86 | + "if=/dev/zero", |
| 87 | + "of=/tmp/diskspace-failure-" + Date.now() + ".tmp", |
| 88 | + "count=1000", |
| 89 | + "bs=" + config.diskSpace * 1000, |
| 90 | + ]); |
| 91 | + } else if (config.failureMode === "denylist") { |
| 92 | + console.log( |
| 93 | + "Injecting dependency failure through a network block for denylisted sites: " + |
| 94 | + config.denylist |
| 95 | + ); |
| 96 | + |
| 97 | + // if the global mitm doesn't yet exist, create it now |
| 98 | + if (mitm == null) { |
| 99 | + mitm = Mitm(); |
| 100 | + } |
| 101 | + mitm.enable(); |
| 102 | + |
| 103 | + // attach a handler to filter the configured deny patterns |
| 104 | + let blRegexs = []; |
67 | 105 | config.denylist.forEach(function (regexStr) {
|
68 |
| - blRegexs.push(new RegExp(regexStr)) |
69 |
| - }) |
70 |
| - mitm.on('connect', function (socket, opts) { |
71 |
| - let block = false |
| 106 | + blRegexs.push(new RegExp(regexStr)); |
| 107 | + }); |
| 108 | + mitm.on("connect", function (socket, opts) { |
| 109 | + let block = false; |
72 | 110 | blRegexs.forEach(function (blRegex) {
|
73 | 111 | if (blRegex.test(opts.host)) {
|
74 |
| - console.log('Intercepted network connection to ' + opts.host) |
75 |
| - block = true |
| 112 | + console.log("Intercepted network connection to " + opts.host); |
| 113 | + block = true; |
76 | 114 | }
|
77 |
| - }) |
| 115 | + }); |
78 | 116 | if (block) {
|
79 |
| - socket.end() |
| 117 | + socket.end(); |
80 | 118 | } else {
|
81 |
| - socket.bypass() |
| 119 | + socket.bypass(); |
82 | 120 | }
|
83 |
| - }) |
| 121 | + }); |
| 122 | + |
| 123 | + // remove any previously attached handlers, leaving only the most recently added |
| 124 | + while (typeof mitm._events.connect != "function") { |
| 125 | + mitm.removeListener("connect", mitm._events.connect[0]); |
| 126 | + } |
84 | 127 | }
|
85 | 128 | }
|
86 |
| - return fn.apply(this, arguments) |
| 129 | + return fn.apply(this, arguments); |
87 | 130 | } catch (ex) {
|
88 |
| - console.log(ex) |
89 |
| - throw ex |
| 131 | + console.log(ex); |
| 132 | + throw ex; |
90 | 133 | }
|
91 |
| - } |
92 |
| -} |
| 134 | + }; |
| 135 | +}; |
93 | 136 |
|
94 |
| -module.exports = injectFailure |
| 137 | +module.exports = injectFailure; |
0 commit comments