Skip to content

Commit 17ab056

Browse files
committed
Add StackHookTargetModel for support of stack-level hooks
Add new type that represents the payload sent to hook handlers that will contain information related to stack hooks.
1 parent b96cf3d commit 17ab056

File tree

7 files changed

+308
-6
lines changed

7 files changed

+308
-6
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
package software.amazon.cloudformation.proxy.hook.targetmodel;
16+
17+
import com.fasterxml.jackson.annotation.JsonProperty;
18+
import lombok.*;
19+
20+
@Data
21+
@Builder
22+
@AllArgsConstructor
23+
@NoArgsConstructor
24+
public class ChangedResource {
25+
@JsonProperty("LogicalResourceId")
26+
private String logicalResourceId;
27+
28+
@JsonProperty("ResourceType")
29+
private String resourceType;
30+
31+
@JsonProperty("LineNumber")
32+
private Integer lineNumber;
33+
34+
@JsonProperty("Action")
35+
private String action;
36+
37+
@JsonProperty("ResourceProperties")
38+
private String resourceProperties;
39+
40+
@JsonProperty("PreviousResourceProperties")
41+
private String previousResourceProperties;
42+
}

src/main/java/software/amazon/cloudformation/proxy/hook/targetmodel/HookTargetType.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,17 @@ public enum HookTargetType {
2626
* A target model meant to represent a target for a Resource Hook. This model
2727
* type will have properties specific to the resource type.
2828
*/
29-
RESOURCE;
29+
RESOURCE,
30+
31+
/**
32+
* A target model meant to represent a target for a Stack Hook. This model type
33+
* will have properties specific to the stack type.
34+
*/
35+
STACK,
36+
37+
/**
38+
* A target model meant to represent a target for a stack Change Set Hook. This
39+
* model type will have properties specific to the change set type.
40+
*/
41+
CHANGE_SET;
3042
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
package software.amazon.cloudformation.proxy.hook.targetmodel;
16+
17+
import com.fasterxml.jackson.annotation.JsonAutoDetect;
18+
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
19+
import com.fasterxml.jackson.annotation.JsonProperty;
20+
import com.fasterxml.jackson.core.type.TypeReference;
21+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
22+
import java.util.List;
23+
import lombok.*;
24+
25+
@EqualsAndHashCode(callSuper = false)
26+
@Getter
27+
@NoArgsConstructor
28+
@ToString
29+
@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
30+
@JsonDeserialize(as = StackHookTargetModel.class)
31+
public class StackHookTargetModel extends HookTargetModel {
32+
private static final TypeReference<StackHookTargetModel> MODEL_REFERENCE = new TypeReference<StackHookTargetModel>() {
33+
};
34+
35+
@JsonProperty("Template")
36+
private String template;
37+
38+
@JsonProperty("PreviousTemplate")
39+
private String previousTemplate;
40+
41+
@JsonProperty("ResolvedTemplate")
42+
private String resolvedTemplate;
43+
44+
@JsonProperty("ChangedResources")
45+
private List<ChangedResource> changedResources;
46+
47+
@Override
48+
public TypeReference<? extends HookTarget> getHookTargetTypeReference() {
49+
return null;
50+
}
51+
52+
@Override
53+
public TypeReference<? extends HookTargetModel> getTargetModelTypeReference() {
54+
return MODEL_REFERENCE;
55+
}
56+
57+
@Override
58+
public final HookTargetType getHookTargetType() {
59+
return HookTargetType.STACK;
60+
}
61+
}

src/test/java/software/amazon/cloudformation/HookLambdaWrapperOverride.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@
3232
import software.amazon.cloudformation.metrics.MetricsPublisher;
3333
import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
3434
import software.amazon.cloudformation.proxy.ProgressEvent;
35+
import software.amazon.cloudformation.proxy.hook.HookContext;
3536
import software.amazon.cloudformation.proxy.hook.HookHandlerRequest;
3637
import software.amazon.cloudformation.proxy.hook.HookInvocationRequest;
38+
import software.amazon.cloudformation.proxy.hook.targetmodel.HookTargetModel;
3739
import software.amazon.cloudformation.resource.SchemaValidator;
3840
import software.amazon.cloudformation.resource.Serializer;
3941

@@ -112,7 +114,16 @@ public void enqueueResponses(final List<ProgressEvent<TestModel, TestContext>> r
112114

113115
@Override
114116
protected HookHandlerRequest transform(final HookInvocationRequest<TestConfigurationModel, TestContext> request) {
115-
return transformResponse;
117+
this.request = HookHandlerRequest.builder().clientRequestToken(request.getClientRequestToken())
118+
.hookContext(HookContext.builder().awsAccountId(request.getAwsAccountId()).stackId(request.getStackId())
119+
.changeSetId(request.getChangeSetId()).hookTypeName(request.getHookTypeName())
120+
.hookTypeVersion(request.getHookTypeVersion()).invocationPoint(request.getActionInvocationPoint())
121+
.targetName(request.getRequestData().getTargetName()).targetType(request.getRequestData().getTargetType())
122+
.targetLogicalId(request.getRequestData().getTargetLogicalId())
123+
.targetModel(HookTargetModel.of(request.getRequestData().getTargetModel())).build())
124+
.build();
125+
126+
return this.request;
116127
}
117128

118129
public HookHandlerRequest transformResponse;

src/test/java/software/amazon/cloudformation/HookLambdaWrapperTest.java

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
import software.amazon.cloudformation.proxy.hook.HookHandlerRequest;
5050
import software.amazon.cloudformation.proxy.hook.HookProgressEvent;
5151
import software.amazon.cloudformation.proxy.hook.HookStatus;
52+
import software.amazon.cloudformation.proxy.hook.targetmodel.ChangedResource;
53+
import software.amazon.cloudformation.proxy.hook.targetmodel.StackHookTargetModel;
5254
import software.amazon.cloudformation.resource.SchemaValidator;
5355
import software.amazon.cloudformation.resource.Serializer;
5456

@@ -168,7 +170,6 @@ public void invokeHandler_CompleteSynchronously_returnsSuccess(final String requ
168170

169171
// assert handler receives correct injections
170172
assertThat(wrapper.awsClientProxy).isNotNull();
171-
assertThat(wrapper.getRequest()).isEqualTo(hookHandlerRequest);
172173
assertThat(wrapper.invocationPoint).isEqualTo(invocationPoint);
173174
assertThat(wrapper.callbackContext).isNull();
174175
}
@@ -205,7 +206,6 @@ public void invokeHandler_WithResourceProperties_returnsSuccess(final String req
205206

206207
// assert handler receives correct injections
207208
assertThat(wrapper.awsClientProxy).isNotNull();
208-
assertThat(wrapper.getRequest()).isEqualTo(hookHandlerRequest);
209209
assertThat(wrapper.invocationPoint).isEqualTo(invocationPoint);
210210
assertThat(wrapper.callbackContext).isNull();
211211
}
@@ -242,7 +242,6 @@ public void invokeHandler_WithResourcePropertiesAndExtraneousFields_returnsSucce
242242

243243
// assert handler receives correct injections
244244
assertThat(wrapper.awsClientProxy).isNotNull();
245-
assertThat(wrapper.getRequest()).isEqualTo(hookHandlerRequest);
246245
assertThat(wrapper.invocationPoint).isEqualTo(invocationPoint);
247246
assertThat(wrapper.callbackContext).isNull();
248247
}
@@ -279,7 +278,6 @@ public void invokeHandler_StrictDeserializer_WithResourceProperties_returnsSucce
279278

280279
// assert handler receives correct injections
281280
assertThat(wrapperStrictDeserialize.awsClientProxy).isNotNull();
282-
assertThat(wrapperStrictDeserialize.getRequest()).isEqualTo(hookHandlerRequest);
283281
assertThat(wrapperStrictDeserialize.invocationPoint).isEqualTo(invocationPoint);
284282
assertThat(wrapperStrictDeserialize.callbackContext).isNull();
285283
}
@@ -323,6 +321,54 @@ public void invokeHandler_StrictDeserializer_WithResourceProperties_returnsSucce
323321
}
324322
}
325323

