Skip to content

Commit

Permalink
feat(lambda): introduce a new kind of Code, CfnParametersCode.
Browse files Browse the repository at this point in the history
This Code type is helpful when there is no local file/directory to use Assets with,
and the Lambda is supposed to be deployed in a CodePipeline.
  • Loading branch information
skinny85 committed Apr 2, 2019
1 parent 997dbcc commit 5ab21c3
Show file tree
Hide file tree
Showing 6 changed files with 380 additions and 13 deletions.
157 changes: 156 additions & 1 deletion packages/@aws-cdk/aws-codepipeline-actions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,9 +324,135 @@ This package defines the following actions:
changes from the people (or system) applying the changes.
* **CloudFormationExecuteChangeSetAction** - Execute a change set prepared previously.
##### Lambda deployed through CodePipeline
If you want to deploy your Lambda through CodePipeline,
and you don't use assets (for example, because your CDK code and Lambda code are separate),
you can use a special Lambda `Code` class, `CfnParametersCode`.
Note that your Lambda must be in a different Stack than your Pipeline.
The Lambda itself will be deployed, alongside the entire Stack it belongs to,
using a CloudFormation CodePipeline Action. Example:
```typescript
const lambdaStack = new cdk.Stack(app, 'LambdaStack', {
// remove the Stack from `cdk synth` and `cdk deploy`
// unless you explicitly filter for it
autoDeploy: false,
});
const lambdaCode = lambda.Code.cfnParameters();
const func = new lambda.Function(lambdaStack, 'Lambda', {
code: lambdaCode,
handler: 'index.handler',
runtime: lambda.Runtime.NodeJS810,
});
// other resources that your Lambda needs, added to the lambdaStack...

const pipeline = new codepipeline.Pipeline(pipelineStack, 'Pipeline');

// add the source code repository containing this code to your Pipeline,
// and the source code of the Lambda Function, if they're separate
pipeline.addStage({
name: 'Source',
actions: [
// ...
],
});

// synthesize the Lambda CDK template, using CodeBuild
// the below values are just examples, assuming your CDK code is in TypeScript/JavaScript -
// adjust the build environment and/or commands accordingly
const cdkBuildProject = new codebuild.Project(pipelineStack, 'CdkBuildProject', {
environment: {
buildImage: codebuild.LinuxBuildImage.UBUNTU_14_04_NODEJS_10_1_0,
},
buildSpec: {
version: '0.2',
phases: {
install: {
commands: 'npm install',
},
build: {
commands: [
'npm run build',
'npm run cdk synth LambdaStack -- -o .'
],
},
},
artifacts: {
files: 'LambdaStack.template.yaml',
},
},
});
const cdkBuildAction = codepipeline_actions.CodeBuildBuildAction({
actionName: 'CDK_Build',
project: cdkBuildProject,
inputArtifact: cdkSourceAction.outputArtifact,
});

// build your Lambda code, using CodeBuild
// again, this example assumes your Lambda is written in TypeScript/JavaScript -
// make sure to adjust the build environment and/or commands if they don't match your specific situation
const lambdaBuildProject = new codebuild.Project(pipelineStack, 'LambdaBuildProject', {
environment: {
buildImage: codebuild.LinuxBuildImage.UBUNTU_14_04_NODEJS_10_1_0,
},
buildSpec: {
version: '0.2',
phases: {
install: {
commands: 'npm install',
},
build: {
commands: 'npm run build',
},
},
artifacts: {
files: [
'index.js',
'node_modules/**/*',
],
},
},
});
const lambdaBuildAction = codepipeline_actions.CodeBuildBuildAction({
actionName: 'Lambda_Build',
project: lambdaBuildProject,
inputArtifact: lambdaSourceAction.outputArtifact,
});

pipeline.addStage({
name: 'Build',
actions: [
cdkBuildAction,
lambdaBuildAction,
],
});

// finally, deploy your Lambda Stack
pipeline.addStage({
name: 'Deploy',
actions: [
new codepipeline_actions.CloudFormationCreateUpdateStackAction({
actionName: 'Lambda_CFN_Deploy',
templatePath: cdkBuildAction.outputArtifact.atPath('LambdaStack.template.yaml'),
stackName: 'YourDeployStackHere',
adminPermissions: true,
parameterOverrides: {
...lambdaBuildAction.outputArtifact.fillParameters(lambdaCode),
},
additionalInputArtifacts: [
lambdaBuildAction.outputArtifact,
],
}),
],
});
```
#### AWS CodeDeploy
To use CodeDeploy in a Pipeline:
##### Server deployments
To use CodeDeploy for EC2/on-premise deployments in a Pipeline:
```ts
import codedeploy = require('@aws-cdk/aws-codedeploy');
Expand All @@ -348,6 +474,35 @@ pipeline.addStage({
});
```
##### Lambda deployments
To use CodeDeploy for blue-green Lambda deployments in a Pipeline:
```typescript
const lambdaCode = lambda.Code.cfnParameters();
const func = new lambda.Function(lambdaStack, 'Lambda', {
code: lambdaCode,
handler: 'index.handler',
runtime: lambda.Runtime.NodeJS810,
});
// used to make sure each CDK synthesis produces a different Version
const version = func.newVersion();
const alias = new lambda.Alias(lambdaStack, 'LambdaAlias', {
aliasName: 'Prod',
version,
});

new codedeploy.LambdaDeploymentGroup(lambdaStack, 'DeploymentGroup', {
alias,
deploymentConfig: codedeploy.LambdaDeploymentConfig.Linear10PercentEvery1Minute,
});
```
Then, you need to create your Pipeline Stack,
where you will define your Pipeline,
and deploy the `lambdaStack` using a CloudFormation CodePipeline Action
(see above for a complete example).
#### AWS S3
To use an S3 Bucket as a deployment target in CodePipeline:
Expand Down
19 changes: 19 additions & 0 deletions packages/@aws-cdk/aws-codepipeline/lib/artifact.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import lambda = require("@aws-cdk/aws-lambda");
import { Token } from "@aws-cdk/cdk";

