Skip to content

Brianlao public fork master #445

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
17ab056
Add StackHookTargetModel for support of stack-level hooks
brianlaoaws Feb 6, 2024
dd20afb
Add support to download hook target data for stack-level hooks
brianlaoaws Feb 29, 2024
2e26056
Add support to download hook target data for stack-level hooks
brianlaoaws Feb 29, 2024
13b5137
Add support to download hook target data for stack-level hooks
brianlaoaws Feb 29, 2024
29c1982
Merge pull request #1 from brianlaoaws/feature/download-payload
brianlaoaws Mar 4, 2024
f3d9632
Merge branch 'master' of https://github.com/aws-cloudformation/cloudf…
brianlaoaws Apr 4, 2024
73822f5
Skip stack level hook for stack if prior stack level change set hook …
brianlaoaws Apr 4, 2024
e341c99
Skip stack level hook for stack if prior stack level change set hook …
brianlaoaws Apr 4, 2024
a2040f3
Merge pull request #2 from brianlaoaws/feature/new-operation-status-s…
brianlaoaws Apr 4, 2024
f88e5de
fix method
brianlaoaws Jun 28, 2024
c8c005f
Merge pull request #3 from brianlaoaws/feature/fix-method-is-remote
brianlaoaws Jun 28, 2024
c73d8d3
Fix resource targetting for a stack level hook
brianlaoaws Sep 17, 2024
a74a040
Merge pull request #4 from brianlaoaws/master-from-brianlao-github-fort
brianlaoaws Sep 17, 2024
e74adc2
Fix resource targetting for a stack level hook
brianlaoaws Sep 18, 2024
cfdb66b
Merge pull request #5 from brianlaoaws/master-from-brianlao-github-fort
brianlaoaws Sep 18, 2024
fccc46d
bump version
brianlaoaws Nov 25, 2024
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
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>software.amazon.cloudformation</groupId>
<artifactId>aws-cloudformation-rpdk-java-plugin</artifactId>
<version>2.1.1</version>
<version>2.2.1</version>
<name>AWS CloudFormation RPDK Java Plugin</name>
<description>The CloudFormation Resource Provider Development Kit (RPDK) allows you to author your own resource providers that can be used by CloudFormation. This plugin library helps to provide runtime bindings for the execution of your providers by CloudFormation.
</description>
Expand Down Expand Up @@ -491,7 +491,7 @@
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.13</version>
<version>1.6.8</version>
<extensions>true</extensions>
<configuration>
<serverId>sonatype-nexus-staging</serverId>
Expand Down
2 changes: 1 addition & 1 deletion python/rpdk/java/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import logging

__version__ = "2.1.1"
__version__ = "2.2.1"

