1 | #!/usr/bin/env node
|
2 |
|
3 | const os = require('os')
|
4 | const packageJson = require('../package.json')
|
5 | const slsArt = require('../lib/')
|
6 |
|
7 | /**
|
8 | * Determine whether any of the given flags exists in the given argv (object representation)
|
9 | * @param flags The set flags to look for
|
10 | * @param argv The object containing the processed CLI arguments
|
11 | * @returns boolean Whether one of the given flags was a truthy value within the given argv object.
|
12 | */
|
13 | const hasFlag = (flags, argv) => flags.some(
|
14 | (flag) => {
|
15 | const f = flag.split('-').join('') // remove '-' or '--'
|
16 | return f in argv && argv[f] // flag is defined in argv object (argv name is misleading) and has a truthy value
|
17 | } // eslint-disable-line comma-dangle
|
18 | )
|
19 |
|
20 | const yargs = require('yargs')
|
21 | .help()
|
22 | .version(packageJson.version)
|
23 | .options({
|
24 | D: {
|
25 | alias: 'debug',
|
26 | description: 'Execute the command in debug mode. It will be chatty about what it is happening in the code.',
|
27 | requiresArg: false,
|
28 | },
|
29 | V: {
|
30 | alias: 'verbose',
|
31 | description: 'Execute the command in verbose mode. It will be chatty about what it is attempting to accomplish.',
|
32 | requiresArg: false,
|
33 | },
|
34 | })
|
35 | .global(['D', 'V'])
|
36 | .command('deploy', 'Deploy a default version of the function that will execute your Artillery scripts. See ' +
|
37 | 'https://serverless.com/framework/docs/providers/aws/cli-reference/deploy/ for reference.', {})
|
38 | .command(
|
39 | 'invoke',
|
40 | 'Invoke your function with your Artillery script. Will prefer a script given by `-d`, `--data`, `-p`, or ' +
|
41 | '`--path` over a `script.[yml|json]` file in the current directory over the default script. Invocation mode ' +
|
42 | 'will default to "performance" but adding the `-a` flag will run the script in "acceptance" mode. ' +
|
43 | 'See https://serverless.com/framework/docs/providers/aws/cli-reference/invoke/ for reference.',
|
44 | {
|
45 | a: {
|
46 | alias: 'acceptance',
|
47 | description: 'Execute the script in acceptance mode. It will execute each flow once, reporting failures.',
|
48 | requiresArg: false,
|
49 | },
|
50 | m: {
|
51 | alias: 'monitoring',
|
52 | description: 'Execute the script in monitoring mode. It will execute each flow a multiple of times, alerting ' +
|
53 | 'if the number of errors exceeds the configured threshold.',
|
54 | requiresArg: false,
|
55 | },
|
56 | d: {
|
57 | alias: 'data',
|
58 | description: 'A stringified script to execute',
|
59 | requiresArg: true,
|
60 | },
|
61 | p: {
|
62 | alias: 'path',
|
63 | description: 'A path to the file containing the script to execute',
|
64 | requiresArg: true,
|
65 | },
|
66 | si: {
|
67 | alias: 'stdIn',
|
68 | description: 'Have serverless read the event to invoke the remote function with from the "standard in" stream',
|
69 | requiresArg: false,
|
70 | },
|
71 | jo: {
|
72 | alias: 'jsonOnly',
|
73 | description: 'Only write JSON to console.log to facilitate piping the invocation result into a tool such as jq',
|
74 | requiresArg: false,
|
75 | },
|
76 | },
|
77 | /**
|
78 | * Custom argument rejection logic, to deal with legacy, argument conflicts (between slsart and sls), and
|
79 | * unsupported flags.
|
80 | * @param argv The array of arguments that were provided to the CLI
|
81 | */
|
82 | (argv) => {
|
83 | const breakingFlags = [
|
84 | '-s', '--script',
|
85 | ]
|
86 | const reservedFlags = [
|
87 | '-t', '--type',
|
88 | '-f', '--function',
|
89 | ]
|
90 | const unsupportedFlags = [
|
91 | '--raw',
|
92 | ]
|
93 | // ##!! LEGACY MANAGEMENT BEGIN !!##
|
94 | // TODO Delete this block once transition is satisfactory (previously we accepted -s and --script to provide the
|
95 | // TODO artillery script. This conflicts with the -s and --stage flags of the serverless framework. As such,
|
96 | // TODO while we transition our users away from providing the script via -s/--script, we reject those flags and
|
97 | // TODO redirect them.
|
98 | // TODO Transition start date:
|
99 | if (hasFlag(breakingFlags, argv)) {
|
100 | console.error([
|
101 | os.EOL,
|
102 | `DDOS Off!${os.EOL}${os.EOL}`,
|
103 | `TL;DR: use -p/--path to supply a script, use --stage to supply a stage.${os.EOL}${os.EOL}`,
|
104 | `**BREAKING CHANGE**${os.EOL}`,
|
105 | 'In order to facilitate the richer and more sustainable use of `serverless-artillery`, we have made a ',
|
106 | `'breaking change during this 0.x.x formation period.${os.EOL}`,
|
107 | 'The `-s` flag you provided is no longer supported. Instead, use the `-p` flag (still supplying the path ',
|
108 | 'to your script. The `-p` flag is the standard Serverless Framework flag for supplying a "path" to the ',
|
109 | `"event" file that is to be used to invoke the function.${os.EOL}`,
|
110 | 'We apologize for not forseeing the consequence of our previous choice. On the positive side, the feature ',
|
111 | 'enabled by this change allows you to use any valid command line flag of the serverless framework with ',
|
112 | `\`slsart\` in order to facilitate your workflow, whatever is involved.${os.EOL}`,
|
113 | `Notable exceptions include: "${reservedFlags.join('", "')}" `,
|
114 | `flags which are reserved due to assumptions of \`serverless-artillery\`${os.EOL}`,
|
115 | `**BREAKING CHANGE**${os.EOL}${os.EOL}`,
|
116 | 'NOTE: if you have intended to supply the "STAGE" of the service, please use the long form of that flag ' +
|
117 | `(\`--stage\`) to specify that setting. This constraint will eventually be removed.${os.EOL}${os.EOL}`,
|
118 | `DDOS On!${os.EOL}${os.EOL}`,
|
119 | ].join(''))
|
120 | process.exit(1)
|
121 | }
|
122 | // ##!! LEGACY MANAGEMENT END !!##
|
123 | if (hasFlag(reservedFlags, argv)) {
|
124 | console.error([
|
125 | os.EOL,
|
126 | `!ERROR!${os.EOL}`,
|
127 | 'One of the `serverless` flags you provided is reserved for exclusive `serverless-artillery` use, ',
|
128 | `reserved flags include: "${
|
129 | Object.keys(reservedFlags).map(key => reservedFlags[key]).join('", "')
|
130 | }".${os.EOL}`,
|
131 | 'Please see the "reserved flags" documentation in the README ',
|
132 | `(https://www.npmjs.com/package/serverless-artillery#reserved-flags).${os.EOL}`,
|
133 | ].join(''))
|
134 | process.exit(1)
|
135 | } else if (hasFlag(unsupportedFlags, argv)) {
|
136 | console.error([
|
137 | os.EOL,
|
138 | `!ERROR!${os.EOL}`,
|
139 | `One of the flags you provided is unsupported by \`serverless-artillery\`. Unsupported flags include: "${
|
140 | Object.keys(unsupportedFlags).map(key => unsupportedFlags[key]).join('", "')}"${os.EOL}`,
|
141 | 'Please see the "unsupported flags" documentation in the README ',
|
142 | `(https://www.npmjs.com/package/serverless-artillery#unsupported-flags).${os.EOL}`,
|
143 | ].join(''))
|
144 | process.exit(1)
|
145 | }
|
146 | } // eslint-disable-line comma-dangle
|
147 | )
|
148 | .command('kill', 'Stop a currently running load test and remove the function.', {})
|
149 | .command('remove', 'Remove the function and the associated resources created for or by it. See ' +
|
150 | 'https://serverless.com/framework/docs/providers/aws/cli-reference/remove/ for reference.', {})
|
151 | .command(
|
152 | 'script',
|
153 | 'Create a local Artillery script so that you can customize it for your specific load requirements. ' +
|
154 | 'See https://artillery.io for documentation.',
|
155 | {
|
156 | e: {
|
157 | alias: 'endpoint',
|
158 | description: 'The endpoint to load with traffic.',
|
159 | requiresArg: true,
|
160 | type: 'string',
|
161 | },
|
162 | d: {
|
163 | alias: 'duration',
|
164 | description: 'The duration, in seconds, to load the given endpoint.',
|
165 | requiresArg: true,
|
166 | type: 'number',
|
167 | },
|
168 | r: {
|
169 | alias: 'rate',
|
170 | description: 'The rate, in requests per second, at which to load the given endpoint.',
|
171 | requiresArg: true,
|
172 | type: 'number',
|
173 | },
|
174 | t: {
|
175 | alias: 'rampTo',
|
176 | description: 'The rate to ramp up to from the given (starting) rate, in requests per second at which to load ' +
|
177 | 'the given endpoint.',
|
178 | requiresArg: true,
|
179 | type: 'number',
|
180 | },
|
181 | o: {
|
182 | alias: 'out',
|
183 | description: 'The file to output the generated script in to.',
|
184 | requiresArg: true,
|
185 | type: 'string',
|
186 | },
|
187 | } // eslint-disable-line comma-dangle
|
188 | )
|
189 | .command('configure', 'Create a local copy of the deployment assets for modification and deployment. See ' +
|
190 | 'https://serverless.com/framework/docs/ for documentation.', {})
|
191 | .command('upgrade', 'Upgrade local assets to latest version.', {})
|
192 | .demand(1)
|
193 |
|
194 | const command = yargs.argv._[0]
|
195 |
|
196 | if (yargs.argv.debug) {
|
197 | console.log(`options were:${os.EOL}${JSON.stringify(yargs.argv, null, 2)}`)
|
198 | console.log(`command that will be executed: slsArt[${command}](${yargs.argv})`)
|
199 | }
|
200 |
|
201 | if (!(command in slsArt)) {
|
202 | yargs.showHelp()
|
203 | process.exit(1)
|
204 | }
|
205 |
|
206 | slsArt[command](yargs.argv)
|
207 | .catch((ex) => {
|
208 | console.error(ex.message)
|
209 | if (yargs.argv.verbose) {
|
210 | console.error(ex.stack)
|
211 | }
|
212 | process.exit(1)
|
213 | })
|