Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(aws-codecommit): use CloudWatch Events instead of polling by default in the CodePipeline Action #1026

Merged
merged 1 commit into from
Nov 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export class PipelineExecuteChangeSetAction extends PipelineCloudFormationAction
ChangeSetName: props.changeSetName,
});

SingletonPolicy.forRole(props.stage.pipelineRole)
SingletonPolicy.forRole(props.stage.pipeline.role)
.grantExecuteChangeSet(props);
}
}
Expand Down Expand Up @@ -210,7 +210,7 @@ export abstract class PipelineCloudFormationDeployAction extends PipelineCloudFo
}
}

SingletonPolicy.forRole(props.stage.pipelineRole).grantPassRole(this.role);
SingletonPolicy.forRole(props.stage.pipeline.role).grantPassRole(this.role);
}

/**
Expand Down Expand Up @@ -255,7 +255,7 @@ export class PipelineCreateReplaceChangeSetAction extends PipelineCloudFormation
this.addInputArtifact(props.templateConfiguration.artifact);
}

SingletonPolicy.forRole(props.stage.pipelineRole).grantCreateReplaceChangeSet(props);
SingletonPolicy.forRole(props.stage.pipeline.role).grantCreateReplaceChangeSet(props);
}
}

Expand Down Expand Up @@ -310,7 +310,7 @@ export class PipelineCreateUpdateStackAction extends PipelineCloudFormationDeplo
this.addInputArtifact(props.templateConfiguration.artifact);
}

SingletonPolicy.forRole(props.stage.pipelineRole).grantCreateUpdateStack(props);
SingletonPolicy.forRole(props.stage.pipeline.role).grantCreateUpdateStack(props);
}
}

Expand All @@ -332,7 +332,7 @@ export class PipelineDeleteStackAction extends PipelineCloudFormationDeployActio
super(parent, id, props, {
ActionMode: 'DELETE_ONLY',
});
SingletonPolicy.forRole(props.stage.pipelineRole).grantDeleteStack(props);
SingletonPolicy.forRole(props.stage.pipeline.role).grantDeleteStack(props);
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-cloudformation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"license": "Apache-2.0",
"devDependencies": {
"@aws-cdk/assert": "^0.14.1",
"@aws-cdk/aws-events": "^0.14.1",
"@types/lodash": "^4.14.116",
"cdk-build-tools": "^0.14.1",
"cdk-integ-tools": "^0.14.1",
Expand Down
55 changes: 36 additions & 19 deletions packages/@aws-cdk/aws-cloudformation/test/test.pipeline-actions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import cpapi = require('@aws-cdk/aws-codepipeline-api');
import events = require('@aws-cdk/aws-events');
import iam = require('@aws-cdk/aws-iam');
import cdk = require('@aws-cdk/cdk');
import _ = require('lodash');
Expand All @@ -10,7 +11,7 @@ export = nodeunit.testCase({
'works'(test: nodeunit.Test) {
const stack = new cdk.Stack();
const pipelineRole = new RoleDouble(stack, 'PipelineRole');
const stage = new StageDouble({ pipelineRole });
const stage = new StageDouble({ pipeline: new PipelineDouble({ role: pipelineRole }) });
const artifact = new cpapi.Artifact(stack as any, 'TestArtifact');
const action = new cloudformation.PipelineCreateReplaceChangeSetAction(stack, 'Action', {
stage,
Expand Down Expand Up @@ -43,7 +44,7 @@ export = nodeunit.testCase({
'uses a single permission statement if the same ChangeSet name is used'(test: nodeunit.Test) {
const stack = new cdk.Stack();
const pipelineRole = new RoleDouble(stack, 'PipelineRole');
const stage = new StageDouble({ pipelineRole });
const stage = new StageDouble({ pipeline: new PipelineDouble({ role: pipelineRole }) });
const artifact = new cpapi.Artifact(stack as any, 'TestArtifact');
new cloudformation.PipelineCreateReplaceChangeSetAction(stack, 'ActionA', {
stage,
Expand Down Expand Up @@ -97,7 +98,7 @@ export = nodeunit.testCase({
'works'(test: nodeunit.Test) {
const stack = new cdk.Stack();
const pipelineRole = new RoleDouble(stack, 'PipelineRole');
const stage = new StageDouble({ pipelineRole });
const stage = new StageDouble({ pipeline: new PipelineDouble({ role: pipelineRole }) });
new cloudformation.PipelineExecuteChangeSetAction(stack, 'Action', {
stage,
changeSetName: 'MyChangeSet',
Expand All @@ -120,7 +121,7 @@ export = nodeunit.testCase({
'uses a single permission statement if the same ChangeSet name is used'(test: nodeunit.Test) {
const stack = new cdk.Stack();
const pipelineRole = new RoleDouble(stack, 'PipelineRole');
const stage = new StageDouble({ pipelineRole });
const stage = new StageDouble({ pipeline: new PipelineDouble({ role: pipelineRole }) });
new cloudformation.PipelineExecuteChangeSetAction(stack, 'ActionA', {
stage,
changeSetName: 'MyChangeSet',
Expand Down Expand Up @@ -158,7 +159,7 @@ export = nodeunit.testCase({
const stack = new cdk.Stack();
const pipelineRole = new RoleDouble(stack, 'PipelineRole');
const action = new cloudformation.PipelineCreateUpdateStackAction(stack, 'Action', {
stage: new StageDouble({ pipelineRole }),
stage: new StageDouble({ pipeline: new PipelineDouble({ role: pipelineRole }) }),
templatePath: new cpapi.Artifact(stack as any, 'TestArtifact').atPath('some/file'),
stackName: 'MyStack',
replaceOnFailure: true,
Expand All @@ -179,7 +180,7 @@ export = nodeunit.testCase({
const stack = new cdk.Stack();
const pipelineRole = new RoleDouble(stack, 'PipelineRole');
const action = new cloudformation.PipelineDeleteStackAction(stack, 'Action', {
stage: new StageDouble({ pipelineRole }),
stage: new StageDouble({ pipeline: new PipelineDouble({ role: pipelineRole }) }),
stackName: 'MyStack',
});
const stackArn = _stackArn('MyStack');
Expand Down Expand Up @@ -273,26 +274,42 @@ function _stackArn(stackName: string): string {
});
}

class StageDouble implements cpapi.IStage, cpapi.IInternalStage {
public readonly name: string;
class PipelineDouble implements cpapi.IPipeline {
public readonly pipelineArn: string;
public readonly pipelineRole: iam.Role;
public readonly _internal = this;
public readonly role: iam.Role;

public readonly actions = new Array<cpapi.Action>();

constructor({ name, pipelineName, pipelineRole }: { name?: string, pipelineName?: string, pipelineRole: iam.Role }) {
this.name = name || 'TestStage';
constructor({ pipelineName, role }: { pipelineName?: string, role: iam.Role }) {
this.pipelineArn = cdk.ArnUtils.fromComponents({ service: 'codepipeline', resource: 'pipeline', resourceName: pipelineName || 'TestPipeline' });
this.pipelineRole = pipelineRole;
this.role = role;
}

public grantPipelineBucketRead() {
throw new Error('Unsupported');
public get uniqueId(): string {
throw new Error("Unsupported");
}

public grantPipelineBucketReadWrite() {
throw new Error('Unsupported');
public grantBucketRead(): void {
throw new Error("Unsupported");
}

public grantBucketReadWrite(): void {
throw new Error("Unsupported");
}

public asEventRuleTarget(): events.EventRuleTargetProps {
throw new Error("Unsupported");
}
}

class StageDouble implements cpapi.IStage, cpapi.IInternalStage {
public readonly name: string;
public readonly pipeline: cpapi.IPipeline;
public readonly _internal = this;

public readonly actions = new Array<cpapi.Action>();

constructor({ name, pipeline }: { name?: string, pipeline: cpapi.IPipeline }) {
this.name = name || 'TestStage';
this.pipeline = pipeline;
}

public _attachAction(action: cpapi.Action) {
Expand Down
12 changes: 5 additions & 7 deletions packages/@aws-cdk/aws-codebuild/lib/pipeline-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export class PipelineTestAction extends codepipeline.TestAction {
function setCodeBuildNeededPermissions(stage: codepipeline.IStage, project: ProjectRef,
needsPipelineBucketWrite: boolean) {
// grant the Pipeline role the required permissions to this Project
stage.pipelineRole.addToPolicy(new iam.PolicyStatement()
stage.pipeline.role.addToPolicy(new iam.PolicyStatement()
.addResource(project.projectArn)
.addActions(
'codebuild:BatchGetBuilds',
Expand All @@ -117,11 +117,9 @@ function setCodeBuildNeededPermissions(stage: codepipeline.IStage, project: Proj
));

// allow the Project access to the Pipline's artifact Bucket
if (project.role) {
if (needsPipelineBucketWrite) {
stage.grantPipelineBucketReadWrite(project.role);
} else {
stage.grantPipelineBucketRead(project.role);
}
if (needsPipelineBucketWrite) {
stage.pipeline.grantBucketReadWrite(project.role);
} else {
stage.pipeline.grantBucketRead(project.role);
}
}
14 changes: 9 additions & 5 deletions packages/@aws-cdk/aws-codecommit/lib/pipeline-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ export interface CommonPipelineSourceActionProps extends codepipeline.CommonActi
*/
branch?: string;

