Skip to content

Commit 2d463be

Browse files
authored
feat(cloudformation): allow specifying additional inputs for deploy Actions (#2020)
Additional input Artifacts are needed when using the `parameterOverrides` property of the Action (any Artifact used in that map needs to be added to the input Artifacts of the Action, otherwise the Pipeline will fail at runtime). Also did some cleanup in the Action superclass while I was in the area. Fixes #1247
1 parent 1f24336 commit 2d463be

File tree

5 files changed

+110
-8
lines changed

5 files changed

+110
-8
lines changed

packages/@aws-cdk/aws-cloudformation/lib/pipeline-actions.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,23 @@ export interface PipelineCloudFormationDeployActionProps extends PipelineCloudFo
200200
* @default No overrides
201201
*/
202202
parameterOverrides?: { [name: string]: any };
203+
204+
/**
205+
* The list of additional input Artifacts for this Action.
206+
* This is especially useful when used in conjunction with the `parameterOverrides` property.
207+
* For example, if you have:
208+
*
209+
* parameterOverrides: {
210+
* 'Param1': action1.outputArtifact.bucketName,
211+
* 'Param2': action2.outputArtifact.objectKey,
212+
* }
213+
*
214+
* , if the output Artifacts of `action1` and `action2` were not used to
215+
* set either the `templateConfiguration` or the `templatePath` properties,
216+
* you need to make sure to include them in the `additionalInputArtifacts` -
217+
* otherwise, you'll get an "unrecognized Artifact" error during your Pipeline's execution.
218+
*/
219+
additionalInputArtifacts?: codepipeline.Artifact[];
203220
}
204221
// tslint:enable:max-line-length
205222

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

225242
this.props = props;
243+
244+
for (const inputArtifact of props.additionalInputArtifacts || []) {
245+
this.addInputArtifact(inputArtifact);
246+
}
226247
}
227248

