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
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