Skip to content
This repository has been archived by the owner on Aug 2, 2021. It is now read-only.

Commit

Permalink
feat(aws): deploy to lambda and create version tags
Browse files Browse the repository at this point in the history
  • Loading branch information
trieloff committed Dec 7, 2020
1 parent b845f90 commit 2686c41
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 4 deletions.
6 changes: 6 additions & 0 deletions src/action_builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,12 @@ module.exports = class ActionBuilder {
return this;
}

withAWSRole(value) {
// propagate AWS region
this._deployers.aws.withAWSRole(value);
return this;
}

get testPath() {
return this._test;
}
Expand Down
6 changes: 6 additions & 0 deletions src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ class CLI {
type: 'string',
default: '',
})
.option('aws-role', {
description: 'the AWS role ARN to execute lambda functions with',
type: 'string',
default: '',
})
.group([
'name', 'node-version', 'params', 'params-file', 'web-secure',
'namespace', 'timeout', 'updated-by', 'updated-at'], 'OpenWhisk Action Options')
Expand Down Expand Up @@ -242,6 +247,7 @@ class CLI {
.withLinks(argv.versionLink)
.withLinksPackage(argv.linksPackage)
.withAWSRegion(argv.awsRegion)
.withAWSRole(argv.awsRole)
.withPackageShared(argv.package.shared);
}

Expand Down
121 changes: 118 additions & 3 deletions src/deploy/AWSDeployer.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,23 @@
* governing permissions and limitations under the License.
*/
const {
S3Client, CreateBucketCommand, PutObjectCommand, DeleteBucketCommand, DeleteObjectCommand,
S3Client,
CreateBucketCommand,
PutObjectCommand,
DeleteBucketCommand,
DeleteObjectCommand,
} = require('@aws-sdk/client-s3');
const {
LambdaClient,
CreateFunctionCommand,
GetFunctionCommand,
UpdateFunctionConfigurationCommand,
UpdateFunctionCodeCommand,
GetAliasCommand,
PublishVersionCommand,
CreateAliasCommand,
UpdateAliasCommand,
} = require('@aws-sdk/client-lambda');
const path = require('path');
const fse = require('fs-extra');
const crypto = require('crypto');
Expand All @@ -23,6 +38,7 @@ class AWSDeployer extends BaseDeployer {

Object.assign(this, {
_region: '',
_role: '',
});
}

Expand All @@ -31,15 +47,24 @@ class AWSDeployer extends BaseDeployer {
return this;
}

withAWSRole(value) {
this._role = value;
return this;
}

ready() {
return !!this._region && !!this._s3;
const res = !!this._region && !!this._s3 && !!this._role && !!this._lambda;
return res;
}

async init() {
this._bucket = `poly-func-maker-temp-${crypto.randomBytes(16).toString('hex')}`;
this._s3 = new S3Client({
region: this._region,
});
this._lambda = new LambdaClient({
region: this._region,
});
}

async createS3Bucket() {
Expand Down Expand Up @@ -76,11 +101,101 @@ class AWSDeployer extends BaseDeployer {
this.log.info(`Bucket ${this._bucket} emptied and deleted`);
}

async createLambda() {
const functionName = `${this._builder.packageName}--${this._builder.name.replace(/@.*/g, '')}`;
const functionVersion = this._builder.name.replace(/.*@/g, '').replace(/\./g, '_');

const functionConfig = {
Code: {
S3Bucket: this._bucket,
S3Key: this._key,
},
// todo: package name
FunctionName: functionName,
Role: this._role,
Runtime: `nodejs${this._builder.nodeVersion}.x`,
// todo: cram annotations into description?
Tags: {
pkgVersion: this._builder.version,
dependencies: this._builder.dependencies.main.map((dep) => `${dep.name}:${dep.version}`).join(','),
repository: encodeURIComponent(this._builder.gitUrl).replace(/%/g, '@'),
git: encodeURIComponent(`${this._builder.gitOrigin}#${this._builder.gitRef}`).replace(/%/g, '@'),
updated: `${this._builder.updatedAt}`,
},
Description: this._builder.pkgJson.description,
MemorySize: this._builder.memory,
Timeout: Math.floor(this._builder.timeout / 1000),
// todo: what about package params?
Environment: {
Variables: this._builder.params,
},
Handler: 'index.lambda',
};

try {
this.log.info(`Updating existing Lambda function ${functionName}`);
await this._lambda.send(new GetFunctionCommand({
FunctionName: functionName,
}));

const updatecode = this._lambda.send(new UpdateFunctionCodeCommand({
FunctionName: functionName,
...functionConfig.Code,
}));
const updateconfig = this._lambda.send(
new UpdateFunctionConfigurationCommand(functionConfig),
);

await updateconfig;
await updatecode;
} catch (e) {
if (e.name === 'ResourceNotFoundException') {
this.log.info(`Creating new Lambda function ${functionName}`);
await this._lambda.send(new CreateFunctionCommand(functionConfig));
} else {
this.log.error(`Unable to verify existence of Lambda function ${functionName}`);
throw e;
}
}

const versiondata = await this._lambda.send(new PublishVersionCommand({
FunctionName: functionName,
}));

const versionNum = versiondata.Version;
try {
await this._lambda.send(new GetAliasCommand({
FunctionName: functionName,
Name: functionVersion,
}));

this.log.info(`Updating existing alias ${functionName}:${functionVersion} to v${versionNum}`);
await this._lambda.send(new UpdateAliasCommand({
FunctionName: functionName,
Name: functionVersion,
FunctionVersion: versionNum,

}));
} catch (e) {
if (e.name === 'ResourceNotFoundException') {
this.log.info(`Creating new alias ${functionName}:${functionVersion} at v${versionNum}`);
await this._lambda.send(new CreateAliasCommand({
FunctionName: functionName,
Name: functionVersion,
FunctionVersion: versionNum,
}));
} else {
this.log.error(`Unable to verify existence of Lambda alias ${functionName}:${functionVersion}`);
throw e;
}
}
}

async deploy() {
try {
await this.createS3Bucket();
await this.uploadZIP();
// await this.createLambda();
await this.createLambda();
await this.deleteS3Bucket();
} catch (err) {
this.log.error(`Unable to deploy Lambda function: ${err.message}`, err);
Expand Down
4 changes: 3 additions & 1 deletion test/aws.integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ describe('AWS Integration Test', () => {
'--verbose',
'--deploy',
'--aws-region', 'us-east-1',
'--aws-role', 'arn:aws:iam::320028119408:role/lambda-role',
'-p', 'FOO=bar',
'--test', '/foo',
'--directory', testRoot,
'--entryFile', 'index.js',
Expand All @@ -53,5 +55,5 @@ describe('AWS Integration Test', () => {
const out = builder._logger.output;
assert.ok(out.indexOf(`ok: 200
Hello, world.`) > 0, out);
}).timeout(10000);
}).timeout(50000);
});

0 comments on commit 2686c41

Please sign in to comment.