Skip to content

Commit

Permalink
feat: re-structure the CodePipeline Construct library API.
Browse files Browse the repository at this point in the history
  • Loading branch information
skinny85 committed Jan 22, 2019
1 parent 2b491d4 commit 12f1576
Show file tree
Hide file tree
Showing 65 changed files with 1,208 additions and 1,077 deletions.
28 changes: 17 additions & 11 deletions packages/@aws-cdk/app-delivery/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
## Continuous Integration / Continuous Delivery for CDK Applications
This library includes a *CodePipeline* action for deploying AWS CDK Applications.
This library includes a *CodePipeline* composite Action for deploying AWS CDK Applications.

This module is part of the [AWS Cloud Development Kit](https://github.com/awslabs/aws-cdk) project.

Expand Down Expand Up @@ -29,7 +29,7 @@ The example below defines a *CDK App* that contains 3 stacks:
```

#### `index.ts`
```ts
```typescript
import codebuild = require('@aws-cdk/aws-codebuild');
import codepipeline = require('@aws-cdk/aws-codepipeline');
import cdk = require('@aws-cdk/cdk');
Expand All @@ -46,11 +46,13 @@ const pipeline = new codepipeline.Pipeline(pipelineStack, 'CodePipeline', {
restartExecutionOnUpdate: true,
/* ... */
});

// Configure the CodePipeline source - where your CDK App's source code is hosted
const source = new codepipeline.GitHubSourceAction(pipelineStack, 'GitHub', {
stage: pipeline.addStage('source'),
const source = new codepipeline.GitHubSourceAction('GitHub', {
/* ... */
});
pipeline.addStage(new codepipeline.Stage('source').addAction(source));

const project = new codebuild.PipelineProject(pipelineStack, 'CodeBuild', {
/**
* Choose an environment configuration that meets your use case. For NodeJS
Expand All @@ -60,22 +62,25 @@ const project = new codebuild.PipelineProject(pipelineStack, 'CodeBuild', {
* },
*/
});
const buildStage = pipeline.addStage('build');
const buildAction = project.addToPipeline(buildStage, 'CodeBuild');
const buildAction = project.asCodePipelineAction('CodeBuild', {
inputArtifact: source.outputArtifact,
});
pipeline.addStage(new codepipeline.Stage('build').addAction(buildAction));
const synthesizedApp = buildAction.outputArtifact;

// Optionally, self-update the pipeline stack
const selfUpdateStage = pipeline.addStage('SelfUpdate');
const selfUpdateStage = new codepipeline.Stage('SelfUpdate');
pipeline.addStage(selfUpdateStage);
new cicd.PipelineDeployStackAction(pipelineStack, 'SelfUpdatePipeline', {
stage: selfUpdateStage,
stack: pipelineStack,
inputArtifact: synthesizedApp,
});

// Now add our service stacks
const deployStage = pipeline.addStage('Deploy');
const deployStage = new codepipeline.Stage('Deploy');
pipeline.addStage(deployStage);
const serviceStackA = new MyServiceStackA(app, 'ServiceStackA', { /* ... */ });
const serviceStackB = new MyServiceStackB(app, 'ServiceStackB', { /* ... */ });
// Add actions to deploy the stacks in the deploy stage:
const deployServiceAAction = new cicd.PipelineDeployStackAction(pipelineStack, 'DeployServiceStackA', {
stage: deployStage,
Expand All @@ -84,7 +89,6 @@ const deployServiceAAction = new cicd.PipelineDeployStackAction(pipelineStack, '
// See the note below for details about this option.
adminPermissions: false,
});

// Add the necessary permissions for you service deploy action. This role is
// is passed to CloudFormation and needs the permissions necessary to deploy
// stack. Alternatively you can enable [Administrator](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_administrator) permissions above,
Expand All @@ -95,7 +99,9 @@ deployServiceAAction.addToRolePolicy(
.addResource(myResource.myResourceArn)
// add more Action(s) and/or Resource(s) here, as needed
);
const deployServiceBAction = new cicd.PipelineDeployStackAction(pipelineStack, 'DeployServiceStackB', {

const serviceStackB = new MyServiceStackB(app, 'ServiceStackB', { /* ... */ });
new cicd.PipelineDeployStackAction(pipelineStack, 'DeployServiceStackB', {
stage: deployStage,
stack: serviceStackB,
inputArtifact: synthesizedApp,
Expand Down
22 changes: 11 additions & 11 deletions packages/@aws-cdk/app-delivery/lib/pipeline-deploy-stack-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ export interface PipelineDeployStackActionProps {
}

/**
* A CodePipeline action to deploy a stack that is part of a CDK App. This
* action takes care of preparing and executing a CloudFormation ChangeSet.
* A Construct to deploy a stack that is part of a CDK App, using CodePipeline.
* This composite Action takes care of preparing and executing a CloudFormation ChangeSet.
*
* It currently does *not* support stacks that make use of ``Asset``s, and
* requires the deployed stack is in the same account and region where the
Expand Down Expand Up @@ -120,24 +120,24 @@ export class PipelineDeployStackAction extends cdk.Construct {
const changeSetName = props.changeSetName || 'CDK-CodePipeline-ChangeSet';

const capabilities = cfnCapabilities(props.adminPermissions, props.capabilities);
const changeSetAction = new cfn.PipelineCreateReplaceChangeSetAction(this, 'ChangeSet', {
const changeSetAction = new cfn.PipelineCreateReplaceChangeSetAction('ChangeSet', {
changeSetName,
runOrder: createChangeSetRunOrder,
stackName: props.stack.name,
stage: props.stage,
templatePath: props.inputArtifact.atPath(`${props.stack.name}.template.yaml`),
adminPermissions: props.adminPermissions,
role: props.role,
capabilities,
});
this.role = changeSetAction.role;
props.stage
.addAction(changeSetAction)
.addAction(new cfn.PipelineExecuteChangeSetAction('Execute', {
changeSetName,
runOrder: executeChangeSetRunOrder,
stackName: props.stack.name,
}));

new cfn.PipelineExecuteChangeSetAction(this, 'Execute', {
changeSetName,
runOrder: executeChangeSetRunOrder,
stackName: props.stack.name,
stage: props.stage,
});
this.role = changeSetAction.role;
}

/**
Expand Down
8 changes: 4 additions & 4 deletions packages/@aws-cdk/app-delivery/test/integ.cicd.expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"Effect": "Allow",
"Resource": {
"Fn::GetAtt": [
"DeployStackChangeSetRole4923A126",
"CodePipelineDeployChangeSetRoleF9F2B343",
"Arn"
]
}
Expand Down Expand Up @@ -202,7 +202,7 @@
"TemplatePath": "Artifact_CICDGitHubF8BA7ADD::CICD.template.yaml",
"RoleArn": {
"Fn::GetAtt": [
"DeployStackChangeSetRole4923A126",
"CodePipelineDeployChangeSetRoleF9F2B343",
"Arn"
]
}
Expand Down Expand Up @@ -243,7 +243,7 @@
"CodePipelineRoleDefaultPolicy8D520A8D"
]
},
"DeployStackChangeSetRole4923A126": {
"CodePipelineDeployChangeSetRoleF9F2B343": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
Expand All @@ -261,4 +261,4 @@
}
}
}
}
}
10 changes: 7 additions & 3 deletions packages/@aws-cdk/app-delivery/test/integ.cicd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@ const pipeline = new code.Pipeline(stack, 'CodePipeline', {
removalPolicy: cdk.RemovalPolicy.Destroy
})
});
const source = new code.GitHubSourceAction(stack, 'GitHub', {
stage: pipeline.addStage('Source'),
const source = new code.GitHubSourceAction('GitHub', {
owner: 'awslabs',
repo: 'aws-cdk',
oauthToken: new cdk.Secret('DummyToken'),
pollForSourceChanges: true,
outputArtifactName: 'Artifact_CICDGitHubF8BA7ADD',
});
const stage = pipeline.addStage('Deploy');
pipeline.addStage(new code.Stage('Source', {
actions: [source],
}));
const stage = new code.Stage('Deploy');
pipeline.addStage(stage);
new cicd.PipelineDeployStackAction(stack, 'DeployStack', {
stage,
stack,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@ export = nodeunit.testCase({
const app = new cdk.App();
const stack = new cdk.Stack(app, 'Test', { env: { account: pipelineAccount } });
const pipeline = new code.Pipeline(stack, 'Pipeline');
const fakeAction = new FakeAction(stack, 'Fake', pipeline);
const fakeAction = new FakeAction('Fake');
pipeline.addStage(new code.Stage('FakeStage', {
actions: [fakeAction],
}));
new PipelineDeployStackAction(stack, 'Action', {
changeSetName: 'ChangeSet',
inputArtifact: fakeAction.outputArtifact,
stack: new cdk.Stack(app, 'DeployedStack', { env: { account: stackAccount } }),
stage: pipeline.addStage('DeployStage'),
stage: new code.Stage('DeployStage'),
adminPermissions: false,
});
}, 'Cross-environment deployment is not supported');
Expand All @@ -54,14 +57,17 @@ export = nodeunit.testCase({
const app = new cdk.App();
const stack = new cdk.Stack(app, 'Test');
const pipeline = new code.Pipeline(stack, 'Pipeline');
const fakeAction = new FakeAction(stack, 'Fake', pipeline);
const fakeAction = new FakeAction('Fake');
pipeline.addStage(new code.Stage('FakeStage', {
actions: [fakeAction],
}));
new PipelineDeployStackAction(stack, 'Action', {
changeSetName: 'ChangeSet',
createChangeSetRunOrder: createRunOrder,
executeChangeSetRunOrder: executeRunOrder,
inputArtifact: fakeAction.outputArtifact,
stack: new cdk.Stack(app, 'DeployedStack'),
stage: pipeline.addStage('DeployStage'),
stage: new code.Stage('DeployStage'),
adminPermissions: false,
});
}, 'createChangeSetRunOrder must be < executeChangeSetRunOrder');
Expand All @@ -80,24 +86,30 @@ export = nodeunit.testCase({

const selfUpdatingStack = createSelfUpdatingStack(pipelineStack);

const selfUpdateStage1 = new code.Stage('SelfUpdate1');
const selfUpdateStage2 = new code.Stage('SelfUpdate2');
const selfUpdateStage3 = new code.Stage('SelfUpdate3');
const pipeline = selfUpdatingStack.pipeline;
const selfUpdateStage = pipeline.addStage('SelfUpdate');
pipeline
.addStage(selfUpdateStage1)
.addStage(selfUpdateStage2)
.addStage(selfUpdateStage3);
new PipelineDeployStackAction(pipelineStack, 'SelfUpdatePipeline', {
stage: selfUpdateStage,
stage: selfUpdateStage1,
stack: pipelineStack,
inputArtifact: selfUpdatingStack.synthesizedApp,
capabilities: cfn.CloudFormationCapabilities.NamedIAM,
adminPermissions: false,
});
new PipelineDeployStackAction(pipelineStack, 'DeployStack', {
stage: selfUpdateStage,
stage: selfUpdateStage2,
stack: stackWithNoCapability,
inputArtifact: selfUpdatingStack.synthesizedApp,
capabilities: cfn.CloudFormationCapabilities.None,
adminPermissions: false,
});
new PipelineDeployStackAction(pipelineStack, 'DeployStack2', {
stage: selfUpdateStage,
stage: selfUpdateStage3,
stack: stackWithAnonymousCapability,
inputArtifact: selfUpdatingStack.synthesizedApp,
capabilities: cfn.CloudFormationCapabilities.AnonymousIAM,
Expand Down Expand Up @@ -143,8 +155,9 @@ export = nodeunit.testCase({
const pipelineStack = getTestStack();
const selfUpdatingStack = createSelfUpdatingStack(pipelineStack);

const selfUpdateStage = new code.Stage('SelfUpdate');
const pipeline = selfUpdatingStack.pipeline;
const selfUpdateStage = pipeline.addStage('SelfUpdate');
pipeline.addStage(selfUpdateStage);
new PipelineDeployStackAction(pipelineStack, 'SelfUpdatePipeline', {
stage: selfUpdateStage,
stack: pipelineStack,
Expand Down Expand Up @@ -176,11 +189,12 @@ export = nodeunit.testCase({
const pipelineStack = getTestStack();
const selfUpdatingStack = createSelfUpdatingStack(pipelineStack);

const pipeline = selfUpdatingStack.pipeline;
const role = new iam.Role(pipelineStack, 'MyRole', {
assumedBy: new iam.ServicePrincipal('cloudformation.amazonaws.com'),
});
const selfUpdateStage = pipeline.addStage('SelfUpdate');
const pipeline = selfUpdatingStack.pipeline;
const selfUpdateStage = new code.Stage('SelfUpdate');
pipeline.addStage(selfUpdateStage);
const deployAction = new PipelineDeployStackAction(pipelineStack, 'SelfUpdatePipeline', {
stage: selfUpdateStage,
stack: pipelineStack,
Expand All @@ -203,7 +217,8 @@ export = nodeunit.testCase({

// WHEN //
// this our app/service/infra to deploy
const deployStage = pipeline.addStage('Deploy');
const deployStage = new code.Stage('Deploy');
pipeline.addStage(deployStage);
const deployAction = new PipelineDeployStackAction(pipelineStack, 'DeployServiceStackA', {
stage: deployStage,
stack: emptyStack,
Expand Down Expand Up @@ -248,7 +263,7 @@ export = nodeunit.testCase({
},
Roles: [
{
Ref: 'DeployServiceStackAChangeSetRoleA1245536',
Ref: 'CodePipelineDeployChangeSetRoleF9F2B343',
},
],
}));
Expand All @@ -262,13 +277,18 @@ export = nodeunit.testCase({
const app = new cdk.App();
const stack = new cdk.Stack(app, 'Test');
const pipeline = new code.Pipeline(stack, 'Pipeline');
const fakeAction = new FakeAction(stack, 'Fake', pipeline);
const fakeAction = new FakeAction('Fake');
pipeline.addStage(new code.Stage('FakeStage', {
actions: [fakeAction],
}));
const deployedStack = new cdk.Stack(app, 'DeployedStack');
const deployStage = new code.Stage('DeployStage');
pipeline.addStage(deployStage);
const action = new PipelineDeployStackAction(stack, 'Action', {
changeSetName: 'ChangeSet',
inputArtifact: fakeAction.outputArtifact,
stack: deployedStack,
stage: pipeline.addStage('DeployStage'),
stage: deployStage,
adminPermissions: false,
});
for (let i = 0 ; i < assetCount ; i++) {
Expand All @@ -286,15 +306,18 @@ export = nodeunit.testCase({
class FakeAction extends api.Action {
public readonly outputArtifact: api.Artifact;

constructor(scope: cdk.Construct, id: string, pipeline: code.Pipeline) {
super(scope, id, {
constructor(actionName: string) {
super(actionName, {
artifactBounds: api.defaultBounds(),
category: api.ActionCategory.Test,
provider: 'Test',
stage: pipeline.addStage('FakeStage'),
});

this.outputArtifact = new api.Artifact(this, 'OutputArtifact');
this.outputArtifact = new api.Artifact('OutputArtifact');
}

protected bind(_pipeline: api.IPipeline, _parent: cdk.Construct): void {
// do nothing
}
}

Expand All @@ -309,15 +332,21 @@ function createSelfUpdatingStack(pipelineStack: cdk.Stack): SelfUpdatingPipeline

// simple source
const bucket = s3.Bucket.import( pipeline, 'PatternBucket', { bucketArn: 'arn:aws:s3:::totally-fake-bucket' });
new s3.PipelineSourceAction(pipeline, 'S3Source', {
const sourceAction = new s3.PipelineSourceAction('S3Source', {
bucket,
bucketKey: 'the-great-key',
stage: pipeline.addStage('source'),
});
pipeline.addStage(new code.Stage('source', {
actions: [sourceAction],
}));

const project = new codebuild.PipelineProject(pipelineStack, 'CodeBuild');
const buildStage = pipeline.addStage('build');
const buildAction = project.addToPipeline(buildStage, 'CodeBuild');
const buildAction = project.asCodePipelineAction('CodeBuild', {
inputArtifact: sourceAction.outputArtifact,
});
pipeline.addStage(new code.Stage('build', {
actions: [buildAction],
}));
const synthesizedApp = buildAction.outputArtifact;
return {synthesizedApp, pipeline};
}
Expand Down
Loading

0 comments on commit 12f1576

Please sign in to comment.