@@ -7,7 +7,7 @@ import cloudformation = require('../lib');
7
7
8
8
export = nodeunit . testCase ( {
9
9
'CreateReplaceChangeSet' : {
10
- works ( test : nodeunit . Test ) {
10
+ ' works' ( test : nodeunit . Test ) {
11
11
const stack = new cdk . Stack ( ) ;
12
12
const pipelineRole = new RoleDouble ( stack , 'PipelineRole' ) ;
13
13
const stage = new StageDouble ( { pipelineRole } ) ;
@@ -22,8 +22,8 @@ export = nodeunit.testCase({
22
22
_assertPermissionGranted ( test , pipelineRole . statements , 'iam:PassRole' , action . role . roleArn ) ;
23
23
24
24
const stackArn = _stackArn ( 'MyStack' ) ;
25
- const changeSetCondition = { StringEquals : { 'cloudformation:ChangeSetName' : 'MyChangeSet' } } ;
26
- _assertPermissionGranted ( test , pipelineRole . statements , 'cloudformation:DescribeStacks' , stackArn ) ;
25
+ const changeSetCondition = { StringEqualsIfExists : { 'cloudformation:ChangeSetName' : 'MyChangeSet' } } ;
26
+ _assertPermissionGranted ( test , pipelineRole . statements , 'cloudformation:DescribeStacks' , stackArn , changeSetCondition ) ;
27
27
_assertPermissionGranted ( test , pipelineRole . statements , 'cloudformation:DescribeChangeSet' , stackArn , changeSetCondition ) ;
28
28
_assertPermissionGranted ( test , pipelineRole . statements , 'cloudformation:CreateChangeSet' , stackArn , changeSetCondition ) ;
29
29
_assertPermissionGranted ( test , pipelineRole . statements , 'cloudformation:DeleteChangeSet' , stackArn , changeSetCondition ) ;
@@ -37,11 +37,64 @@ export = nodeunit.testCase({
37
37
ChangeSetName : 'MyChangeSet'
38
38
} ) ;
39
39
40
+ test . done ( ) ;
41
+ } ,
42
+
43
+ 'uses a single permission statement if the same ChangeSet name is used' ( test : nodeunit . Test ) {
44
+ const stack = new cdk . Stack ( ) ;
45
+ const pipelineRole = new RoleDouble ( stack , 'PipelineRole' ) ;
46
+ const stage = new StageDouble ( { pipelineRole } ) ;
47
+ const artifact = new cpapi . Artifact ( stack as any , 'TestArtifact' ) ;
48
+ new cloudformation . PipelineCreateReplaceChangeSetAction ( stack , 'ActionA' , {
49
+ stage,
50
+ changeSetName : 'MyChangeSet' ,
51
+ stackName : 'StackA' ,
52
+ templatePath : artifact . atPath ( 'path/to/file' )
53
+ } ) ;
54
+
55
+ new cloudformation . PipelineCreateReplaceChangeSetAction ( stack , 'ActionB' , {
56
+ stage,
57
+ changeSetName : 'MyChangeSet' ,
58
+ stackName : 'StackB' ,
59
+ templatePath : artifact . atPath ( 'path/to/other/file' )
60
+ } ) ;
61
+
62
+ test . deepEqual (
63
+ cdk . resolve ( pipelineRole . statements ) ,
64
+ [
65
+ {
66
+ Action : 'iam:PassRole' ,
67
+ Effect : 'Allow' ,
68
+ Resource : [
69
+ { 'Fn::GetAtt' : [ 'ActionARole72759154' , 'Arn' ] } ,
70
+ { 'Fn::GetAtt' : [ 'ActionBRole6A2F6804' , 'Arn' ] }
71
+ ] ,
72
+ } ,
73
+ {
74
+ Action : [
75
+ 'cloudformation:CreateChangeSet' ,
76
+ 'cloudformation:DeleteChangeSet' ,
77
+ 'cloudformation:DescribeChangeSet' ,
78
+ 'cloudformation:DescribeStacks'
79
+ ] ,
80
+ Condition : { StringEqualsIfExists : { 'cloudformation:ChangeSetName' : 'MyChangeSet' } } ,
81
+ Effect : 'Allow' ,
82
+ Resource : [
83
+ // tslint:disable-next-line:max-line-length
84
+ { 'Fn::Join' : [ '' , [ 'arn:' , { Ref : 'AWS::Partition' } , ':cloudformation:' , { Ref : 'AWS::Region' } , ':' , { Ref : 'AWS::AccountId' } , ':stack/StackA/*' ] ] } ,
85
+ // tslint:disable-next-line:max-line-length
86
+ { 'Fn::Join' : [ '' , [ 'arn:' , { Ref : 'AWS::Partition' } , ':cloudformation:' , { Ref : 'AWS::Region' } , ':' , { Ref : 'AWS::AccountId' } , ':stack/StackB/*' ] ] }
87
+ ] ,
88
+ }
89
+ ]
90
+ ) ;
91
+
40
92
test . done ( ) ;
41
93
}
42
94
} ,
95
+
43
96
'ExecuteChangeSet' : {
44
- works ( test : nodeunit . Test ) {
97
+ ' works' ( test : nodeunit . Test ) {
45
98
const stack = new cdk . Stack ( ) ;
46
99
const pipelineRole = new RoleDouble ( stack , 'PipelineRole' ) ;
47
100
const stage = new StageDouble ( { pipelineRole } ) ;
@@ -61,6 +114,42 @@ export = nodeunit.testCase({
61
114
ChangeSetName : 'MyChangeSet'
62
115
} ) ;
63
116
117
+ test . done ( ) ;
118
+ } ,
119
+
120
+ 'uses a single permission statement if the same ChangeSet name is used' ( test : nodeunit . Test ) {
121
+ const stack = new cdk . Stack ( ) ;
122
+ const pipelineRole = new RoleDouble ( stack , 'PipelineRole' ) ;
123
+ const stage = new StageDouble ( { pipelineRole } ) ;
124
+ new cloudformation . PipelineExecuteChangeSetAction ( stack , 'ActionA' , {
125
+ stage,
126
+ changeSetName : 'MyChangeSet' ,
127
+ stackName : 'StackA' ,
128
+ } ) ;
129
+
130
+ new cloudformation . PipelineExecuteChangeSetAction ( stack , 'ActionB' , {
131
+ stage,
132
+ changeSetName : 'MyChangeSet' ,
133
+ stackName : 'StackB' ,
134
+ } ) ;
135
+
136
+ test . deepEqual (
137
+ cdk . resolve ( pipelineRole . statements ) ,
138
+ [
139
+ {
140
+ Action : 'cloudformation:ExecuteChangeSet' ,
141
+ Condition : { StringEquals : { 'cloudformation:ChangeSetName' : 'MyChangeSet' } } ,
142
+ Effect : 'Allow' ,
143
+ Resource : [
144
+ // tslint:disable-next-line:max-line-length
145
+ { 'Fn::Join' : [ '' , [ 'arn:' , { Ref : 'AWS::Partition' } , ':cloudformation:' , { Ref : 'AWS::Region' } , ':' , { Ref : 'AWS::AccountId' } , ':stack/StackA/*' ] ] } ,
146
+ // tslint:disable-next-line:max-line-length
147
+ { 'Fn::Join' : [ '' , [ 'arn:' , { Ref : 'AWS::Partition' } , ':cloudformation:' , { Ref : 'AWS::Region' } , ':' , { Ref : 'AWS::AccountId' } , ':stack/StackB/*' ] ] }
148
+ ] ,
149
+ }
150
+ ]
151
+ ) ;
152
+
64
153
test . done ( ) ;
65
154
}
66
155
} ,
@@ -72,6 +161,7 @@ export = nodeunit.testCase({
72
161
stage : new StageDouble ( { pipelineRole } ) ,
73
162
templatePath : new cpapi . Artifact ( stack as any , 'TestArtifact' ) . atPath ( 'some/file' ) ,
74
163
stackName : 'MyStack' ,
164
+ replaceOnFailure : true ,
75
165
} ) ;
76
166
const stackArn = _stackArn ( 'MyStack' ) ;
77
167
@@ -144,12 +234,13 @@ function _hasAction(actions: cpapi.Action[], owner: string, provider: string, ca
144
234
return false ;
145
235
}
146
236
147
- function _assertPermissionGranted ( test : nodeunit . Test , statements : PolicyStatementJson [ ] , action : string , resource : string , conditions ?: any ) {
237
+ function _assertPermissionGranted ( test : nodeunit . Test , statements : iam . PolicyStatement [ ] , action : string , resource : string , conditions ?: any ) {
148
238
const conditionStr = conditions
149
239
? ` with condition(s) ${ JSON . stringify ( cdk . resolve ( conditions ) ) } `
150
240
: '' ;
151
- const statementsStr = JSON . stringify ( cdk . resolve ( statements ) , null , 2 ) ;
152
- test . ok ( _grantsPermission ( statements , action , resource , conditions ) ,
241
+ const resolvedStatements = cdk . resolve ( statements ) ;
242
+ const statementsStr = JSON . stringify ( resolvedStatements , null , 2 ) ;
243
+ test . ok ( _grantsPermission ( resolvedStatements , action , resource , conditions ) ,
153
244
`Expected to find a statement granting ${ action } on ${ JSON . stringify ( cdk . resolve ( resource ) ) } ${ conditionStr } , found:\n${ statementsStr } ` ) ;
154
245
}
155
246
@@ -218,14 +309,14 @@ class StageDouble implements cpapi.IStage, cpapi.IInternalStage {
218
309
}
219
310
220
311
class RoleDouble extends iam . Role {
221
- public readonly statements = new Array < PolicyStatementJson > ( ) ;
312
+ public readonly statements = new Array < iam . PolicyStatement > ( ) ;
222
313
223
314
constructor ( parent : cdk . Construct , id : string , props : iam . RoleProps = { assumedBy : new iam . ServicePrincipal ( 'test' ) } ) {
224
315
super ( parent , id , props ) ;
225
316
}
226
317
227
318
public addToPolicy ( statement : iam . PolicyStatement ) {
228
319
super . addToPolicy ( statement ) ;
229
- this . statements . push ( statement . toJson ( ) ) ;
320
+ this . statements . push ( statement ) ;
230
321
}
231
322
}
0 commit comments