/**
Expand Down Expand Up @@ -48,6 +49,24 @@ export class Artifact {
return artifactGetParam(this, jsonFile, keyName);
}

/**
* Create parameters for Lambda `Code` pointing to this Artifact.
*
* It returns a map with 2 keys that correspond to the parameters defining the Lambda Code,
* and as values it contains the appropriate expressions pointing inside this Artifact.
* The map should be provided to the CloudFormation Action
* that is deploying the Stack that the Lambda is part of,
* in the `parameterOverrides` property.
*
* @param code the Lambda code defined using CloudFormation parameters
*/
public fillParameters(code: lambda.CfnParametersCode): { [name: string]: any } {
const ret: { [name: string]: any } = {};
ret[code.bucketNameParam] = this.bucketName;
ret[code.objectKeyParam] = this.objectKey;
return ret;
}

public toString() {
return this.artifactName;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-codepipeline/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,16 @@
"dependencies": {
"@aws-cdk/aws-events": "^0.27.0",
"@aws-cdk/aws-iam": "^0.27.0",
"@aws-cdk/aws-lambda": "^0.27.0",
"@aws-cdk/aws-s3": "^0.27.0",
"@aws-cdk/aws-sns": "^0.27.0",
"@aws-cdk/cdk": "^0.27.0"
},
"homepage": "https://github.com/awslabs/aws-cdk",
"peerDependencies": {
"@aws-cdk/aws-events": "^0.27.0",
"@aws-cdk/aws-iam": "^0.27.0",
"@aws-cdk/aws-lambda": "^0.27.0",
"@aws-cdk/aws-s3": "^0.27.0",
"@aws-cdk/aws-sns": "^0.27.0",
"@aws-cdk/cdk": "^0.27.0"
},
"engines": {
Expand Down
39 changes: 39 additions & 0 deletions packages/@aws-cdk/aws-codepipeline/test/test.artifact.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import lambda = require('@aws-cdk/aws-lambda');
import cdk = require('@aws-cdk/cdk');
import { Test } from 'nodeunit';
import codepipeline = require('../lib');

// tslint:disable:no-string-literal

export = {
'CodePipeline Artifacts': {
'can override Lambda Code'(test: Test) {
// given
const stack = new cdk.Stack();
const code = new lambda.CfnParametersCode({
bucketNameParam: new cdk.CfnParameter(stack, 'BucketNameParam', {
type: 'String',
}),
objectKeyParam: new cdk.CfnParameter(stack, 'ObjectKeyParam', {
type: 'String',
}),
});

const artifact = new codepipeline.Artifact('MyArtifact');

// when
const overrides = stack.node.resolve(artifact.fillParameters(code));

// then
test.deepEqual(overrides['BucketNameParam'], {
'Fn::GetArtifactAtt': ['MyArtifact', 'BucketName'],
});

test.deepEqual(overrides['ObjectKeyParam'], {
'Fn::GetArtifactAtt': ['MyArtifact', 'ObjectKey'],
});

test.done();
},
},
};
Loading

0 comments on commit 5ab21c3

Please sign in to comment.