artillery
Version:
Cloud-scale load testing. https://www.artillery.io
166 lines (162 loc) • 7.43 kB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import fs from 'node:fs';
import path from 'node:path';
import { Args, Command, Flags } from '@oclif/core';
import dotenv from 'dotenv';
import { CommonRunFlags } from "../cli/common-flags.js";
import { ECS_WORKER_ROLE_NAME } from "../platform/aws/constants.js";
import { parseResourceTags } from "../platform/aws/resource-tags.js";
import PlatformECS from "../platform/aws-ecs/ecs.js";
import runCluster from "../platform/aws-ecs/legacy/run-cluster.js";
import { supportedRegions } from "../platform/aws-ecs/legacy/util.js";
import { Plugin as CloudPlugin } from "../platform/cloud/cloud.js";
import * as telemetry from "../telemetry.js";
import generateId from "../util/generate-id.js";
class RunCommand extends Command {
static aliases = ['run:fargate', 'run:ecs', 'run-ecs'];
// Enable multiple args:
static strict = false;
async run() {
const { flags, _argv, args } = await this.parse(RunCommand);
flags['platform-opt'] = [`region=${flags.region}`];
flags.platform = 'aws:ecs';
// Validate early, before any AWS resources are touched:
try {
parseResourceTags(flags['aws-tags']);
}
catch (err) {
console.error(err.message);
process.exit(1);
}
if (flags.dotenv) {
const dotEnvPath = path.resolve(process.cwd(), flags.dotenv);
try {
fs.statSync(dotEnvPath);
}
catch (_err) {
console.log(`WARNING: could not read dotenv file: ${flags.dotenv}`);
}
dotenv.config({ path: dotEnvPath });
}
const testRunId = process.env.ARTILLERY_TEST_RUN_ID || generateId('t');
global.artillery.testRunId = testRunId;
const cloud = new CloudPlugin(null, null, { flags });
global.artillery.cloudEnabled = cloud.enabled;
if (cloud.enabled) {
try {
await cloud.init();
}
catch (caughtErr) {
const err = caughtErr;
if (err.name === 'CloudAPIKeyMissing') {
console.error('Error: API key is required to record test results to Artillery Cloud');
console.error('See https://docs.art/get-started-cloud for more information');
process.exit(7);
}
else if (err.name === 'APIKeyUnauthorized') {
console.error('Error: API key is not recognized or is not authorized to record tests');
process.exit(7);
}
else if (err.name === 'PingFailed') {
console.error('Error: unable to reach Artillery Cloud API. This could be due to firewall restrictions on your network');
console.log('Please see https://docs.art/cloud/err-ping');
process.exit(7);
}
else {
console.error('Error: something went wrong connecting to Artillery Cloud');
console.error('Check https://status.artillery.io for status updates');
console.error(err);
}
}
}
flags.taskRoleName = flags['task-role-name'] || ECS_WORKER_ROLE_NAME;
const ECS = new PlatformECS(null, null, {}, {
testRunId: 'foo',
region: flags.region,
taskRoleName: flags.taskRoleName
});
await ECS.init();
process.env.USE_NOOP_BACKEND_STORE = 'true';
telemetry.capture('run:fargate', {
region: flags.region,
count: flags.count
});
// Delegate the rest to existing implementation:
runCluster(args.script, flags);
}
}
RunCommand.description = `launch a test using AWS ECS/Fargate
Examples:
To launch a test with 10 load generating workers using AWS Fargate in us-east-1:
$ artillery run:fargate --count 10 --region us-east-1 my-test.yml
`;
RunCommand.flags = {
...CommonRunFlags,
count: Flags.integer({
description: 'Number of load generator workers to launch'
}),
cluster: Flags.string({
description: 'Name of the Fargate/ECS cluster to run the test on'
}),
region: Flags.string({
char: 'r',
description: 'The AWS region to run in',
options: supportedRegions,
default: 'us-east-1'
}),
secret: Flags.string({
multiple: true,
description: 'Make secrets available to workers. The secret must exist in SSM parameter store for the given region, under /artilleryio/<SECRET_NAME>'
}),
'launch-type': Flags.string({
description: 'The launch type to use for the test. Defaults to Fargate.',
options: ['ecs:fargate', 'ecs:ec2']
}),
spot: Flags.boolean({
description: 'Use Fargate Spot (https://docs.art/fargate-spot) Ignored when --launch-type is set to ecs:ec2'
}),
'launch-config': Flags.string({
description: 'JSON to customize launch configuration of ECS/Fargate tasks (see https://www.artillery.io/docs/reference/cli/run-fargate#using---launch-config)'
}),
'container-dns-servers': Flags.string({
description: 'Comma-separated list of DNS servers for Artillery container. Maps to dnsServers parameter in ECS container definition'
}),
'task-ephemeral-storage': Flags.string({
description: 'Ephemeral storage in GiB for the worker task. Maps to ephemeralStorage parameter in ECS container definition. Fargate-only.',
type: 'integer'
}),
'subnet-ids': Flags.string({
description: 'Comma-separated list of AWS VPC subnet IDs to launch Fargate tasks in'
}),
'security-group-ids': Flags.string({
description: 'Comma-separated list of AWS VPC security group IDs to launch Fargate tasks in'
}),
'task-role-name': Flags.string({
description: 'Custom IAM role name for Fargate containers to assume'
}),
cpu: Flags.string({
description: 'Set task vCPU on Fargate (defaults to 4 vCPU). Value may be set as a number of vCPUs between 1-16 (e.g. 4), or as number of vCPU units (e.g. 4096).'
}),
memory: Flags.string({
description: 'Set task memory on Fargate (defaults to 8 GB). Value may be set as number of GB between 1-120 (e.g. 8), or as MiB (e.g. 8192)'
}),
packages: Flags.string({
description: 'Path to package.json file which lists dependencies for the test script'
}),
'max-duration': Flags.string({
description: 'Maximum duration of the test run'
}),
'aws-tags': Flags.string({
description: 'Comma-separated list of tags in key:value format to apply to AWS resources created for the test run (Fargate tasks and SQS queue), for example: --aws-tags "team:perf,cost-center:1234"'
}),
'no-assign-public-ip': Flags.boolean({
description: 'Turn off the default behavior of assigning public IPs to Fargate worker tasks. When this option is used you must make sure tasks have a route to the internet, i.e. via a NAT gateway attached to a private subnet',
default: false
})
};
RunCommand.args = {
script: Args.string()
};
export default RunCommand;