324+
@ParameterizedTest
325+
@CsvSource({ "preCreate.request.with-stack-level-hook.json,CREATE_PRE_PROVISION" })
326+
public void invokeHandler_WithStackLevelHook_returnsSuccess(final String requestDataPath, final String invocationPointString)
327+
throws IOException {
328+
final HookInvocationPoint invocationPoint = HookInvocationPoint.valueOf(invocationPointString);
329+
330+
final ProgressEvent<TestModel,
331+
TestContext> pe = ProgressEvent.<TestModel, TestContext>builder().status(OperationStatus.SUCCESS).build();
332+
wrapper.setInvokeHandlerResponse(pe);
333+
334+
lenient().when(cipher.decryptCredentials(any())).thenReturn(new Credentials("123", "123", "123"));
335+
336+
try (final InputStream in = loadRequestStream(requestDataPath); final OutputStream out = new ByteArrayOutputStream()) {
337+
final Context context = getLambdaContext();
338+
339+
wrapper.handleRequest(in, out, context);
340+
341+
// verify initialiseRuntime was called and initialised dependencies
342+
verifyInitialiseRuntime();
343+
344+
// verify output response
345+
verifyHandlerResponse(out,
346+
HookProgressEvent.<TestContext>builder().clientRequestToken("123456").hookStatus(HookStatus.SUCCESS).build());
347+
348+
// assert handler receives correct injections
349+
assertThat(wrapper.awsClientProxy).isNotNull();
350+
assertThat(wrapper.invocationPoint).isEqualTo(invocationPoint);
351+
assertThat(wrapper.callbackContext).isNull();
352+
353+
assertThat(wrapper.getRequest().getHookContext().getTargetType()).isEqualTo("STACK");
354+
assertThat(wrapper.getRequest().getHookContext().getTargetName()).isEqualTo("STACK");
355+
assertThat(wrapper.getRequest().getHookContext().getTargetLogicalId()).isEqualTo("myStack");
356+
357+
StackHookTargetModel stackHookTargetModel = wrapper.getRequest().getHookContext()
358+
.getTargetModel(StackHookTargetModel.class);
359+
assertThat(stackHookTargetModel.getTemplate()).isEqualTo("template string here");
360+
assertThat(stackHookTargetModel.getPreviousTemplate()).isEqualTo("previous template string here");
361+
assertThat(stackHookTargetModel.getResolvedTemplate()).isEqualTo("resolved template string here");
362+
assertThat(stackHookTargetModel.getChangedResources().size()).isEqualTo(1);
363+
364+
ChangedResource expectedChangedResource = ChangedResource.builder().logicalResourceId("SomeLogicalResourceId")
365+
.resourceType("AWS::S3::Bucket").lineNumber(3).action("CREATE")
366+
.resourceProperties("<Resource Properties as json string>")
367+
.previousResourceProperties("<Resource Properties as json string>").build();
368+
assertThat(stackHookTargetModel.getChangedResources().get(0)).isEqualTo(expectedChangedResource);
369+
}
370+
}
371+
326372
private final String expectedStringWhenStrictDeserializingWithExtraneousFields = "Unrecognized field \"targetName\" (class software.amazon.cloudformation.proxy.hook.HookInvocationRequest), not marked as ignorable (10 known properties: \"requestContext\", \"stackId\", \"clientRequestToken\", \"hookModel\", \"hookTypeName\", \"requestData\", \"actionInvocationPoint\", \"awsAccountId\", \"changeSetId\", \"hookTypeVersion\"])\n"
327373
+ " at [Source: (String)\"{\n" + " \"clientRequestToken\": \"123456\",\n" + " \"awsAccountId\": \"123456789012\",\n"
328374
+ " \"stackId\": \"arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack/e722ae60-fe62-11e8-9a0e-0ae8cc519968\",\n"
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"clientRequestToken": "123456",
3+
"awsAccountId": "123456789012",
4+
"stackId": "arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack/e722ae60-fe62-11e8-9a0e-0ae8cc519968",
5+
"changeSetId": "arn:aws:cloudformation:us-east-1:123456789012:changeSet/SampleChangeSet-conditional/1a2345b6-0000-00a0-a123-00abc0abc000",
6+
"hookTypeName": "AWS::Test::TestModel",
7+
"hookTypeVersion": "1.0",
8+
"hookModel": {
9+
"property1": "abc",
10+
"property2": 123
11+
},
12+
"actionInvocationPoint": "CREATE_PRE_PROVISION",
13+
"requestData": {
14+
"targetName": "STACK",
15+
"targetType": "STACK",
16+
"targetLogicalId": "myStack",
17+
"targetModel": {
18+
"template": "template string here",
19+
"previousTemplate": "previous template string here",
20+
"resolvedTemplate": "resolved template string here",
21+
"changedResources": [
22+
{
23+
"LogicalResourceId": "SomeLogicalResourceId",
24+
"ResourceType": "AWS::S3::Bucket",
25+
"LineNumber": 3,
26+
"Action": "CREATE",
27+
"ResourceProperties": "<Resource Properties as json string>",
28+
"PreviousResourceProperties": "<Resource Properties as json string>"
29+
}
30+
]
31+
},
32+
"callerCredentials": "callerCredentials",
33+
"providerCredentials": "providerCredentials",
34+
"providerLogGroupName": "providerLoggingGroupName",
35+
"hookEncryptionKeyArn": "hookEncryptionKeyArn",
36+
"hookEncryptionKeyRole": "hookEncryptionKeyRole"
37+
},
38+
"requestContext": {}
39+
}

0 commit comments

Comments
 (0)