Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,10 @@ class StageDouble implements cpapi.IStage, cpapi.IInternalStage {
this.pipelineRole = pipelineRole;
}

public grantPipelineBucketRead() {
throw new Error('Unsupported');
}

public grantPipelineBucketReadWrite() {
throw new Error('Unsupported');
}
Expand Down
25 changes: 24 additions & 1 deletion packages/@aws-cdk/aws-codebuild/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,30 @@ You can also add the Project to the Pipeline directly:

```ts
// equivalent to the code above:
project.addBuildToPipeline(buildStage, 'CodeBuild');
const buildAction = project.addBuildToPipeline(buildStage, 'CodeBuild');
```

In addition to the build Action,
there is also a test Action.
It works very similarly to the build Action,
the only difference is that the test Action does not always produce an output artifact.

Examples:

```ts
new codebuild.PipelineTestAction(this, 'IntegrationTest', {
stage: buildStage,
project,
// outputArtifactName is optional - if you don't specify it,
// the Action will have an undefined `outputArtifact` property
outputArtifactName: 'IntegrationTestOutput',
});

// equivalent to the code above:
project.addTestToPipeline(buildStage, 'IntegrationTest', {
// of course, this property is optional here as well
outputArtifactName: 'IntegrationTestOutput',
});
```

### Using Project as an event target
Expand Down
89 changes: 70 additions & 19 deletions packages/@aws-cdk/aws-codebuild/lib/pipeline-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,33 +44,84 @@ export class PipelineBuildAction extends codepipeline.BuildAction {
// https://qiita.com/ikeisuke/items/2fbc0b80b9bbd981b41f

super(parent, name, {
stage: props.stage,
runOrder: props.runOrder,
provider: 'CodeBuild',
inputArtifact: props.inputArtifact,
outputArtifactName: props.outputArtifactName,
configuration: {
ProjectName: props.project.projectName
}
ProjectName: props.project.projectName,
},
...props,
});

const actions = [
setCodeBuildNeededPermissions(props.stage, props.project, true);
}
}

/**
* Common properties for creating {@link PipelineTestAction} -
* either directly, through its constructor,
* or through {@link ProjectRef#addTestToPipeline}.
*/
export interface CommonPipelineTestActionProps extends codepipeline.CommonActionProps {
/**
* The source to use as input for this test.
*
* @default CodePipeline will use the output of the last Action from a previous Stage as input
*/
inputArtifact?: codepipeline.Artifact;

/**
* The optional name of the output artifact.
* If you provide a value here,
* then the `outputArtifact` property of your Action will be non-null.
* If you don't, `outputArtifact` will be `null`.
*
* @default the Action will not have an output artifact
*/
outputArtifactName?: string;
}

/**
* Construction properties of the {@link PipelineTestAction CodeBuild test CodePipeline Action}.
*/
export interface PipelineTestActionProps extends CommonPipelineTestActionProps,
codepipeline.CommonActionConstructProps {
/**
* The build Project.
*/
project: ProjectRef;
}

export class PipelineTestAction extends codepipeline.TestAction {
constructor(parent: cdk.Construct, name: string, props: PipelineTestActionProps) {
super(parent, name, {
provider: 'CodeBuild',
configuration: {
ProjectName: props.project.projectName,
},
...props,
});

// the Action needs write permissions only if it's producing an output artifact
setCodeBuildNeededPermissions(props.stage, props.project, !!props.outputArtifactName);
}
}

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()
.addResource(project.projectArn)
.addActions(
'codebuild:BatchGetBuilds',
'codebuild:StartBuild',
'codebuild:StopBuild',
];
));

props.stage.pipelineRole.addToPolicy(new iam.PolicyStatement()
.addResource(props.project.projectArn)
.addActions(...actions));

// allow codebuild to read and write artifacts to the pipline's artifact bucket.
if (props.project.role) {
props.stage.grantPipelineBucketReadWrite(props.project.role);
// allow the Project access to the Pipline's artifact Bucket
if (project.role) {
if (needsPipelineBucketWrite) {
stage.grantPipelineBucketReadWrite(project.role);
} else {
stage.grantPipelineBucketRead(project.role);
}

// policy must be added as a dependency to the pipeline!!
// TODO: grants - build.addResourcePermission() and also make sure permission
// includes the pipeline role AWS principal.
}
}
22 changes: 21 additions & 1 deletion packages/@aws-cdk/aws-codebuild/lib/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import s3 = require('@aws-cdk/aws-s3');
import cdk = require('@aws-cdk/cdk');
import { BuildArtifacts, CodePipelineBuildArtifacts, NoBuildArtifacts } from './artifacts';
import { cloudformation } from './codebuild.generated';
import { CommonPipelineBuildActionProps, PipelineBuildAction } from './pipeline-actions';
import {
CommonPipelineBuildActionProps, CommonPipelineTestActionProps,
PipelineBuildAction, PipelineTestAction
} from './pipeline-actions';
import { BuildSource, NoSource } from './source';

