@sls-next/aws-lambda
Version:
Deploy Lambda functions to AWS in seconds with [Serverless Components](https://github.com/serverless/components). Utilizes layers for dependency management and S3 accelerated uploads for maximum upload speeds.
199 lines (157 loc) • 5.21 kB
text/typescript
import AWS from "aws-sdk";
const AwsSdkLambda = AWS.Lambda;
import { mergeDeepRight, pick } from "ramda";
import { Component, utils } from "@serverless/core";
import {
createLambda,
updateLambdaCode,
updateLambdaConfig,
getLambda,
deleteLambda,
configChanged,
pack
} from "./utils";
import { waitUntilReady } from "./waitUntilReady";
const outputsList = [
"name",
"hash",
"description",
"memory",
"timeout",
"code",
"bucket",
"shims",
"handler",
"runtime",
"env",
"role",
"arn",
"region",
"tags"
];
const defaults = {
description: "AWS Lambda Component",
memory: 512,
timeout: 10,
code: process.cwd(),
bucket: undefined,
shims: [],
handler: "handler.hello",
runtime: "nodejs10.x",
env: {},
region: "us-east-1",
tags: undefined
};
class AwsLambda extends Component {
context: any;
state: any;
save: () => void;
load: (name: string) => any;
async default(inputs: Record<string, unknown> = {}) {
this.context.status(`Deploying`);
const config: any = mergeDeepRight(defaults, inputs);
config.name = inputs.name || this.state.name || this.context.resourceId();
config.tags = inputs.tags || this.state.tags;
this.context.debug(
`Starting deployment of lambda ${config.name} to the ${config.region} region.`
);
if (AWS?.config) {
AWS.config.update({
maxRetries: parseInt(process.env.SLS_NEXT_MAX_RETRIES ?? "10"),
retryDelayOptions: { base: 200 }
});
}
const lambda = new AwsSdkLambda({
region: config.region,
credentials: this.context.credentials.aws
});
if (!config.role || !config.role.arn) {
const awsIamRole = await this.load("@serverless/aws-iam-role");
const outputsAwsIamRole = await awsIamRole(config.role);
config.role = { arn: outputsAwsIamRole.arn };
}
this.context.status("Packaging");
this.context.debug(`Packaging lambda code from ${config.code}.`);
config.zipPath = await pack(config.code, config.shims);
config.hash = await utils.hashFile(config.zipPath);
const prevLambda = await getLambda({ lambda, ...config });
if (!prevLambda) {
this.context.status(`Creating`);
this.context.debug(
`Creating lambda ${config.name} in the ${config.region} region.`
);
const createResult = await createLambda({ lambda, ...config });
config.arn = createResult.arn;
config.hash = createResult.hash;
// Wait for Lambda to be in a ready state after creation and before doing anything else
await waitUntilReady(this.context, config.name, config.region);
} else {
config.arn = prevLambda.arn;
if (configChanged(prevLambda, config)) {
if (prevLambda.hash !== config.hash) {
this.context.status(`Uploading code`);
this.context.debug(`Uploading ${config.name} lambda code.`);
await updateLambdaCode({ lambda, ...config });
}
// Wait for Lambda to be in a ready state after code updated and before doing anything else
await waitUntilReady(this.context, config.name, config.region);
this.context.status(`Updating`);
this.context.debug(`Updating ${config.name} lambda config.`);
const updateResult = await updateLambdaConfig({ lambda, ...config });
config.hash = updateResult.hash;
// Wait for Lambda to be ready after updating config and before doing anything else
await waitUntilReady(this.context, config.name, config.region);
}
}
// todo we probably don't need this logic now that we auto generate names
if (this.state.name && this.state.name !== config.name) {
this.context.status(`Replacing`);
await deleteLambda({ lambda, name: this.state.name });
}
this.context.debug(
`Successfully deployed lambda ${config.name} in the ${config.region} region.`
);
const outputs = pick(outputsList, config);
this.state = outputs;
await this.save();
return outputs;
}
async publishVersion() {
const { name, region, hash } = this.state;
const lambda = new AwsSdkLambda({
region,
credentials: this.context.credentials.aws
});
const { Version } = await lambda
.publishVersion({
FunctionName: name,
CodeSha256: hash
})
.promise();
return { version: Version };
}
async remove() {
this.context.status(`Removing`);
if (!this.state.name) {
this.context.debug(`Aborting removal. Function name not found in state.`);
return;
}
const { name, region } = this.state;
const lambda = new AwsSdkLambda({
region,
credentials: this.context.credentials.aws
});
const awsIamRole = await this.load("@serverless/aws-iam-role");
await awsIamRole.remove();
this.context.debug(`Removing lambda ${name} from the ${region} region.`);
await deleteLambda({ lambda, name });
this.context.debug(
`Successfully removed lambda ${name} from the ${region} region.`
);
const outputs = pick(outputsList, this.state);
this.state = {};
await this.save();
return outputs;
}
}
export default AwsLambda;