228249
/**
@@ -293,8 +314,7 @@ export class PipelineCreateReplaceChangeSetAction extends PipelineCloudFormation
293314
});
294315

295316
this.addInputArtifact(props.templatePath.artifact);
296-
if (props.templateConfiguration &&
297-
props.templateConfiguration.artifact.artifactName !== props.templatePath.artifact.artifactName) {
317+
if (props.templateConfiguration) {
298318
this.addInputArtifact(props.templateConfiguration.artifact);
299319
}
300320

@@ -357,8 +377,7 @@ export class PipelineCreateUpdateStackAction extends PipelineCloudFormationDeplo
357377
});
358378

359379
this.addInputArtifact(props.templatePath.artifact);
360-
if (props.templateConfiguration &&
361-
props.templateConfiguration.artifact.artifactName !== props.templatePath.artifact.artifactName) {
380+
if (props.templateConfiguration) {
362381
this.addInputArtifact(props.templateConfiguration.artifact);
363382
}
364383

packages/@aws-cdk/aws-codepipeline-api/lib/action.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,12 +252,30 @@ export abstract class Action {
252252
}
253253

254254
protected addOutputArtifact(name: string): Artifact {
255+
// adding the same name multiple times doesn't do anything -
256+
// addOutputArtifact is idempotent
257+
const ret = this._outputArtifacts.find(output => output.artifactName === name);
258+
if (ret) {
259+
return ret;
260+
}
261+
255262
const artifact = new Artifact(name);
256263
this._actionOutputArtifacts.push(artifact);
257264
return artifact;
258265
}
259266

260267
protected addInputArtifact(artifact: Artifact): Action {
268+
// adding the same artifact multiple times doesn't do anything -
269+
// addInputArtifact is idempotent
270+
if (this._actionInputArtifacts.indexOf(artifact) !== -1) {
271+
return this;
272+
}
273+
274+
// however, a _different_ input with the same name is an error
275+
if (this._actionInputArtifacts.find(input => input.artifactName === artifact.artifactName)) {
276+
throw new Error(`Action ${this.actionName} already has an input with the name '${artifact.artifactName}'`);
277+
}
278+
261279
this._actionInputArtifacts.push(artifact);
262280
return this;
263281
}

packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.expected.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,12 @@
211211
"Arn"
212212
]
213213
},
214-
"ParameterOverrides": "{\"BucketName\":{\"Fn::GetArtifactAtt\":[\"SourceArtifact\",\"BucketName\"]},\"ObjectKey\":{\"Fn::GetArtifactAtt\":[\"SourceArtifact\",\"ObjectKey\"]},\"Url\":{\"Fn::GetArtifactAtt\":[\"SourceArtifact\",\"URL\"]},\"OtherParam\":{\"Fn::GetParam\":[\"SourceArtifact\",\"params.json\",\"OtherParam\"]}}"
214+
"ParameterOverrides": "{\"BucketName\":{\"Fn::GetArtifactAtt\":[\"SourceArtifact\",\"BucketName\"]},\"ObjectKey\":{\"Fn::GetArtifactAtt\":[\"SourceArtifact\",\"ObjectKey\"]},\"Url\":{\"Fn::GetArtifactAtt\":[\"AdditionalArtifact\",\"URL\"]},\"OtherParam\":{\"Fn::GetParam\":[\"SourceArtifact\",\"params.json\",\"OtherParam\"]}}"
215215
},
216216
"InputArtifacts": [
217+
{
218+
"Name": "AdditionalArtifact"
219+
},
217220
{
218221
"Name": "SourceArtifact"
219222
}
@@ -274,4 +277,4 @@
274277
}
275278
}
276279
}
277-
}
280+
}

packages/@aws-cdk/aws-codepipeline/test/integ.pipeline-cfn.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import cfn = require('@aws-cdk/aws-cloudformation');
2+
import cpapi = require('@aws-cdk/aws-codepipeline-api');
23
import { Role } from '@aws-cdk/aws-iam';
34
import { ServicePrincipal } from '@aws-cdk/aws-iam';
45
import s3 = require('@aws-cdk/aws-s3');
@@ -33,6 +34,9 @@ const role = new Role(stack, 'CfnChangeSetRole', {
3334
assumedBy: new ServicePrincipal('cloudformation.amazonaws.com'),
3435
});
3536

37+
// fake Artifact, just for testing
38+
const additionalArtifact = new cpapi.Artifact('AdditionalArtifact');
39+
3640
pipeline.addStage(sourceStage);
3741
pipeline.addStage({
3842
name: 'CFN',
@@ -47,9 +51,10 @@ pipeline.addStage({
4751
parameterOverrides: {
4852
BucketName: source.outputArtifact.bucketName,
4953
ObjectKey: source.outputArtifact.objectKey,
50-
Url: source.outputArtifact.url,
54+
Url: additionalArtifact.url,
5155
OtherParam: source.outputArtifact.getParam('params.json', 'OtherParam'),
5256
},
57+
additionalInputArtifacts: [additionalArtifact],
5358
}),
5459
],
5560
});

packages/@aws-cdk/aws-codepipeline/test/test.action.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// import { validateArtifactBounds, validateSourceAction } from '../lib/validation';
21
import { expect, haveResourceLike } from '@aws-cdk/assert';
32
import codebuild = require('@aws-cdk/aws-codebuild');
43
import codecommit = require('@aws-cdk/aws-codecommit');
@@ -150,6 +149,64 @@ export = {
150149

151150
test.done();
152151
},
152+
153+
'input Artifacts': {
154+
'can be added multiple times to an Action safely'(test: Test) {
155+
const artifact = new actions.Artifact('SomeArtifact');
156+
157+
const stack = new cdk.Stack();
158+
const project = new codebuild.PipelineProject(stack, 'Project');
159+
const action = project.toCodePipelineBuildAction({
160+
actionName: 'CodeBuild',
161+
inputArtifact: artifact,
162+
additionalInputArtifacts: [artifact],
163+
});
164+
165+
test.equal(action._inputArtifacts.length, 1);
166+
167+
test.done();
168+
},
169+
170+
'cannot have duplicate names'(test: Test) {
171+
const artifact1 = new actions.Artifact('SomeArtifact');
172+
const artifact2 = new actions.Artifact('SomeArtifact');
173+
174+
const stack = new cdk.Stack();
175+
const project = new codebuild.PipelineProject(stack, 'Project');
176+
177+
test.throws(() =>
178+
project.toCodePipelineBuildAction({
179+
actionName: 'CodeBuild',
180+
inputArtifact: artifact1,
181+
additionalInputArtifacts: [artifact2],
182+
})
183+
, /SomeArtifact/);
184+
185+
test.done();
186+
},
187+
},
188+
189+
'output Artifact names': {
190+
'accept the same name multiple times safely'(test: Test) {
191+
const artifact = new actions.Artifact('SomeArtifact');
192+
193+
const stack = new cdk.Stack();
194+
const project = new codebuild.PipelineProject(stack, 'Project');
195+
const action = project.toCodePipelineBuildAction({
196+
actionName: 'CodeBuild',
197+
inputArtifact: artifact,
198+
outputArtifactName: 'Artifact1',
199+
additionalOutputArtifactNames: [
200+
'Artifact1',
201+
'Artifact1',
202+
],
203+
});
204+
205+
test.equal(action._outputArtifacts.length, 1);
206+
207+
test.done();
208+
},
209+
},
153210
};
154211

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

0 commit comments

Comments
 (0)