const CODEPIPELINE_TYPE = 'CODEPIPELINE';
Expand Down Expand Up @@ -97,6 +100,23 @@ export abstract class ProjectRef extends cdk.Construct implements events.IEventR
});
}

/**
* Convenience method for creating a new {@link PipelineTestAction} test Action,
* and adding it to the given Stage.
*
* @param stage the Pipeline Stage to add the new Action to
* @param name the name of the newly created Action
* @param props the properties of the new Action
* @returns the newly created {@link PipelineBuildAction} test Action
*/
public addTestToPipeline(stage: codepipeline.IStage, name: string, props: CommonPipelineTestActionProps = {}): PipelineTestAction {
return new PipelineTestAction(this, name, {
stage,
project: this,
...props,
});
}

/**
* Defines a CloudWatch event rule triggered when the build project state
* changes. You can filter specific build status events using an event
Expand Down
25 changes: 6 additions & 19 deletions packages/@aws-cdk/aws-codepipeline-api/lib/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ export interface IStage {
*/
readonly _internal: IInternalStage;

/* 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;

/**
* Grants read & write permissions to the Pipeline's S3 Bucket to the given Identity.
*
Expand Down Expand Up @@ -238,25 +244,6 @@ export abstract class Action extends cdk.Construct {
}
}

// export class TestAction extends Action {
// constructor(parent: Stage, name: string, provider: string, artifactBounds: ActionArtifactBounds, configuration?: any) {
// super(parent, name, {
// category: ActionCategory.Test,
// provider,
// artifactBounds,
// configuration
// });
// }
// }

// export class CodeBuildTest extends TestAction {
// constructor(parent: Stage, name: string, project: codebuild.ProjectArnAttribute) {
// super(parent, name, 'CodeBuild', { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 1 }, {
// ProjectName: project
// });
// }
// }

// export class ElasticBeanstalkDeploy extends DeployAction {
// constructor(parent: Stage, name: string, applicationName: string, environmentName: string) {
// super(parent, name, 'ElasticBeanstalk', { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 0 }, {
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-codepipeline-api/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export * from './action';
export * from './build-action';
export * from './deploy-action';
export * from './source-action';
export * from './test-action';
export * from './validation';
74 changes: 74 additions & 0 deletions packages/@aws-cdk/aws-codepipeline-api/lib/test-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import cdk = require("@aws-cdk/cdk");
import { Action, ActionCategory, CommonActionConstructProps, CommonActionProps } from "./action";
import { Artifact } from "./artifact";

/**
* Construction properties of the low-level {@link TestAction test Action}.
*/
export interface TestActionProps extends CommonActionProps, CommonActionConstructProps {
/**
* The source to use as input for this test.
*
* @default CodePipeline will use the output of the last Action from a previous Stage as input
*/
inputArtifact?: Artifact;

/**
* The optional name of the output artifact.
* If you provide a value here,
* then the `outputArtifact` property of your Action will be non-null.
* If you don't, `outputArtifact` will be `null`.
*
* @default the Action will not have an output artifact
*/
outputArtifactName?: string;

/**
* The service provider that the action calls.
*
* @example 'CodeBuild'
*/
provider: string;

/**
* The source action owner (could be 'AWS', 'ThirdParty' or 'Custom').
*
* @default 'AWS'
*/
owner?: string;

/**
* The action's configuration. These are key-value pairs that specify input values for an action.
* For more information, see the AWS CodePipeline User Guide.
*
* http://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html#action-requirements
*/
configuration?: any;
}

/**
* The low-level test Action.
*
* Test Actions are very similar to build Actions -
* the difference is that test Actions don't have to have an output artifact.
*
* You should never need to use this class directly,
* instead preferring the concrete implementations,
* like {@link codebuild.PipelineTestAction}.
*/
export abstract class TestAction extends Action {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add some documentation here explaining that this is an abstract class and reference the concrete implementations and/or APIs such as addTestToPipeline

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

public readonly outputArtifact?: Artifact;

constructor(parent: cdk.Construct, name: string, props: TestActionProps) {
super(parent, name, {
category: ActionCategory.Test,
artifactBounds: { minInputs: 1, maxInputs: 1, minOutputs: 0, maxOutputs: 0 },
...props,
});

this.addInputArtifact(props.inputArtifact);
if (props.outputArtifactName) {
this.outputArtifact = this.addOutputArtifact(props.outputArtifactName);
}
}
}
4 changes: 4 additions & 0 deletions packages/@aws-cdk/aws-codepipeline/lib/stage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ export class Stage extends cdk.Construct implements actions.IStage, actions.IInt
return this.validateHasActions();
}

public grantPipelineBucketRead(identity: iam.IPrincipal): void {
this.pipeline.artifactBucket.grantRead(identity);
}

public grantPipelineBucketReadWrite(identity: iam.IPrincipal): void {
this.pipeline.artifactBucket.grantReadWrite(identity);
}
Expand Down
Loading