@artilleryio/platform-fargate
Version:
Fargate support for Artillery
225 lines (186 loc) • 6.3 kB
JavaScript
;
const A = require('async');
const debug = require('debug')('commands:create-test');
const uuid = require('uuid');
const { S3_BUCKET_NAME_PREFIX } = require('../constants');
const util = require('../util');
const { getBucketName } = require('../util');
const createS3Client = require('../utils/create-s3-client');
const setDefaultAWSCredentials = require('../utils/aws-set-default-credentials');
const path = require('path');
const fs = require('fs');
const _ = require('lodash');
const YAML = require('yaml-js');
const chalk = require('chalk');
const { createBOM, prettyPrint } = require('../bom');
function tryCreateTest(scriptPath, options) {
createTest(scriptPath, options);
}
async function createTest(scriptPath, options, callback) {
const absoluteScriptPath = path.resolve(process.cwd(), scriptPath);
const contextPath = options.context ?
path.resolve(options.context) :
path.dirname(absoluteScriptPath);
debug('script:', absoluteScriptPath);
debug('root:', contextPath);
let context = {
contextDir: contextPath,
scriptPath: absoluteScriptPath,
originalScriptPath: scriptPath,
name: options.name, // test name, eg simple-bom or aht_$UUID
manifestPath: options.manifestPath,
packageJsonPath: options.packageJsonPath,
};
if (typeof options.config === 'string') {
const absoluteConfigPath = path.resolve(process.cwd(), options.config);
context.configPath = absoluteConfigPath;
}
// If we have a callback we were called from another command:
if (!callback) {
if (typeof context.name !== 'string') {
// FIXME: not sure why it happens, but if --name not given interactively, its value is set to a Function
console.log(util.formatError(new Error('A test name must be provided with the --name option')));
process.exit(1);
}
console.log(`Creating named test ${chalk.blue(context.name)}\n`);
}
await setDefaultAWSCredentials();
A.waterfall(
[
A.constant(context),
async function(context) {
context.s3Bucket = await getBucketName();
return context;
},
prepareManifest,
printManifest,
syncS3,
writeTestMetadata
],
function(err, context) {
if (err) {
console.log(err);
return;
}
// If we have a callback we were called from another command:
if (callback) {
callback(err, context);
} else {
// TODO: How does one wrap the text automatically depending on terminal width?
console.log(`\nTest ${chalk.blue(context.name)} has been created. You can now use the name of the test as an argument for artillery run-test`);
}
});
}
function prepareManifest(context, callback) {
let includeFiles;
let fileToAnalyse = context.scriptPath;
let extraFiles = [];
if (context.configPath) {
debug('context has been provided; extraFiles =', extraFiles);
fileToAnalyse = context.configPath;
extraFiles.push(context.scriptPath);
}
createBOM(fileToAnalyse, extraFiles, {packageJsonPath: context.packageJsonPath}, (err, bom) => {
debug(err);
debug(bom);
context.manifest = bom;
return callback(err, context);
});
}
function printManifest(context, callback) {
prettyPrint(context.manifest);
return callback(null, context);
}
function syncS3(context, callback) {
const plainS3 = createS3Client();
const prefix = `tests/${context.name}`;
context.s3Prefix = prefix;
debug('Will try syncing to:', context.s3Bucket);
debug('Manifest: ', context.manifest);
// Iterate through manifest, for each element: has orig (local source) and noPrefix (S3
// destination) properties
A.eachLimit(
context.manifest.files,
3,
(item, eachDone) => {
// If we can't read the file, it may have been specified with a
// template in its name, e.g. a payload file like:
// {{ $environment }}-users.csv
// If so, ignore it, hope config.includeFiles was used, and let
// "artillery run" in the worker deal with it.
let body;
try {
body = fs.readFileSync(item.orig);
} catch (fsErr) {
debug(fsErr);
}
if (!body) {
return eachDone(null, context);
}
const key = context.s3Prefix + '/' + item.noPrefix; // root for a9 command is s3Prefix? yes.
plainS3.putObject(
{
Bucket: context.s3Bucket,
Key: key,
// TODO: stream, not readFileSync
Body: body
},
(s3Err, s3Resp) => {
if (s3Err) {
// TODO: retry - see async#retry
return eachDone(s3Err, context);
}
debug(`Uploaded ${key}`);
return eachDone(null, context);
}
);
},
(bomUploadErr) => {
return callback(bomUploadErr, context);
});
}
// create just overwrites an existing test for now
function writeTestMetadata(context, callback) {
const metadata = {
createdOn: Date.now(),
// createdBy: 'username',
name: context.name,
// scriptPath: context.newScriptPath,
modules: context.manifest.modules
};
// Here we need to provide config information (if given) -- so that the worker knows how to load it.
if (context.configPath) {
const res = context.manifest.files.filter((o) => {
return o.orig === context.configPath;
});
const newConfigPath = res[0].noPrefix; // if we have been given a config, we must have an entry
metadata.configPath = newConfigPath;
}
const newScriptPath = (context.manifest.files.filter((o) => {
return o.orig === context.scriptPath;
}))[0].noPrefix;
metadata.scriptPath = newScriptPath;
debug('metadata', metadata);
const s3 = createS3Client();
const key = context.s3Prefix + '/metadata.json'; // TODO: Rename to something less likely to clash
debug('metadata location:', `${context.s3Bucket}/${key}`);
s3.putObject(
{
Body: JSON.stringify(metadata),
Bucket: context.s3Bucket,
Key: key
},
function(s3Err, s3Resp) {
if (s3Err) {
// TODO: probably delete the S3 dir
return callback(s3Err, context);
}
return callback(null, context);
});
}
module.exports = {
tryCreateTest,
createTest,
syncS3,
prepareManifest
};