logging.getLogger(__name__).addHandler(logging.NullHandler())
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,18 @@
import com.amazonaws.AmazonServiceException;
import com.amazonaws.retry.RetryUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.annotations.VisibleForTesting;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
Expand All @@ -31,10 +37,15 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.awscore.exception.AwsServiceException;
import software.amazon.awssdk.http.HttpExecuteRequest;
import software.amazon.awssdk.http.HttpExecuteResponse;
import software.amazon.awssdk.http.HttpStatusCode;
import software.amazon.awssdk.http.HttpStatusFamily;
import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.utils.IoUtils;
import software.amazon.cloudformation.encryption.Cipher;
import software.amazon.cloudformation.encryption.KMSCipher;
import software.amazon.cloudformation.exceptions.BaseHandlerException;
Expand Down Expand Up @@ -63,6 +74,7 @@
import software.amazon.cloudformation.proxy.hook.HookInvocationRequest;
import software.amazon.cloudformation.proxy.hook.HookProgressEvent;
import software.amazon.cloudformation.proxy.hook.HookRequestContext;
import software.amazon.cloudformation.proxy.hook.HookRequestData;
import software.amazon.cloudformation.proxy.hook.HookStatus;
import software.amazon.cloudformation.resource.SchemaValidator;
import software.amazon.cloudformation.resource.Serializer;
Expand All @@ -89,6 +101,9 @@ public abstract class HookAbstractWrapper<TargetT, CallbackT, ConfigurationT> {
final SchemaValidator validator;
final TypeReference<HookInvocationRequest<ConfigurationT, CallbackT>> typeReference;

final TypeReference<Map<String, Object>> hookStackPayloadS3TypeReference = new TypeReference<>() {
};

private MetricsPublisher providerMetricsPublisher;

private CloudWatchLogHelper cloudWatchLogHelper;
Expand Down Expand Up @@ -222,18 +237,20 @@ private ProgressEvent<TargetT, CallbackT> processInvocation(final JSONObject raw

assert request != null : "Invalid request object received. Request object is null";

if (request.getRequestData() == null || request.getRequestData().getTargetModel() == null) {
throw new TerminalException("Invalid request object received. Target Model can not be null.");
}

// TODO: Include hook schema validation here after schema is finalized
boolean isPayloadRemote = isHookInvocationPayloadRemote(request.getRequestData());

try {
// initialise dependencies with platform credentials
initialiseRuntime(request.getHookTypeName(), request.getRequestData().getProviderCredentials(),
request.getRequestData().getProviderLogGroupName(), request.getAwsAccountId(),
request.getRequestData().getHookEncryptionKeyArn(), request.getRequestData().getHookEncryptionKeyRole());

if (isPayloadRemote) {
Map<String, Object> targetModelData = retrieveHookInvocationPayloadFromS3(request.getRequestData().getPayload());

request.getRequestData().setTargetModel(targetModelData);
}

// transform the request object to pass to caller
HookHandlerRequest hookHandlerRequest = transform(request);
ConfigurationT typeConfiguration = request.getHookModel();
Expand Down Expand Up @@ -366,6 +383,50 @@ private void writeResponse(final OutputStream outputStream, final HookProgressEv
outputStream.flush();
}

public Map<String, Object> retrieveHookInvocationPayloadFromS3(final String s3PresignedUrl) {
if (s3PresignedUrl != null) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

try {
URL presignedUrl = new URL(s3PresignedUrl);
SdkHttpRequest httpRequest = SdkHttpRequest.builder().method(SdkHttpMethod.GET).uri(presignedUrl.toURI()).build();

HttpExecuteRequest executeRequest = HttpExecuteRequest.builder().request(httpRequest).build();

HttpExecuteResponse response = HTTP_CLIENT.prepareRequest(executeRequest).call();

response.responseBody().ifPresentOrElse(abortableInputStream -> {
try {
IoUtils.copy(abortableInputStream, byteArrayOutputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
}, () -> loggerProxy.log("Hook invocation payload is empty."));

String str = byteArrayOutputStream.toString(StandardCharsets.UTF_8);

return this.serializer.deserialize(str, hookStackPayloadS3TypeReference);
} catch (RuntimeException | IOException | URISyntaxException exp) {
loggerProxy.log("Failed to retrieve hook invocation payload" + exp.toString());
}
}
return Collections.emptyMap();
}

@VisibleForTesting
protected boolean isHookInvocationPayloadRemote(HookRequestData hookRequestData) {
if (hookRequestData == null) {
throw new TerminalException("Invalid request object received. Target Model can not be null.");
}

if ((hookRequestData.getTargetModel() == null || hookRequestData.getTargetModel().isEmpty())
&& hookRequestData.getPayload() == null) {
throw new TerminalException("No payload data set.");
}

return (hookRequestData.getTargetModel() == null || hookRequestData.getTargetModel().isEmpty());
}

/**
* Transforms the incoming request to the subset of typed models which the
* handler implementor needs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ public enum OperationStatus {
PENDING,
IN_PROGRESS,
SUCCESS,
CHANGE_SET_SUCCESS_SKIP_STACK_HOOK,
FAILED
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class HookRequestData {
private String targetType;
private String targetLogicalId;
private Map<String, Object> targetModel;
private String payload;
private String callerCredentials;
private String providerCredentials;
private String providerLogGroupName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ public enum HookStatus {
PENDING,
IN_PROGRESS,
SUCCESS,
CHANGE_SET_SUCCESS_SKIP_STACK_HOOK,
FAILED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package software.amazon.cloudformation.proxy.hook.targetmodel;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ChangedResource {
@JsonProperty("LogicalResourceId")
private String logicalResourceId;

@JsonProperty("ResourceType")
private String resourceType;

@JsonProperty("LineNumber")
private Integer lineNumber;

@JsonProperty("Action")
private String action;

@JsonProperty("ResourceProperties")
private String resourceProperties;

@JsonProperty("PreviousResourceProperties")
private String previousResourceProperties;
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,17 @@ public enum HookTargetType {
* A target model meant to represent a target for a Resource Hook. This model
* type will have properties specific to the resource type.
*/
RESOURCE;
RESOURCE,

/**
* A target model meant to represent a target for a Stack Hook. This model type
* will have properties specific to the stack type.
*/
STACK,

/**
* A target model meant to represent a target for a stack Change Set Hook. This
* model type will have properties specific to the change set type.
*/
CHANGE_SET;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package software.amazon.cloudformation.proxy.hook.targetmodel;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.util.List;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

@EqualsAndHashCode(callSuper = false)
@Getter
@NoArgsConstructor
@ToString
@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
@JsonDeserialize(as = StackHookTargetModel.class)
public class StackHookTargetModel extends HookTargetModel {
private static final TypeReference<StackHookTargetModel> MODEL_REFERENCE = new TypeReference<StackHookTargetModel>() {
};

@JsonProperty("Template")
private Object template;

@JsonProperty("PreviousTemplate")
private Object previousTemplate;

@JsonProperty("ResolvedTemplate")
private Object resolvedTemplate;

@JsonProperty("ChangedResources")
private List<ChangedResource> changedResources;

@Override
public TypeReference<? extends HookTarget> getHookTargetTypeReference() {
return null;
}

@Override
public TypeReference<? extends HookTargetModel> getTargetModelTypeReference() {
return MODEL_REFERENCE;
}

@Override
public final HookTargetType getHookTargetType() {
return HookTargetType.STACK;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import lombok.Data;
import lombok.EqualsAndHashCode;
Expand All @@ -32,8 +33,10 @@
import software.amazon.cloudformation.metrics.MetricsPublisher;
import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
import software.amazon.cloudformation.proxy.ProgressEvent;
import software.amazon.cloudformation.proxy.hook.HookContext;
import software.amazon.cloudformation.proxy.hook.HookHandlerRequest;
import software.amazon.cloudformation.proxy.hook.HookInvocationRequest;
import software.amazon.cloudformation.proxy.hook.targetmodel.HookTargetModel;
import software.amazon.cloudformation.resource.SchemaValidator;
import software.amazon.cloudformation.resource.Serializer;

Expand All @@ -44,6 +47,8 @@
@EqualsAndHashCode(callSuper = true)
public class HookLambdaWrapperOverride extends HookLambdaWrapper<TestModel, TestContext, TestConfigurationModel> {

private Map<String, Object> hookInvocationPayloadFromS3;

/**
* This .ctor provided for testing
*/
Expand Down Expand Up @@ -112,11 +117,29 @@ public void enqueueResponses(final List<ProgressEvent<TestModel, TestContext>> r

@Override
protected HookHandlerRequest transform(final HookInvocationRequest<TestConfigurationModel, TestContext> request) {
return transformResponse;
this.request = HookHandlerRequest.builder().clientRequestToken(request.getClientRequestToken())
.hookContext(HookContext.builder().awsAccountId(request.getAwsAccountId()).stackId(request.getStackId())
.changeSetId(request.getChangeSetId()).hookTypeName(request.getHookTypeName())
.hookTypeVersion(request.getHookTypeVersion()).invocationPoint(request.getActionInvocationPoint())
.targetName(request.getRequestData().getTargetName()).targetType(request.getRequestData().getTargetType())
.targetLogicalId(request.getRequestData().getTargetLogicalId())
.targetModel(HookTargetModel.of(request.getRequestData().getTargetModel())).build())
.build();

return this.request;
}

public HookHandlerRequest transformResponse;

@Override
public Map<String, Object> retrieveHookInvocationPayloadFromS3(final String s3PresignedUrl) {
return hookInvocationPayloadFromS3;
}

public void setHookInvocationPayloadFromS3(Map<String, Object> input) {
hookInvocationPayloadFromS3 = input;
}

@Override
protected TypeReference<HookInvocationRequest<TestConfigurationModel, TestContext>> getTypeReference() {
return new TypeReference<HookInvocationRequest<TestConfigurationModel, TestContext>>() {
Expand Down
Loading
Loading