Skip to content

Commit

Permalink
feat(lambda): make container lambdas the default and deprecate zipfile
Browse files Browse the repository at this point in the history
  • Loading branch information
bernardobridge committed May 22, 2024
1 parent 9c43ff4 commit ed262b6
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 411 deletions.
11 changes: 5 additions & 6 deletions packages/artillery/lib/cmds/run-lambda.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ class RunLambdaCommand extends Command {
flags['platform-opt'].push(`subnet-ids=${flags['subnet-ids']}`);
}

if (flags.container) {
flags['platform-opt'].push('container=true');
}

flags.platform = 'aws:lambda';

RunCommand.runCommandImplementation(flags, argv, args);
Expand Down Expand Up @@ -65,8 +61,11 @@ RunLambdaCommand.flags = {
default: '1'
}),
container: Flags.boolean({
description: 'Use a container image for Lambda (experimental)',
default: false
description: '[DEPRECATED] Use a container image for Lambda (experimental)',
deprecated: {
message:
'The "container" flag has been deprecated. Container is now the default mode for Lambda functions.'
}
}),
architecture: Flags.string({
description: 'Architecture of the Lambda function',
Expand Down
22 changes: 2 additions & 20 deletions packages/artillery/lib/cmds/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -429,12 +429,7 @@ RunCommand.runCommandImplementation = async function (flags, argv, args) {
}
};

function replaceProcessorIfTypescript(
script,
scriptPath,
platform,
isContainerLambda
) {
function replaceProcessorIfTypescript(script, scriptPath) {
const relativeProcessorPath = script.config.processor;
const userExternalPackages = script.config.bundling?.external || [];

Expand All @@ -447,10 +442,6 @@ function replaceProcessorIfTypescript(
return script;
}

if (platform == 'aws:lambda' && !isContainerLambda) {
throw new Error('Typescript processor is not supported on AWS Lambda');
}

const actualProcessorPath = path.resolve(
path.dirname(scriptPath),
relativeProcessorPath
Expand Down Expand Up @@ -538,12 +529,7 @@ async function prepareTestExecutionPlan(inputFiles, flags, args) {
script6.config.statsInterval = script6.config.statsInterval || 30;

const script7 = addDefaultPlugins(script5);
const script8 = replaceProcessorIfTypescript(
script7,
scriptPath,
flags.platform,
flags.container
);
const script8 = replaceProcessorIfTypescript(script7, scriptPath);

return script8;
}
Expand Down Expand Up @@ -614,10 +600,6 @@ async function sendTelemetry(script, flags, extraProps) {

properties.platform = flags.platform;

if (flags.container) {
properties.isContainerLambda = true;
}

properties.count = flags.count;

if (properties.targetHash) {
Expand Down
201 changes: 1 addition & 200 deletions packages/artillery/lib/platform/aws-lambda/dependencies.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
const fs = require('fs-extra');
const path = require('path');
const temp = require('temp');
const spawn = require('cross-spawn');
const archiver = require('archiver');
const AWS = require('aws-sdk');
const debug = require('debug')('platform:aws-lambda');
const Table = require('cli-table3');
const { randomUUID } = require('crypto');
const { promisify } = require('node:util');
const { createBOM: createBOMForZip } = require('../../create-bom/create-bom');
const { createBOM: createBOMForContainer } = require('../aws-ecs/legacy/bom');
const { createBOM } = require('../aws-ecs/legacy/bom');

const _createLambdaBom = async (
absoluteScriptPath,
Expand All @@ -29,203 +23,11 @@ const _createLambdaBom = async (
createBomOpts.flags = flags;
}

const createBOM = flags.container ? createBOMForContainer : createBOMForZip;

const bom = await promisify(createBOM)(entryPoint, extraFiles, createBomOpts);

return bom;
};

async function createZip(src, out) {
const archive = archiver('zip', { zlib: { level: 9 } });
const stream = fs.createWriteStream(out);

return new Promise((resolve, reject) => {
archive
.directory(src, false)
.on('error', (err) => reject(err))
.pipe(stream);

stream.on('close', () => resolve());
archive.finalize();
});
}

async function _uploadLambdaZip(bucketName, zipfile) {
const key = `lambda/${randomUUID()}.zip`;

// TODO: Set lifecycle policy on the bucket/key prefix to delete after 24 hours

const s3 = new AWS.S3();
const s3res = await s3
.putObject({
Body: fs.createReadStream(zipfile),
Bucket: bucketName,
Key: key
})
.promise();

return key;
}

const createAndUploadLambdaZip = async (
bucketName,
absoluteScriptPath,
absoluteConfigPath,
flags
) => {
const dirname = temp.mkdirSync(); // TODO: May want a way to override this by the user
const zipfile = temp.path({ suffix: '.zip' });
debug({ dirname, zipfile });

artillery.log('- Bundling test data');
const bom = await _createLambdaBom(
absoluteScriptPath,
absoluteConfigPath,
flags
);

for (const f of bom.files) {
artillery.log(' -', f.noPrefix);
}

if (flags.dotenv) {
fs.copyFileSync(
path.resolve(process.cwd(), flags.dotenv),
path.join(dirname, path.basename(flags.dotenv))
);
}

// Copy handler:
fs.copyFileSync(
path.resolve(__dirname, 'lambda-handler', 'a9-handler-index.js'),
path.join(dirname, 'a9-handler-index.js')
);
fs.copyFileSync(
path.resolve(__dirname, 'lambda-handler', 'a9-handler-helpers.js'),
path.join(dirname, 'a9-handler-helpers.js')
);
fs.copyFileSync(
path.resolve(__dirname, 'lambda-handler', 'a9-handler-dependencies.js'),
path.join(dirname, 'a9-handler-dependencies.js')
);
fs.copyFileSync(
path.resolve(__dirname, 'lambda-handler', 'package.json'),
path.join(dirname, 'package.json')
);

// FIXME: This may overwrite lambda-handler's index.js or package.json
// Copy files that make up the test:
for (const o of bom.files) {
fs.ensureFileSync(path.join(dirname, o.noPrefix));
fs.copyFileSync(o.orig, path.join(dirname, o.noPrefix));
}

artillery.log('- Installing dependencies');
const { stdout, stderr, status, error } = spawn.sync(
'npm',
['install', '--omit', 'dev'],
{
cwd: dirname
}
);

if (error) {
artillery.log(stdout?.toString(), stderr?.toString(), status, error);
} else {
// artillery.log(' npm log is in:', temp.path({suffix: '.log'}));
}

// Install extra plugins & engines
if (bom.modules.length > 0) {
artillery.log(
`- Installing extra engines & plugins: ${bom.modules.join(', ')}`
);
const { stdout, stderr, status, error } = spawn.sync(
'npm',
['install'].concat(bom.modules),
{ cwd: dirname }
);
if (error) {
artillery.log(stdout?.toString(), stderr?.toString(), status, error);
}
}

// Copy this version of Artillery into the Lambda package
const a9basepath = path.resolve(__dirname, '..', '..', '..');
// TODO: read this from .files in package.json instead:
for (const dir of ['bin', 'lib']) {
const destdir = path.join(dirname, 'node_modules', 'artillery', dir);
const srcdir = path.join(a9basepath, dir);
fs.ensureDirSync(destdir);
fs.copySync(srcdir, destdir);
}
for (const fn of ['console-reporter.js', 'util.js']) {
const destfn = path.join(dirname, 'node_modules', 'artillery', fn);
const srcfn = path.join(a9basepath, fn);
fs.copyFileSync(srcfn, destfn);
}

fs.copyFileSync(
path.resolve(a9basepath, 'package.json'),
path.join(dirname, 'node_modules', 'artillery', 'package.json')
);

const a9cwd = path.join(dirname, 'node_modules', 'artillery');
debug({ a9basepath, a9cwd });

const {
stdout: stdout2,
stderr: stderr2,
status: status2,
error: error2
} = spawn.sync('npm', ['install', '--omit', 'dev'], { cwd: a9cwd });
if (error2) {
artillery.log(stdout2?.toString(), stderr2?.toString(), status2, error2);
} else {
// artillery.log(' npm log is in:', temp.path({suffix: '.log'}));
}

const {
stdout: stdout3,
stderr: stderr3,
status: status3,
error: error3
} = spawn.sync(
'npm',
[
'uninstall',
'try-require',
'esbuild-wasm',
'artillery-plugin-publish-metrics'
],
{
cwd: a9cwd
}
);
if (error3) {
artillery.log(stdout3?.toString(), stderr3?.toString(), status3, error3);
} else {
// artillery.log(' npm log is in:', temp.path({suffix: '.log'}));
}

fs.removeSync(path.join(dirname, 'node_modules', 'aws-sdk'));
fs.removeSync(path.join(a9cwd, 'node_modules', 'tap'));
fs.removeSync(path.join(a9cwd, 'node_modules', 'prettier'));

artillery.log('- Creating zip package');
await createZip(dirname, zipfile);

artillery.log('Preparing AWS environment...');
const s3Path = await _uploadLambdaZip(bucketName, zipfile);
debug({ s3Path });

return {
s3Path,
bom
};
};

async function _uploadFileToS3(item, testRunId, bucketName) {
const s3 = new AWS.S3();
const prefix = `tests/${testRunId}`;
Expand Down Expand Up @@ -333,6 +135,5 @@ const createAndUploadTestDependencies = async (
};

module.exports = {
createAndUploadLambdaZip,
createAndUploadTestDependencies
};
Loading

0 comments on commit ed262b6

Please sign in to comment.