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(cloudformation): allow specifying additional inputs for deploy Actions #2020

Merged
merged 5 commits into from
Mar 15, 2019
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
27 changes: 23 additions & 4 deletions packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,23 @@ export interface PipelineCloudFormationDeployActionProps extends PipelineCloudFo
* @default No overrides
*/
parameterOverrides?: { [name: string]: any };

/**
* The list of additional input Artifacts for this Action.
* This is especially useful when used in conjunction with the `parameterOverrides` property.
* For example, if you have:
*
* parameterOverrides: {
* 'Param1': action1.outputArtifact.bucketName,
* 'Param2': action2.outputArtifact.objectKey,
* }
*
* , if the output Artifacts of `action1` and `action2` were not used to
Copy link
Contributor

Choose a reason for hiding this comment

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

comma at the beginning of the sentence?

* set either the `templateConfiguration` or the `templatePath` properties,
* you need to make sure to include them in the `additionalInputArtifacts` -
* otherwise, you'll get an "unrecognized Artifact" error during your Pipeline's execution.
Copy link
Contributor

Choose a reason for hiding this comment

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

mention that artifacts can also be added using addInputArtifact

*/
additionalInputArtifacts?: codepipeline.Artifact[];
}
// tslint:enable:max-line-length

Expand All @@ -223,6 +240,10 @@ export abstract class PipelineCloudFormationDeployAction extends PipelineCloudFo
});

this.props = props;

for (const inputArtifact of props.additionalInputArtifacts || []) {
this.addInputArtifact(inputArtifact);
}
}