// TODO: use CloudWatch events instead
/**
* Whether or not AWS CodePipeline should poll for source changes.
* Whether AWS CodePipeline should poll for source changes.
* If this is `false`, the Pipeline will use CloudWatch Events to detect source changes instead.
*
* @default true
* @default false
*/
pollForSourceChanges?: boolean;
}
Expand Down Expand Up @@ -54,11 +54,15 @@ export class PipelineSourceAction extends codepipeline.SourceAction {
configuration: {
RepositoryName: props.repository.repositoryName,
BranchName: props.branch || 'master',
PollForSourceChanges: props.pollForSourceChanges !== undefined ? props.pollForSourceChanges : true
PollForSourceChanges: props.pollForSourceChanges || false,
},
outputArtifactName: props.outputArtifactName
});

if (!props.pollForSourceChanges) {
props.repository.onCommit(props.stage.pipeline.uniqueId + 'EventRule', props.stage.pipeline, props.branch || 'master');
}

// https://docs.aws.amazon.com/codecommit/latest/userguide/auth-and-access-control-permissions-reference.html#aa-acp
const actions = [
'codecommit:GetBranch',
Expand All @@ -68,7 +72,7 @@ export class PipelineSourceAction extends codepipeline.SourceAction {
'codecommit:CancelUploadArchive',
];

props.stage.pipelineRole.addToPolicy(new iam.PolicyStatement()
props.stage.pipeline.role.addToPolicy(new iam.PolicyStatement()
.addResource(props.repository.repositoryArn)
.addActions(...actions));
}
Expand Down
6 changes: 3 additions & 3 deletions packages/@aws-cdk/aws-codedeploy/lib/pipeline-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class PipelineDeployAction extends codepipeline.DeployAction {
resourceName: props.applicationName,
sep: ':',
});
props.stage.pipelineRole.addToPolicy(new iam.PolicyStatement()
props.stage.pipeline.role.addToPolicy(new iam.PolicyStatement()
.addResource(applicationArn)
.addActions(
'codedeploy:GetApplicationRevision',
Expand All @@ -67,7 +67,7 @@ export class PipelineDeployAction extends codepipeline.DeployAction {
resourceName: `${props.applicationName}/${props.deploymentGroupName}`,
sep: ':',
});
props.stage.pipelineRole.addToPolicy(new iam.PolicyStatement()
props.stage.pipeline.role.addToPolicy(new iam.PolicyStatement()
.addResource(deploymentGroupArn)
.addActions(
'codedeploy:CreateDeployment',
Expand All @@ -80,7 +80,7 @@ export class PipelineDeployAction extends codepipeline.DeployAction {
resourceName: '*',
sep: ':',
});
props.stage.pipelineRole.addToPolicy(new iam.PolicyStatement()
props.stage.pipeline.role.addToPolicy(new iam.PolicyStatement()
.addResource(deployConfigArn)
.addActions(
'codedeploy:GetDeploymentConfig',
Expand Down
47 changes: 32 additions & 15 deletions packages/@aws-cdk/aws-codepipeline-api/lib/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,42 +68,59 @@ export interface IInternalStage {
}

/**
* The abstract interface of a Pipeline Stage that is used by Actions.
* The abstract view of an AWS CodePipeline as required and used by Actions.
* It extends {@link events.IEventRuleTarget},
* so this interface can be used as a Target for CloudWatch Events.
*/
export interface IStage {
/**
* The physical, human-readable name of this Pipeline Stage.
*/
readonly name: string;

export interface IPipeline extends events.IEventRuleTarget {
/**
* The ARN of the Pipeline.
*/
readonly pipelineArn: string;

/**
* The service Role of the Pipeline.
* The unique ID of the Pipeline Construct.
*/
readonly pipelineRole: iam.Role;
readonly uniqueId: string;

/**
* The API of Stage used internally by the CodePipeline Construct.
* You should never need to call any of the methods inside of it yourself.
* The service Role of the Pipeline.
*/
readonly _internal: IInternalStage;
readonly role: iam.Role;

/* Grants read permissions to the Pipeline's S3 Bucket to the given Identity.
*
* @param identity the IAM Identity to grant the permissions to
*/
grantPipelineBucketRead(identity: iam.IPrincipal): void;
grantBucketRead(identity?: iam.IPrincipal): void;

/**
* Grants read & write permissions to the Pipeline's S3 Bucket to the given Identity.
*
* @param identity the IAM Identity to grant the permissions to
*/
grantPipelineBucketReadWrite(identity: iam.IPrincipal): void;
grantBucketReadWrite(identity?: iam.IPrincipal): void;
}

/**
* The abstract interface of a Pipeline Stage that is used by Actions.
*/
export interface IStage {
/**
* The physical, human-readable name of this Pipeline Stage.
*/
readonly name: string;

/**
* The Pipeline this Stage belongs to.
*/
readonly pipeline: IPipeline;

/**
* The API of Stage used internally by the CodePipeline Construct.
* You should never need to call any of the methods inside of it yourself.
*/
readonly _internal: IInternalStage;
}

/**
Expand Down Expand Up @@ -215,7 +232,7 @@ export abstract class Action extends cdk.Construct {
rule.addEventPattern({
detailType: [ 'CodePipeline Stage Execution State Change' ],
source: [ 'aws.codepipeline' ],
resources: [ this.stage.pipelineArn ],
resources: [ this.stage.pipeline.pipelineArn ],
detail: {
stage: [ this.stage.name ],
action: [ this.id ],
Expand Down
Loading