/**
Expand Down Expand Up @@ -293,8 +314,7 @@ export class PipelineCreateReplaceChangeSetAction extends PipelineCloudFormation
});

this.addInputArtifact(props.templatePath.artifact);
if (props.templateConfiguration &&
props.templateConfiguration.artifact.artifactName !== props.templatePath.artifact.artifactName) {
if (props.templateConfiguration) {
this.addInputArtifact(props.templateConfiguration.artifact);
}

Expand Down Expand Up @@ -357,8 +377,7 @@ export class PipelineCreateUpdateStackAction extends PipelineCloudFormationDeplo
});

this.addInputArtifact(props.templatePath.artifact);
if (props.templateConfiguration &&
props.templateConfiguration.artifact.artifactName !== props.templatePath.artifact.artifactName) {
if (props.templateConfiguration) {
this.addInputArtifact(props.templateConfiguration.artifact);
}

Expand Down
18 changes: 18 additions & 0 deletions packages/@aws-cdk/aws-codepipeline-api/lib/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,30 @@ export abstract class Action {
}

protected addOutputArtifact(name: string): Artifact {
// adding the same name multiple times doesn't do anything -
// addOutputArtifact is idempotent
const ret = this._outputArtifacts.find(output => output.artifactName === name);
if (ret) {
return ret;
}

const artifact = new Artifact(name);
this._actionOutputArtifacts.push(artifact);
return artifact;
}

protected addInputArtifact(artifact: Artifact): Action {
// adding the same artifact multiple times doesn't do anything -
// addInputArtifact is idempotent
if (this._actionInputArtifacts.indexOf(artifact) !== -1) {
return this;
}

// however, a _different_ input with the same name is an error
if (this._actionInputArtifacts.find(input => input.artifactName === artifact.artifactName)) {
throw new Error(`Action ${this.actionName} already has an input with the name '${artifact.artifactName}'`);
}

this._actionInputArtifacts.push(artifact);
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,12 @@
"Arn"
]
},
"ParameterOverrides": "{\"BucketName\":{\"Fn::GetArtifactAtt\":[\"SourceArtifact\",\"BucketName\"]},\"ObjectKey\":{\"Fn::GetArtifactAtt\":[\"SourceArtifact\",\"ObjectKey\"]},\"Url\":{\"Fn::GetArtifactAtt\":[\"SourceArtifact\",\"URL\"]},\"OtherParam\":{\"Fn::GetParam\":[\"SourceArtifact\",\"params.json\",\"OtherParam\"]}}"
"ParameterOverrides": "{\"BucketName\":{\"Fn::GetArtifactAtt\":[\"SourceArtifact\",\"BucketName\"]},\"ObjectKey\":{\"Fn::GetArtifactAtt\":[\"SourceArtifact\",\"ObjectKey\"]},\"Url\":{\"Fn::GetArtifactAtt\":[\"AdditionalArtifact\",\"URL\"]},\"OtherParam\":{\"Fn::GetParam\":[\"SourceArtifact\",\"params.json\",\"OtherParam\"]}}"
},
"InputArtifacts": [
{
"Name": "AdditionalArtifact"
},
{
"Name": "SourceArtifact"
}
Expand Down Expand Up @@ -274,4 +277,4 @@
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import cfn = require('@aws-cdk/aws-cloudformation');
import cpapi = require('@aws-cdk/aws-codepipeline-api');
import { Role } from '@aws-cdk/aws-iam';
import { ServicePrincipal } from '@aws-cdk/aws-iam';
import s3 = require('@aws-cdk/aws-s3');
Expand Down Expand Up @@ -33,6 +34,9 @@ const role = new Role(stack, 'CfnChangeSetRole', {
assumedBy: new ServicePrincipal('cloudformation.amazonaws.com'),
});

// fake Artifact, just for testing
const additionalArtifact = new cpapi.Artifact('AdditionalArtifact');

pipeline.addStage(sourceStage);
pipeline.addStage({
name: 'CFN',
Expand All @@ -47,9 +51,10 @@ pipeline.addStage({
parameterOverrides: {
BucketName: source.outputArtifact.bucketName,
ObjectKey: source.outputArtifact.objectKey,
Url: source.outputArtifact.url,
Url: additionalArtifact.url,
OtherParam: source.outputArtifact.getParam('params.json', 'OtherParam'),
},
additionalInputArtifacts: [additionalArtifact],
}),
],
});
Expand Down
59 changes: 58 additions & 1 deletion packages/@aws-cdk/aws-codepipeline/test/test.action.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// import { validateArtifactBounds, validateSourceAction } from '../lib/validation';
import { expect, haveResourceLike } from '@aws-cdk/assert';
import codebuild = require('@aws-cdk/aws-codebuild');
import codecommit = require('@aws-cdk/aws-codecommit');
Expand Down Expand Up @@ -150,6 +149,64 @@ export = {

test.done();
},

'input Artifacts': {
'can be added multiple times to an Action safely'(test: Test) {
const artifact = new actions.Artifact('SomeArtifact');

const stack = new cdk.Stack();
const project = new codebuild.PipelineProject(stack, 'Project');
const action = project.toCodePipelineBuildAction({
actionName: 'CodeBuild',
inputArtifact: artifact,
additionalInputArtifacts: [artifact],
});

test.equal(action._inputArtifacts.length, 1);

test.done();
},

'cannot have duplicate names'(test: Test) {
const artifact1 = new actions.Artifact('SomeArtifact');
const artifact2 = new actions.Artifact('SomeArtifact');

const stack = new cdk.Stack();
const project = new codebuild.PipelineProject(stack, 'Project');

test.throws(() =>
project.toCodePipelineBuildAction({
actionName: 'CodeBuild',
inputArtifact: artifact1,
additionalInputArtifacts: [artifact2],
})
, /SomeArtifact/);

test.done();
},
},

'output Artifact names': {
'accept the same name multiple times safely'(test: Test) {
const artifact = new actions.Artifact('SomeArtifact');

const stack = new cdk.Stack();
const project = new codebuild.PipelineProject(stack, 'Project');
const action = project.toCodePipelineBuildAction({
actionName: 'CodeBuild',
inputArtifact: artifact,
outputArtifactName: 'Artifact1',
additionalOutputArtifactNames: [
'Artifact1',
'Artifact1',
],
});

test.equal(action._outputArtifacts.length, 1);

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

function boundsValidationResult(numberOfArtifacts: number, min: number, max: number): string[] {
Expand Down