From 3d1871a07774ae7883fd5667ccbe289b0bf8224a Mon Sep 17 00:00:00 2001 From: Alex Olshanskyy Date: Fri, 5 May 2023 13:14:58 -0700 Subject: [PATCH] Updated Canary handlers --- .../aws-synthetics-canary.json | 15 +- aws-synthetics-canary/resource-role.yaml | 7 + .../synthetics/canary/CanaryHelper.java | 9 + .../synthetics/canary/CreateHandler.java | 4 +- .../amazon/synthetics/canary/ModelHelper.java | 16 +- .../synthetics/canary/UpdateHandler.java | 30 +- .../synthetics/canary/CreateHandlerTest.java | 458 +++--------------- .../synthetics/canary/DeleteHandlerTest.java | 18 +- .../synthetics/canary/ListHandlerTest.java | 36 +- .../synthetics/canary/ReadHandlerTest.java | 89 ++-- .../amazon/synthetics/canary/TestBase.java | 300 ++++++++++-- .../synthetics/canary/UpdateHandlerTest.java | 55 ++- 12 files changed, 543 insertions(+), 494 deletions(-) diff --git a/aws-synthetics-canary/aws-synthetics-canary.json b/aws-synthetics-canary/aws-synthetics-canary.json index cb5f942..6e4667b 100644 --- a/aws-synthetics-canary/aws-synthetics-canary.json +++ b/aws-synthetics-canary/aws-synthetics-canary.json @@ -109,6 +109,9 @@ }, "Handler": { "type": "string" + }, + "SourceLocationArn": { + "type": "string" } }, "required": [ @@ -275,8 +278,7 @@ "ArtifactS3Location", "ExecutionRoleArn", "Schedule", - "RuntimeVersion", - "StartCanaryAfterCreation" + "RuntimeVersion" ], "handlers": { "create": { @@ -349,6 +351,13 @@ "/properties/Code/S3Bucket", "/properties/Code/S3Key", "/properties/Code/S3ObjectVersion", - "/properties/Code/Script" + "/properties/Code/Script", + "/properties/DeleteLambdaResourcesOnCanaryDeletion", + "/properties/StartCanaryAfterCreation", + "/properties/RunConfig/EnvironmentVariables", + "/properties/VisualReference" + ], + "deprecatedProperties": [ + "/properties/DeleteLambdaResourcesOnCanaryDeletion" ] } diff --git a/aws-synthetics-canary/resource-role.yaml b/aws-synthetics-canary/resource-role.yaml index 0edf901..5fe62ae 100644 --- a/aws-synthetics-canary/resource-role.yaml +++ b/aws-synthetics-canary/resource-role.yaml @@ -15,6 +15,13 @@ Resources: Principal: Service: resources.cloudformation.amazonaws.com Action: sts:AssumeRole + Condition: + StringEquals: + aws:SourceAccount: + Ref: AWS::AccountId + StringLike: + aws:SourceArn: + Fn::Sub: arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:type/resource/AWS-Synthetics-Canary/* Path: "/" Policies: - PolicyName: ResourceTypePolicy diff --git a/aws-synthetics-canary/src/main/java/com/amazon/synthetics/canary/CanaryHelper.java b/aws-synthetics-canary/src/main/java/com/amazon/synthetics/canary/CanaryHelper.java index 15b3055..de91e6c 100644 --- a/aws-synthetics-canary/src/main/java/com/amazon/synthetics/canary/CanaryHelper.java +++ b/aws-synthetics-canary/src/main/java/com/amazon/synthetics/canary/CanaryHelper.java @@ -2,6 +2,7 @@ import com.google.common.base.Strings; import software.amazon.awssdk.services.synthetics.SyntheticsClient; +import software.amazon.awssdk.services.synthetics.model.ArtifactConfigOutput; import software.amazon.awssdk.services.synthetics.model.Canary; import software.amazon.awssdk.services.synthetics.model.CanaryRunConfigOutput; import software.amazon.awssdk.services.synthetics.model.GetCanaryRequest; @@ -39,6 +40,14 @@ public static boolean isNullOrEmpty(CanaryRunConfigOutput runConfigOutput) { && runConfigOutput.memoryInMB() == null; } + public static boolean isNullOrEmpty(ArtifactConfigOutput artifactConfigOutput) { + if (artifactConfigOutput == null || artifactConfigOutput.s3Encryption() == null) { + return true; + } + + return artifactConfigOutput.s3Encryption().encryptionMode() == null; + } + public static Canary getCanaryOrNull(AmazonWebServicesClientProxy proxy, SyntheticsClient syntheticsClient, String canaryName) { diff --git a/aws-synthetics-canary/src/main/java/com/amazon/synthetics/canary/CreateHandler.java b/aws-synthetics-canary/src/main/java/com/amazon/synthetics/canary/CreateHandler.java index 965c78f..41d2916 100644 --- a/aws-synthetics-canary/src/main/java/com/amazon/synthetics/canary/CreateHandler.java +++ b/aws-synthetics-canary/src/main/java/com/amazon/synthetics/canary/CreateHandler.java @@ -60,7 +60,7 @@ protected ProgressEvent handleRequest() { private ProgressEvent handleCanaryInStateReady(Canary canary) { log("Canary is in state READY."); - if (model.getStartCanaryAfterCreation()) { + if (model.getStartCanaryAfterCreation() != null && model.getStartCanaryAfterCreation()) { // There is a race condition here. We will get an exception if someone calls // DeleteCanary, StartCanary, or UpdateCanary before we call StartCanary. @@ -80,7 +80,7 @@ private ProgressEvent handleCanaryInStateStartin // If the customer calls StartCanary before we handle the canary in READY state, // then we can end up here even when StartCanaryAfterCreation is false. - if (model.getStartCanaryAfterCreation()) { + if (model.getStartCanaryAfterCreation() != null && model.getStartCanaryAfterCreation()) { return waitingForCanaryStateTransition( "Starting canary", "Canary is in state STARTING.", diff --git a/aws-synthetics-canary/src/main/java/com/amazon/synthetics/canary/ModelHelper.java b/aws-synthetics-canary/src/main/java/com/amazon/synthetics/canary/ModelHelper.java index 7190d0f..2f920e6 100644 --- a/aws-synthetics-canary/src/main/java/com/amazon/synthetics/canary/ModelHelper.java +++ b/aws-synthetics-canary/src/main/java/com/amazon/synthetics/canary/ModelHelper.java @@ -34,12 +34,11 @@ public class ModelHelper { // Python Runtime private static final String PYTHON_DIR = "/python/"; private static final String PY_SUFFIX = ".py"; - private static final String PYTHON_PATTERN = "^syn-python-*"; + private static final String PYTHON_PATTERN = "^syn-python-.*"; private static final Pattern python_pattern = Pattern.compile(PYTHON_PATTERN); public static ResourceModel constructModel(Canary canary, ResourceModel model) { Map tags = canary.tags(); - model.setId(canary.id()); model.setName(canary.name()); model.setArtifactS3Location("s3://" + canary.artifactS3Location()); @@ -48,7 +47,6 @@ public static ResourceModel constructModel(Canary canary, ResourceModel model) { model.setSuccessRetentionPeriod(canary.successRetentionPeriodInDays()); model.setRuntimeVersion(canary.runtimeVersion()); model.setState(canary.status().stateAsString()); - model.setCode(buildCodeObject(canary.code())); model.setSchedule(buildCanaryScheduleObject(canary.schedule())); // Tags are optional. Check for null @@ -67,12 +65,23 @@ public static ResourceModel constructModel(Canary canary, ResourceModel model) { .build()); } + if (!CanaryHelper.isNullOrEmpty(canary.artifactConfig())) { + model.setArtifactConfig(ArtifactConfig.builder() + .s3Encryption( + S3Encryption.builder() + .encryptionMode(canary.artifactConfig().s3Encryption().encryptionModeAsString()) + .kmsKeyArn(canary.artifactConfig().s3Encryption().kmsKeyArn()) + .build()) + .build()); + } + return model; } private static Code buildCodeObject(CanaryCodeOutput canaryCodeOutput) { return Code.builder() .handler(canaryCodeOutput.handler()) + .sourceLocationArn(canaryCodeOutput.sourceLocationArn()) .build(); } @@ -172,6 +181,7 @@ public static String buildCanaryArn(ResourceHandlerRequest reques public static Map buildTagInputMap(ResourceModel model) { Map tagMap = new HashMap<>(); List tagList = model.getTags(); + // return null if no Tag specified. if (tagList == null) return null; diff --git a/aws-synthetics-canary/src/main/java/com/amazon/synthetics/canary/UpdateHandler.java b/aws-synthetics-canary/src/main/java/com/amazon/synthetics/canary/UpdateHandler.java index f8d7b23..a22f18a 100644 --- a/aws-synthetics-canary/src/main/java/com/amazon/synthetics/canary/UpdateHandler.java +++ b/aws-synthetics-canary/src/main/java/com/amazon/synthetics/canary/UpdateHandler.java @@ -2,10 +2,28 @@ import com.google.common.base.Strings; import java.util.Objects; -import software.amazon.awssdk.services.synthetics.model.*; + +import software.amazon.awssdk.services.synthetics.model.ArtifactConfigInput; +import software.amazon.awssdk.services.synthetics.model.Canary; +import software.amazon.awssdk.services.synthetics.model.CanaryCodeInput; +import software.amazon.awssdk.services.synthetics.model.CanaryRunConfigInput; +import software.amazon.awssdk.services.synthetics.model.CanaryScheduleInput; +import software.amazon.awssdk.services.synthetics.model.CanaryState; +import software.amazon.awssdk.services.synthetics.model.StartCanaryRequest; +import software.amazon.awssdk.services.synthetics.model.StopCanaryRequest; +import software.amazon.awssdk.services.synthetics.model.TagResourceRequest; +import software.amazon.awssdk.services.synthetics.model.UntagResourceRequest; +import software.amazon.awssdk.services.synthetics.model.UpdateCanaryRequest; +import software.amazon.awssdk.services.synthetics.model.ValidationException; +import software.amazon.awssdk.services.synthetics.model.VisualReferenceInput; +import software.amazon.awssdk.services.synthetics.model.VpcConfigInput; import software.amazon.cloudformation.Action; -import software.amazon.cloudformation.exceptions.*; -import software.amazon.cloudformation.proxy.*; +import software.amazon.cloudformation.exceptions.CfnGeneralServiceException; +import software.amazon.cloudformation.exceptions.CfnInvalidRequestException; +import software.amazon.cloudformation.proxy.HandlerErrorCode; +import software.amazon.cloudformation.proxy.OperationStatus; +import software.amazon.cloudformation.proxy.ProgressEvent; + import java.util.Collections; import java.util.Map; @@ -97,7 +115,7 @@ private ProgressEvent handleCanaryInStateReadyOr canary.status().stateReason()); } - if (model.getStartCanaryAfterCreation()) { + if (model.getStartCanaryAfterCreation() != null && model.getStartCanaryAfterCreation()) { // There is a race condition here. We will get an exception if someone calls // DeleteCanary, StartCanary, or UpdateCanary before we call StartCanary. @@ -117,7 +135,7 @@ private ProgressEvent handleCanaryInStateStartin // If the customer calls StartCanary before we handle the canary in READY or // STOPPED state, then we can end up here even when StartCanaryAfterCreation is false. - if (model.getStartCanaryAfterCreation()) { + if (model.getStartCanaryAfterCreation() != null && model.getStartCanaryAfterCreation()) { return waitingForCanaryStateTransition( "Starting canary", "Canary is in state STARTING.", @@ -147,7 +165,7 @@ private ProgressEvent handleCanaryInStateRunning // If the canary was initially in state RUNNING and StartCanaryAfterCreation is // false, we should stop the canary. - if (!model.getStartCanaryAfterCreation()) { + if (model.getStartCanaryAfterCreation() == null || !model.getStartCanaryAfterCreation()) { // There is a race condition here. We will get an exception if someone calls // DeleteCanary, StopCanary, or UpdateCanary before we call StopCanary. proxy.injectCredentialsAndInvokeV2( diff --git a/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/CreateHandlerTest.java b/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/CreateHandlerTest.java index 24792df..f01aa22 100644 --- a/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/CreateHandlerTest.java +++ b/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/CreateHandlerTest.java @@ -1,12 +1,14 @@ package com.amazon.synthetics.canary; -import org.apache.commons.collections.map.SingletonMap; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; import software.amazon.awssdk.services.synthetics.model.*; -import software.amazon.cloudformation.proxy.*; - -import java.util.HashMap; -import java.util.Map; +import software.amazon.cloudformation.proxy.HandlerErrorCode; +import software.amazon.cloudformation.proxy.OperationStatus; +import software.amazon.cloudformation.proxy.ProgressEvent; +import software.amazon.cloudformation.proxy.ResourceHandlerRequest; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -14,6 +16,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; + public class CreateHandlerTest extends TestBase { private CreateHandler handler = new CreateHandler(); @@ -37,6 +40,7 @@ public void handleRequest_inProgress_canaryStateIsCreating_returnsInProgress() { assertThat(response.getStatus()).isEqualTo(OperationStatus.IN_PROGRESS); assertThat(response.getMessage()).isNotNull(); assertThat(response.getResourceModel()).isNotNull(); + verify(proxy).injectCredentialsAndInvokeV2(eq(GetCanaryRequest.builder().name(CANARY_NAME).build()), any()); } @Test @@ -50,17 +54,21 @@ public void handleRequest_inProgress_canaryStateIsError_fails() { assertThat(response.getMessage()).isNotNull(); assertThat(response.getResourceModel()).isNotNull(); assertThat(response.getErrorCode()).isEqualTo(HandlerErrorCode.GeneralServiceException); + verify(proxy).injectCredentialsAndInvokeV2(eq(GetCanaryRequest.builder().name(CANARY_NAME).build()), any()); } - @Test - public void handleRequest_inProgress_canaryStateIsReady_startCanaryAfterCreationIsFalse_returnsSuccess() { + @ParameterizedTest + @NullSource + @ValueSource(booleans = {false}) + public void handleRequest_inProgress_canaryStateIsReady_startCanaryAfterCreationIsFalseOrNull_returnsSuccess(Boolean startAfterCreate) { configureGetCanaryResponse(CanaryState.READY); - + ResourceHandlerRequest request = startAfterCreate == null ? REQUEST_NULL_START_CANARY : REQUEST; ProgressEvent response = handler.handleRequest( - proxy, REQUEST, CallbackContext.builder().canaryCreateStarted(true).build(), logger); + proxy, request, CallbackContext.builder().canaryCreateStarted(true).build(), logger); assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS); assertThat(response.getResourceModel()).isNotNull(); + verify(proxy).injectCredentialsAndInvokeV2(eq(GetCanaryRequest.builder().name(CANARY_NAME).build()), any()); } @Test @@ -74,18 +82,22 @@ public void handleRequest_inProgress_canaryStateIsReady_startCanaryAfterCreation assertThat(response.getMessage()).isNotNull(); assertThat(response.getResourceModel()).isNotNull(); + verify(proxy).injectCredentialsAndInvokeV2(eq(GetCanaryRequest.builder().name(CANARY_NAME).build()), any()); verify(proxy).injectCredentialsAndInvokeV2(eq(StartCanaryRequest.builder().name(CANARY_NAME).build()), any()); } - @Test - public void handleRequest_inProgress_canaryStateIsStarting_startCanaryAfterCreationIsFalse_returnsSuccess() { + @ParameterizedTest + @NullSource + @ValueSource(booleans = {false}) + public void handleRequest_inProgress_canaryStateIsStarting_startCanaryAfterCreationIsFalseOrNull_returnsSuccess(Boolean value) { configureGetCanaryResponse(CanaryState.STARTING); - + ResourceHandlerRequest request = value == null ? REQUEST_NULL_START_CANARY : REQUEST; ProgressEvent response = handler.handleRequest( - proxy, REQUEST, CallbackContext.builder().canaryCreateStarted(true).build(), logger); + proxy, request, CallbackContext.builder().canaryCreateStarted(true).build(), logger); assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS); assertThat(response.getResourceModel()).isNotNull(); + verify(proxy).injectCredentialsAndInvokeV2(eq(GetCanaryRequest.builder().name(CANARY_NAME).build()), any()); } @Test @@ -98,14 +110,16 @@ public void handleRequest_inProgress_canaryStateIsStarting_startCanaryAfterCreat assertThat(response.getStatus()).isEqualTo(OperationStatus.IN_PROGRESS); assertThat(response.getMessage()).isNotNull(); assertThat(response.getResourceModel()).isNotNull(); + verify(proxy).injectCredentialsAndInvokeV2(eq(GetCanaryRequest.builder().name(CANARY_NAME).build()), any()); } @Test public void handleRequest_SimpleSuccess() { + ResourceModel model = buildModel(false); final ResourceHandlerRequest request = ResourceHandlerRequest.builder() - .desiredResourceState(buildModel()) + .desiredResourceState(model) .build(); final CallbackContext callbackContext = CallbackContext.builder() @@ -113,25 +127,20 @@ public void handleRequest_SimpleSuccess() { .build(); final Canary canary = Canary.builder() - .name("canarytestname") + .name(CANARY_NAME) .code(codeOutputObjectForTesting()) .status(CanaryStatus.builder() .state("RUNNING") .build()) .schedule(canaryScheduleOutputForTesting()) - .runConfig(CanaryRunConfigOutput.builder().timeoutInSeconds(60).build()) .build(); - final GetCanaryResponse getCanaryResponse = GetCanaryResponse.builder() .canary(canary) .build(); - final TagResourceResponse tagResourceResponse = TagResourceResponse.builder().build(); - - doReturn(getCanaryResponse, - tagResourceResponse) + doReturn(getCanaryResponse) .when(proxy) - .injectCredentialsAndInvokeV2(any(), any()); + .injectCredentialsAndInvokeV2(eq(GetCanaryRequest.builder().name(CANARY_NAME).build()), any()); final ProgressEvent response = handler.handleRequest(proxy, request, callbackContext, logger); @@ -139,50 +148,34 @@ public void handleRequest_SimpleSuccess() { assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS); assertThat(response.getCallbackContext()).isNull(); assertThat(response.getCallbackDelaySeconds()).isEqualTo(0); - assertThat(response.getResourceModel()).isEqualTo(request.getDesiredResourceState()); + assertThat(response.getResourceModel().getState()).isEqualTo("RUNNING"); assertThat(response.getResourceModels()).isNull(); assertThat(response.getMessage()).isNull(); assertThat(response.getErrorCode()).isNull(); + verify(proxy).injectCredentialsAndInvokeV2(eq(GetCanaryRequest.builder().name(CANARY_NAME).build()), any()); } @Test - public void handleRequest_SimpleSuccessRemovingOptionalValues() { + public void handleRequest_createCanary_Basic() { ResourceModel model = buildModel(false); + ResourceModel modelClone = buildModel(false); final ResourceHandlerRequest request = ResourceHandlerRequest.builder() .desiredResourceState(model) .build(); - CanaryRunConfigOutput outputExpected = CanaryRunConfigOutput.builder() - .timeoutInSeconds(60) - .memoryInMB(1024) - .activeTracing(false) - .build(); - final Canary canary = Canary.builder() - .name("canarytestname") - .code(codeOutputObjectForTesting()) - .status(CanaryStatus.builder() - .state("RUNNING") - .build()) - .schedule(canaryScheduleOutputForTesting()) - .runConfig(outputExpected) + .name(CANARY_NAME) .build(); final CreateCanaryResponse createCanaryResponse = CreateCanaryResponse.builder() .canary(canary) .build(); - final GetCanaryResponse getCanaryResponse = GetCanaryResponse.builder() - .canary(canary) - .build(); - // final TagResourceRequest tagResourceRequest = TagResourceRequest.builder().resourceArn("arn:aws:synthetics:us-west-1:440056434621:canary:canarytestname").tags(sampleTags()).build(); - final TagResourceResponse tagResourceResponse = TagResourceResponse.builder().build(); final CallbackContext inputContext = CallbackContext.builder().build(); final CallbackContext outputContext = CallbackContext.builder().canaryCreateStarted(true).build(); - doReturn(createCanaryResponse, - getCanaryResponse, - tagResourceResponse).when(proxy).injectCredentialsAndInvokeV2(any(), any()); + doReturn(createCanaryResponse) + .when(proxy).injectCredentialsAndInvokeV2(eq(buildCreateCanaryRequest(false, model)), any()); final ProgressEvent response = handler.handleRequest(proxy, request, inputContext, logger); @@ -190,211 +183,34 @@ public void handleRequest_SimpleSuccessRemovingOptionalValues() { assertThat(response.getStatus()).isEqualTo(OperationStatus.IN_PROGRESS); assertThat(response.getCallbackContext()).isEqualToComparingFieldByField(outputContext); assertThat(response.getCallbackDelaySeconds()).isEqualTo(10); - assertThat(response.getResourceModel()).isEqualTo(request.getDesiredResourceState()); + assertThat(response.getResourceModel()).isEqualTo(modelClone); assertThat(response.getResourceModels()).isNull(); assertThat(response.getMessage()).isNull(); assertThat(response.getErrorCode()).isNull(); - assertThat(response.getResourceModel().getRunConfig()).isNull(); + verify(proxy).injectCredentialsAndInvokeV2(eq(buildCreateCanaryRequest(false, model)), any()); } @Test - public void handleRequest_createCanary_inProgress() { - final ResourceHandlerRequest request = ResourceHandlerRequest.builder() - .desiredResourceState(buildModel()) - .build(); - - final Canary canary = Canary.builder() - .name("canarytestname") - .code(codeOutputObjectForTesting()) - .status(CanaryStatus.builder() - .state("RUNNING") - .build()) - .schedule(canaryScheduleOutputForTesting()) - .build(); - - final CreateCanaryResponse createCanaryResponse = CreateCanaryResponse.builder() - .canary(canary) - .build(); - final GetCanaryResponse getCanaryResponse = GetCanaryResponse.builder() - .canary(canary) - .build(); - // final TagResourceRequest tagResourceRequest = TagResourceRequest.builder().resourceArn("arn:aws:synthetics:us-west-1:440056434621:canary:canarytestname").tags(sampleTags()).build(); - final TagResourceResponse tagResourceResponse = TagResourceResponse.builder().build(); - final CallbackContext inputContext = CallbackContext.builder().build(); - final CallbackContext outputContext = CallbackContext.builder().canaryCreateStarted(true).build(); - - doReturn(createCanaryResponse, - getCanaryResponse, - tagResourceResponse).when(proxy).injectCredentialsAndInvokeV2(any(), any()); - - final ProgressEvent response = handler.handleRequest(proxy, request, inputContext, logger); - - assertThat(response).isNotNull(); - assertThat(response.getStatus()).isEqualTo(OperationStatus.IN_PROGRESS); - assertThat(response.getCallbackContext()).isEqualToComparingFieldByField(outputContext); - assertThat(response.getCallbackDelaySeconds()).isEqualTo(10); - assertThat(response.getResourceModel()).isEqualTo(request.getDesiredResourceState()); - assertThat(response.getResourceModels()).isNull(); - assertThat(response.getMessage()).isNull(); - assertThat(response.getErrorCode()).isNull(); - } - - @Test - public void handleRequest_createCanary_withoutActiveTracing() { - ResourceModel model = buildModel(); - RunConfig runConfig = new RunConfig(); - runConfig.setTimeoutInSeconds(60); - runConfig.setMemoryInMB(1024); - model.setRunConfig(runConfig); - - final ResourceHandlerRequest request = ResourceHandlerRequest.builder() - .desiredResourceState(model) - .build(); - - CanaryRunConfigOutput outputExpected = CanaryRunConfigOutput.builder() - .timeoutInSeconds(60) - .memoryInMB(1024) - .activeTracing(false) - .build(); - - final Canary canary = Canary.builder() - .name("canarytestname") - .code(codeOutputObjectForTesting()) - .status(CanaryStatus.builder() - .state("RUNNING") - .build()) - .schedule(canaryScheduleOutputForTesting()) - .runConfig(outputExpected) - .build(); - - final CreateCanaryResponse createCanaryResponse = CreateCanaryResponse.builder() - .canary(canary) - .build(); - final GetCanaryResponse getCanaryResponse = GetCanaryResponse.builder() - .canary(canary) - .build(); - // final TagResourceRequest tagResourceRequest = TagResourceRequest.builder().resourceArn("arn:aws:synthetics:us-west-1:440056434621:canary:canarytestname").tags(sampleTags()).build(); - final TagResourceResponse tagResourceResponse = TagResourceResponse.builder().build(); - final CallbackContext inputContext = CallbackContext.builder().build(); - final CallbackContext outputContext = CallbackContext.builder().canaryCreateStarted(true).build(); - - doReturn(createCanaryResponse, - getCanaryResponse, - tagResourceResponse).when(proxy).injectCredentialsAndInvokeV2(any(), any()); - - final ProgressEvent response = handler.handleRequest(proxy, request, inputContext, logger); - - assertThat(response).isNotNull(); - assertThat(response.getStatus()).isEqualTo(OperationStatus.IN_PROGRESS); - assertThat(response.getCallbackContext()).isEqualToComparingFieldByField(outputContext); - assertThat(response.getCallbackDelaySeconds()).isEqualTo(10); - assertThat(response.getResourceModel()).isEqualTo(request.getDesiredResourceState()); - assertThat(response.getResourceModels()).isNull(); - assertThat(response.getMessage()).isNull(); - assertThat(response.getErrorCode()).isNull(); - assertThat(response.getResourceModel().getRunConfig().getTimeoutInSeconds()).isEqualTo(60); - assertThat(response.getResourceModel().getRunConfig().getActiveTracing()).isNull(); - } - - @Test - public void handleRequest_createCanary_withActiveTracingTrue() { - ResourceModel model = buildModel(); - RunConfig runConfig = new RunConfig(); - runConfig.setTimeoutInSeconds(60); - runConfig.setMemoryInMB(1024); - runConfig.setActiveTracing(true); - model.setRunConfig(runConfig); - final ResourceHandlerRequest request = ResourceHandlerRequest.builder() - .desiredResourceState(model) - .build(); - - CanaryRunConfigOutput outputExpected = CanaryRunConfigOutput.builder() - .timeoutInSeconds(60) - .memoryInMB(1024) - .activeTracing(true) - .build(); - - final Canary canary = Canary.builder() - .name("canarytestname") - .code(codeOutputObjectForTesting()) - .status(CanaryStatus.builder() - .state("RUNNING") - .build()) - .schedule(canaryScheduleOutputForTesting()) - .runConfig(outputExpected) - .build(); + public void handleRequest_createCanary_WithOptionals() { + ResourceModel model = buildModel(true); + ResourceModel modelClone = buildModel(true); - final CreateCanaryResponse createCanaryResponse = CreateCanaryResponse.builder() - .canary(canary) - .build(); - final GetCanaryResponse getCanaryResponse = GetCanaryResponse.builder() - .canary(canary) - .build(); - // final TagResourceRequest tagResourceRequest = TagResourceRequest.builder().resourceArn("arn:aws:synthetics:us-west-1:440056434621:canary:canarytestname").tags(sampleTags()).build(); - final TagResourceResponse tagResourceResponse = TagResourceResponse.builder().build(); - final CallbackContext inputContext = CallbackContext.builder().build(); - final CallbackContext outputContext = CallbackContext.builder().canaryCreateStarted(true).build(); - - doReturn(createCanaryResponse, - getCanaryResponse, - tagResourceResponse).when(proxy).injectCredentialsAndInvokeV2(any(), any()); - - final ProgressEvent response = handler.handleRequest(proxy, request, inputContext, logger); - - assertThat(response).isNotNull(); - assertThat(response.getStatus()).isEqualTo(OperationStatus.IN_PROGRESS); - assertThat(response.getCallbackContext()).isEqualToComparingFieldByField(outputContext); - assertThat(response.getCallbackDelaySeconds()).isEqualTo(10); - assertThat(response.getResourceModel()).isEqualTo(request.getDesiredResourceState()); - assertThat(response.getResourceModels()).isNull(); - assertThat(response.getMessage()).isNull(); - assertThat(response.getErrorCode()).isNull(); - assertThat(response.getResourceModel().getRunConfig().getTimeoutInSeconds()).isEqualTo(60); - assertThat(response.getResourceModel().getRunConfig().getActiveTracing()).isEqualTo(true); - } - - @Test - public void handleRequest_createCanary_withActiveTracingFalse() { - ResourceModel model = buildModel(); - RunConfig runConfig = new RunConfig(); - runConfig.setTimeoutInSeconds(60); - runConfig.setMemoryInMB(1024); - runConfig.setActiveTracing(false); - model.setRunConfig(runConfig); final ResourceHandlerRequest request = ResourceHandlerRequest.builder() .desiredResourceState(model) .build(); - CanaryRunConfigOutput outputExpected = CanaryRunConfigOutput.builder() - .timeoutInSeconds(60) - .memoryInMB(1024) - .activeTracing(false) - .build(); - final Canary canary = Canary.builder() - .name("canarytestname") - .code(codeOutputObjectForTesting()) - .status(CanaryStatus.builder() - .state("RUNNING") - .build()) - .schedule(canaryScheduleOutputForTesting()) - .runConfig(outputExpected) + .name(CANARY_NAME) .build(); final CreateCanaryResponse createCanaryResponse = CreateCanaryResponse.builder() .canary(canary) .build(); - final GetCanaryResponse getCanaryResponse = GetCanaryResponse.builder() - .canary(canary) - .build(); - // final TagResourceRequest tagResourceRequest = TagResourceRequest.builder().resourceArn("arn:aws:synthetics:us-west-1:440056434621:canary:canarytestname").tags(sampleTags()).build(); - final TagResourceResponse tagResourceResponse = TagResourceResponse.builder().build(); final CallbackContext inputContext = CallbackContext.builder().build(); final CallbackContext outputContext = CallbackContext.builder().canaryCreateStarted(true).build(); - doReturn(createCanaryResponse, - getCanaryResponse, - tagResourceResponse).when(proxy).injectCredentialsAndInvokeV2(any(), any()); + doReturn(createCanaryResponse) + .when(proxy).injectCredentialsAndInvokeV2(eq(buildCreateCanaryRequest(true, model)), any()); final ProgressEvent response = handler.handleRequest(proxy, request, inputContext, logger); @@ -402,148 +218,32 @@ public void handleRequest_createCanary_withActiveTracingFalse() { assertThat(response.getStatus()).isEqualTo(OperationStatus.IN_PROGRESS); assertThat(response.getCallbackContext()).isEqualToComparingFieldByField(outputContext); assertThat(response.getCallbackDelaySeconds()).isEqualTo(10); - assertThat(response.getResourceModel()).isEqualTo(request.getDesiredResourceState()); + assertThat(response.getResourceModel()).isEqualTo(modelClone); assertThat(response.getResourceModels()).isNull(); assertThat(response.getMessage()).isNull(); assertThat(response.getErrorCode()).isNull(); - assertThat(response.getResourceModel().getRunConfig().getTimeoutInSeconds()).isEqualTo(60); - assertThat(response.getResourceModel().getRunConfig().getActiveTracing()).isEqualTo(false); + verify(proxy).injectCredentialsAndInvokeV2(eq(buildCreateCanaryRequest(true, model)), any()); } + // need a separate test for this field because we can either supply code or s3 bucket with s3 key @Test - public void handleRequest_createCanary_withNoEnvironmentVariables() { - ResourceModel model = buildModel(); - RunConfig runConfig = new RunConfig(); - runConfig.setTimeoutInSeconds(60); - runConfig.setMemoryInMB(1024); - runConfig.setActiveTracing(false); - model.setRunConfig(runConfig); - final ResourceHandlerRequest request = ResourceHandlerRequest.builder() - .desiredResourceState(model) - .build(); - - CanaryRunConfigOutput outputExpected = CanaryRunConfigOutput.builder() - .timeoutInSeconds(60) - .memoryInMB(1024) - .activeTracing(false) - .build(); - - final Canary canary = Canary.builder() - .name("canarytestname") - .code(codeOutputObjectForTesting()) - .status(CanaryStatus.builder() - .state("RUNNING") - .build()) - .schedule(canaryScheduleOutputForTesting()) - .runConfig(outputExpected) - .build(); - - final CreateCanaryResponse createCanaryResponse = CreateCanaryResponse.builder() - .canary(canary) - .build(); - final GetCanaryResponse getCanaryResponse = GetCanaryResponse.builder() - .canary(canary) - .build(); - // final TagResourceRequest tagResourceRequest = TagResourceRequest.builder().resourceArn("arn:aws:synthetics:us-west-1:440056434621:canary:canarytestname").tags(sampleTags()).build(); - final TagResourceResponse tagResourceResponse = TagResourceResponse.builder().build(); - final CallbackContext inputContext = CallbackContext.builder().build(); - final CallbackContext outputContext = CallbackContext.builder().canaryCreateStarted(true).build(); - - doReturn(createCanaryResponse, - getCanaryResponse, - tagResourceResponse).when(proxy).injectCredentialsAndInvokeV2(any(), any()); - - final ProgressEvent response = handler.handleRequest(proxy, request, inputContext, logger); - - assertThat(response).isNotNull(); - assertThat(response.getStatus()).isEqualTo(OperationStatus.IN_PROGRESS); - assertThat(response.getCallbackContext()).isEqualToComparingFieldByField(outputContext); - assertThat(response.getCallbackDelaySeconds()).isEqualTo(10); - assertThat(response.getResourceModel()).isEqualTo(request.getDesiredResourceState()); - assertThat(response.getResourceModels()).isNull(); - assertThat(response.getMessage()).isNull(); - assertThat(response.getErrorCode()).isNull(); - assertThat(response.getResourceModel().getRunConfig().getTimeoutInSeconds()).isEqualTo(60); - assertThat(response.getResourceModel().getRunConfig().getActiveTracing()).isEqualTo(false); - } - - @Test - public void handleRequest_createCanary_withEnvironmentVariables() { - ResourceModel model = buildModel(); - Map environmentVariablesMap = new HashMap<>(); - environmentVariablesMap.put("env_key", "env_val"); - RunConfig runConfig = new RunConfig(); - runConfig.setTimeoutInSeconds(60); - runConfig.setMemoryInMB(1024); - runConfig.setActiveTracing(false); - runConfig.setEnvironmentVariables(environmentVariablesMap); - model.setRunConfig(runConfig); - final ResourceHandlerRequest request = ResourceHandlerRequest.builder() - .desiredResourceState(model) - .build(); - - CanaryRunConfigOutput outputExpected = CanaryRunConfigOutput.builder() - .timeoutInSeconds(60) - .memoryInMB(1024) - .activeTracing(false) - .build(); - - final Canary canary = Canary.builder() - .name("canarytestname") - .code(codeOutputObjectForTesting()) - .status(CanaryStatus.builder() - .state("RUNNING") - .build()) - .schedule(canaryScheduleOutputForTesting()) - .runConfig(outputExpected) - .build(); - - final CreateCanaryResponse createCanaryResponse = CreateCanaryResponse.builder() - .canary(canary) - .build(); - final GetCanaryResponse getCanaryResponse = GetCanaryResponse.builder() - .canary(canary) - .build(); - // final TagResourceRequest tagResourceRequest = TagResourceRequest.builder().resourceArn("arn:aws:synthetics:us-west-1:440056434621:canary:canarytestname").tags(sampleTags()).build(); - final TagResourceResponse tagResourceResponse = TagResourceResponse.builder().build(); - final CallbackContext inputContext = CallbackContext.builder().build(); - final CallbackContext outputContext = CallbackContext.builder().canaryCreateStarted(true).build(); - - doReturn(createCanaryResponse, - getCanaryResponse, - tagResourceResponse).when(proxy).injectCredentialsAndInvokeV2(any(), any()); - - final ProgressEvent response = handler.handleRequest(proxy, request, inputContext, logger); - - assertThat(response).isNotNull(); - assertThat(response.getStatus()).isEqualTo(OperationStatus.IN_PROGRESS); - assertThat(response.getCallbackContext()).isEqualToComparingFieldByField(outputContext); - assertThat(response.getCallbackDelaySeconds()).isEqualTo(10); - assertThat(response.getResourceModel()).isEqualTo(request.getDesiredResourceState()); - assertThat(response.getResourceModels()).isNull(); - assertThat(response.getMessage()).isNull(); - assertThat(response.getErrorCode()).isNull(); - assertThat(response.getResourceModel().getRunConfig().getTimeoutInSeconds()).isEqualTo(60); - assertThat(response.getResourceModel().getRunConfig().getActiveTracing()).isEqualTo(false); - } - - @Test - public void handleRequest_createCanary_withKmsEncryption() { - ResourceModel model = buildModel(); - - RunConfig runConfig = new RunConfig(); - runConfig.setTimeoutInSeconds(60); - runConfig.setMemoryInMB(1024); - runConfig.setActiveTracing(false); - model.setRunConfig(runConfig); - - final ArtifactConfig artifactConfig = new ArtifactConfig(); - final S3Encryption s3Encryption = new S3Encryption(); - s3Encryption.setEncryptionMode("SSE_KMS"); - s3Encryption.setKmsKeyArn("arn:aws:kms:us-west-2:222222222222:key/kmskeyId"); - artifactConfig.setS3Encryption(s3Encryption); - model.setArtifactConfig(artifactConfig); - + public void handleRequest_createCanary_WithS3Bucket() { + ResourceModel model = buildModel(true); + ResourceModel modelClone = buildModel(true); + final Code code = new Code("s3bucket", + "s3Key", + null, + null, + "pageLoadBlueprint.handler", + null); + final Code codeClone = new Code("s3bucket", + "s3Key", + null, + null, + "pageLoadBlueprint.handler", + null); + model.setCode(code); + modelClone.setCode(codeClone); final ResourceHandlerRequest request = ResourceHandlerRequest.builder() .desiredResourceState(model) .build(); @@ -554,29 +254,19 @@ public void handleRequest_createCanary_withKmsEncryption() { .activeTracing(false) .build(); + // does not matter what we return, as we ignore canary response final Canary canary = Canary.builder() - .name("canarytestname") - .code(codeOutputObjectForTesting()) - .status(CanaryStatus.builder() - .state("RUNNING") - .build()) - .schedule(canaryScheduleOutputForTesting()) - .runConfig(outputExpected) + .name(CANARY_NAME) .build(); final CreateCanaryResponse createCanaryResponse = CreateCanaryResponse.builder() .canary(canary) .build(); - final GetCanaryResponse getCanaryResponse = GetCanaryResponse.builder() - .canary(canary) - .build(); - final TagResourceResponse tagResourceResponse = TagResourceResponse.builder().build(); final CallbackContext inputContext = CallbackContext.builder().build(); final CallbackContext outputContext = CallbackContext.builder().canaryCreateStarted(true).build(); - doReturn(createCanaryResponse, - getCanaryResponse, - tagResourceResponse).when(proxy).injectCredentialsAndInvokeV2(any(), any()); + doReturn(createCanaryResponse) + .when(proxy).injectCredentialsAndInvokeV2(eq(buildCreateCanaryRequest(true, model)), any()); final ProgressEvent response = handler.handleRequest(proxy, request, inputContext, logger); @@ -584,13 +274,11 @@ public void handleRequest_createCanary_withKmsEncryption() { assertThat(response.getStatus()).isEqualTo(OperationStatus.IN_PROGRESS); assertThat(response.getCallbackContext()).isEqualToComparingFieldByField(outputContext); assertThat(response.getCallbackDelaySeconds()).isEqualTo(10); - assertThat(response.getResourceModel()).isEqualTo(request.getDesiredResourceState()); + assertThat(response.getResourceModel()).isEqualTo(modelClone); assertThat(response.getResourceModels()).isNull(); assertThat(response.getMessage()).isNull(); assertThat(response.getErrorCode()).isNull(); - assertThat(response.getResourceModel().getRunConfig().getTimeoutInSeconds()).isEqualTo(60); - assertThat(response.getResourceModel().getRunConfig().getActiveTracing()).isEqualTo(false); - assertThat(response.getResourceModel().getArtifactConfig().getS3Encryption().getEncryptionMode()).isEqualTo("SSE_KMS"); - assertThat(response.getResourceModel().getArtifactConfig().getS3Encryption().getKmsKeyArn()).isEqualTo("arn:aws:kms:us-west-2:222222222222:key/kmskeyId"); + verify(proxy).injectCredentialsAndInvokeV2(eq(buildCreateCanaryRequest(true, model)), any()); } } + diff --git a/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/DeleteHandlerTest.java b/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/DeleteHandlerTest.java index a68f5b8..2ea2d01 100644 --- a/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/DeleteHandlerTest.java +++ b/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/DeleteHandlerTest.java @@ -117,6 +117,20 @@ public void handleRequest_canaryStateAllows_invokesDeleteCanaryWithDeleteLambda_ .deleteLambda(true).build()), any()); } + @ParameterizedTest + @EnumSource(value = CanaryState.class, names = {"READY", "STOPPED", "ERROR"}) + public void handleRequest_canaryStateAllows_invokesDeleteCanaryWithDeleteLambdaFalse_inProgress(CanaryState state) { + configureGetCanaryResponse(state); + + + ProgressEvent response = + handler.handleRequest(proxy, REQUEST_WITH_DELETELAMBDA_FALSE, null, logger); + + assertThat(response.getStatus()).isEqualTo(OperationStatus.IN_PROGRESS); + verify(proxy).injectCredentialsAndInvokeV2(eq(DeleteCanaryRequest.builder().name(CANARY_NAME) + .deleteLambda(false).build()), any()); + } + @ParameterizedTest @EnumSource(value = CanaryState.class, names = {"READY", "STOPPED", "ERROR"}) public void handleRequest_canaryStateAllows_invokesDeleteCanary_handlesAlreadyDeleted_success(CanaryState state) { @@ -175,7 +189,7 @@ public void handleRequest_confirmCanaryDeleted_canaryExists_inProgress() { } @Test - public void handleRequest_confirmCanaryDeleted_canaryExists_WithDeleteLambda__inProgress() { + public void handleRequest_confirmCanaryDeleted_canaryExists_WithDeleteLambda_inProgress() { CallbackContext context = CallbackContext.builder().canaryDeleteStarted(true).build(); configureGetCanaryResponse(CanaryState.READY); @@ -185,7 +199,7 @@ public void handleRequest_confirmCanaryDeleted_canaryExists_WithDeleteLambda__in } @Test - public void handleRequest_confirmCanaryDeleted_canaryExistsInDELETING_WithDeleteLambda__inProgress() { + public void handleRequest_confirmCanaryDeleted_canaryExistsInDELETING_WithDeleteLambda_inProgress() { CallbackContext context = CallbackContext.builder().canaryDeleteStarted(true).build(); configureGetCanaryResponse(CanaryState.DELETING); diff --git a/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/ListHandlerTest.java b/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/ListHandlerTest.java index 7f9c7b3..8a75e88 100644 --- a/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/ListHandlerTest.java +++ b/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/ListHandlerTest.java @@ -1,22 +1,31 @@ package com.amazon.synthetics.canary; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentMatchers; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import software.amazon.awssdk.services.synthetics.model.Canary; +import software.amazon.awssdk.services.synthetics.model.DescribeCanariesRequest; import software.amazon.awssdk.services.synthetics.model.DescribeCanariesResponse; -import software.amazon.cloudformation.proxy.*; +import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy; +import software.amazon.cloudformation.proxy.Logger; +import software.amazon.cloudformation.proxy.ResourceHandlerRequest; +import software.amazon.cloudformation.proxy.OperationStatus; +import software.amazon.cloudformation.proxy.ProgressEvent; import java.util.ArrayList; import java.util.List; import static com.amazon.synthetics.canary.Matchers.assertThatModelsAreEqual; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) public class ListHandlerTest extends TestBase { @@ -39,16 +48,18 @@ public void setup() { model = ResourceModel.builder().name("listCanary").build(); } - @Test - public void handleRequest_SimpleSuccess() { + @ParameterizedTest + @NullSource + @ValueSource(strings = {"test_next_token"}) + public void handleRequest_SimpleSuccess(final String nextToken) { final ResourceHandlerRequest request = ResourceHandlerRequest.builder() .desiredResourceState(model) - .nextToken("test") + .nextToken(nextToken) .build(); final List canaryList = new ArrayList(); - Canary canary1 = canaryResponseObjectForTesting("1-canary-cfn-unit"); - Canary canary2 = canaryResponseObjectForTesting("2-canary-cfn-unit"); + Canary canary1 = canaryResponseObjectForTesting("1-canary-cfn-unit", false); + Canary canary2 = canaryResponseObjectForTesting("2-canary-cfn-unit", false); canaryList.add(canary1); canaryList.add(canary2); @@ -60,8 +71,8 @@ public void handleRequest_SimpleSuccess() { doReturn(describeCanariesResponse) .when(proxy) .injectCredentialsAndInvokeV2( - ArgumentMatchers.any(), - ArgumentMatchers.any() + eq(DescribeCanariesRequest.builder().nextToken(nextToken).build()), + any() ); final ProgressEvent response = handler.handleRequest(proxy, request, null, logger); @@ -76,5 +87,10 @@ public void handleRequest_SimpleSuccess() { assertThatModelsAreEqual(response.getResourceModels().get(1), canary2); assertThat(response.getMessage()).isNull(); assertThat(response.getErrorCode()).isNull(); + + verify(proxy).injectCredentialsAndInvokeV2( + eq(DescribeCanariesRequest.builder().nextToken(nextToken).build()), + any() + ); } } diff --git a/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/ReadHandlerTest.java b/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/ReadHandlerTest.java index 7310643..99361d7 100644 --- a/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/ReadHandlerTest.java +++ b/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/ReadHandlerTest.java @@ -1,54 +1,62 @@ package com.amazon.synthetics.canary; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.junit.jupiter.MockitoExtension; -import software.amazon.awssdk.services.synthetics.model.Canary; -import software.amazon.awssdk.services.synthetics.model.GetCanaryResponse; -import software.amazon.cloudformation.proxy.*; +import software.amazon.awssdk.services.synthetics.model.GetCanaryRequest; +import software.amazon.awssdk.services.synthetics.model.ResourceNotFoundException; +import software.amazon.cloudformation.exceptions.CfnNotFoundException; +import software.amazon.cloudformation.proxy.ProgressEvent; +import software.amazon.cloudformation.proxy.OperationStatus; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) -public class ReadHandlerTest extends TestBase{ +public class ReadHandlerTest extends TestBase { - @Mock - private AmazonWebServicesClientProxy proxy; + @ParameterizedTest + @ValueSource(booleans = {true, false}) + public void handleRequest_SimpleSuccess(boolean useDefaultS3Encryption) { + final ReadHandler handler = new ReadHandler(); - @Mock - private Logger logger; + configureGetCanaryResponse(canaryResponseObjectForTesting(CANARY_NAME, true, useDefaultS3Encryption)); - @BeforeEach - public void setup() { - proxy = mock(AmazonWebServicesClientProxy.class); - logger = mock(Logger.class); - } + ResourceModel outputModel = buildModelForRead(true, useDefaultS3Encryption); - @Test - public void handleRequest_SimpleSuccess() { - final ReadHandler handler = new ReadHandler(); + final ProgressEvent response + = handler.handleRequest(proxy, buildResourceHandlerRequestWithName(CANARY_NAME), null, logger); - final ResourceModel model = ResourceModel.builder().name("cfncanary").build(); + assertThat(response).isNotNull(); + assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS); + assertThat(response.getCallbackContext()).isNull(); + assertThat(response.getCallbackDelaySeconds()).isEqualTo(0); + assertThat(response.getResourceModel()).isEqualTo(outputModel); + assertThat(response.getResourceModels()).isNull(); + assertThat(response.getMessage()).isNull(); + assertThat(response.getErrorCode()).isNull(); - final ResourceHandlerRequest request = ResourceHandlerRequest.builder() - .desiredResourceState(model) - .build(); + verify(proxy).injectCredentialsAndInvokeV2( + eq(GetCanaryRequest.builder().name(CANARY_NAME).build()), + any() + ); + } - GetCanaryResponse getCanaryResponse = GetCanaryResponse.builder() - .canary(canaryResponseObjectForTesting("cfncanary-unittest")) - .build(); + @Test + public void handleRequest_SimpleSuccess_NoOptionals() { + final ReadHandler handler = new ReadHandler(); - doReturn(getCanaryResponse).when(proxy).injectCredentialsAndInvokeV2(any(), any()); + configureGetCanaryResponse(canaryResponseObjectForTesting(CANARY_NAME, false, false)); - ResourceModel outputModel = buildModelForRead(); + ResourceModel outputModel = buildModelForRead(false, false); final ProgressEvent response - = handler.handleRequest(proxy, request, null, logger); + = handler.handleRequest(proxy, buildResourceHandlerRequestWithName(CANARY_NAME), null, logger); assertThat(response).isNotNull(); assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS); @@ -58,5 +66,26 @@ public void handleRequest_SimpleSuccess() { assertThat(response.getResourceModels()).isNull(); assertThat(response.getMessage()).isNull(); assertThat(response.getErrorCode()).isNull(); + + verify(proxy).injectCredentialsAndInvokeV2( + eq(GetCanaryRequest.builder().name(CANARY_NAME).build()), + any() + ); + } + + @Test + public void handleRequest_CanaryNotFound() { + final ReadHandler handler = new ReadHandler(); + + // if we throw ResourceNotFoundException when getting canary, that error gets caught and CfnNotFoundException is thrown. + configureGetCanaryResponse(ResourceNotFoundException.builder().build()); + + assertThatThrownBy(() -> handler.handleRequest(proxy, buildResourceHandlerRequestWithName(CANARY_NAME), null, logger)) + .isInstanceOf(CfnNotFoundException.class); + + verify(proxy).injectCredentialsAndInvokeV2( + eq(GetCanaryRequest.builder().name(CANARY_NAME).build()), + any() + ); } } diff --git a/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/TestBase.java b/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/TestBase.java index 470623c..44e777e 100644 --- a/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/TestBase.java +++ b/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/TestBase.java @@ -1,19 +1,37 @@ package com.amazon.synthetics.canary; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import software.amazon.awssdk.services.synthetics.model.*; +import com.google.common.base.Strings; + +import software.amazon.awssdk.services.synthetics.model.ArtifactConfigOutput; +import software.amazon.awssdk.services.synthetics.model.Canary; +import software.amazon.awssdk.services.synthetics.model.CanaryCodeInput; +import software.amazon.awssdk.services.synthetics.model.CanaryCodeOutput; +import software.amazon.awssdk.services.synthetics.model.CanaryRunConfigInput; +import software.amazon.awssdk.services.synthetics.model.CanaryRunConfigOutput; +import software.amazon.awssdk.services.synthetics.model.CanaryScheduleInput; +import software.amazon.awssdk.services.synthetics.model.CanaryScheduleOutput; +import software.amazon.awssdk.services.synthetics.model.CanaryState; +import software.amazon.awssdk.services.synthetics.model.CanaryStatus; +import software.amazon.awssdk.services.synthetics.model.CreateCanaryRequest; +import software.amazon.awssdk.services.synthetics.model.GetCanaryRequest; +import software.amazon.awssdk.services.synthetics.model.GetCanaryResponse; +import software.amazon.awssdk.services.synthetics.model.S3EncryptionConfig; +import software.amazon.awssdk.services.synthetics.model.VpcConfigInput; +import software.amazon.awssdk.services.synthetics.model.VpcConfigOutput; +import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy; +import software.amazon.cloudformation.proxy.Logger; +import software.amazon.cloudformation.proxy.ResourceHandlerRequest; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy; -import software.amazon.cloudformation.proxy.Logger; -import software.amazon.cloudformation.proxy.ResourceHandlerRequest; +import java.util.regex.Pattern; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class TestBase { protected static final String CANARY_NAME = "canary-name"; @@ -25,11 +43,23 @@ public class TestBase { .region("us-west-2") .build(); + protected static final ResourceHandlerRequest REQUEST_WITH_DELETELAMBDA_FALSE = ResourceHandlerRequest.builder() + .desiredResourceState(buildModel("syn-nodejs-2.0-beta", false, false, true, false)) + .awsPartition("aws") + .region("us-west-2") + .build(); + protected static final ResourceHandlerRequest REQUEST = ResourceHandlerRequest.builder() .desiredResourceState(buildModel("syn-nodejs-2.0-beta", false, false, true, null)) .awsPartition("aws") .region("us-west-2") .build(); + + protected static final ResourceHandlerRequest REQUEST_NULL_START_CANARY = ResourceHandlerRequest.builder() + .desiredResourceState(buildModel("syn-nodejs-2.0-beta", false, null, true, null)) + .awsPartition("aws") + .region("us-west-2") + .build(); protected static final ResourceHandlerRequest REQUEST_START_CANARY = ResourceHandlerRequest.builder() .desiredResourceState(buildModel("syn-nodejs-2.0-beta", false, true, true, null)) .awsPartition("aws") @@ -46,18 +76,41 @@ public void log(String s) { } } - public static ResourceModel buildModelForRead() { + public static ResourceModel buildModelForRead(boolean includeOptionals, boolean useDefaultS3Encryption) { + if (includeOptionals) { + ResourceModel model = ResourceModel.builder() + .name(CANARY_NAME) + .id("test-id") + .artifactS3Location("s3://cw-syn-results-440056434621-us-west-2/canary/canary-1254-38b-0a58c26fe372") + .code(codeObjectForTesting()) + .executionRoleArn("arn:aws:us-east-1:1234567891000:role/SyntheticsRole") + .schedule(scheduleObjectForTesting()) + .runtimeVersion("syn-1.0") + .vPCConfig(vpcConfigForTesting()) + .state("RUNNING") + .tags(buildTagObject(new HashMap())) + .runConfig(RunConfig.builder().timeoutInSeconds(50).memoryInMB(960).activeTracing(true).build()) + .successRetentionPeriod(31) + .failureRetentionPeriod(31) + .artifactConfig(ArtifactConfig.builder() + .s3Encryption(S3Encryption.builder() + .encryptionMode("SSE") + .kmsKeyArn(useDefaultS3Encryption ? null : "arn:kms") + .build()) + .build()) + .build(); + return model; + } ResourceModel model = ResourceModel.builder() - .name("cfncanary-unittest") + .name(CANARY_NAME) + .id("test-id") .artifactS3Location("s3://cw-syn-results-440056434621-us-west-2/canary/canary-1254-38b-0a58c26fe372") .code(codeObjectForTesting()) .executionRoleArn("arn:aws:us-east-1:1234567891000:role/SyntheticsRole") .schedule(scheduleObjectForTesting()) .runtimeVersion("syn-1.0") - .vPCConfig(vpcConfigForTesting()) .state("RUNNING") .tags(buildTagObject(new HashMap())) - .runConfig(RunConfig.builder().timeoutInSeconds(50).build()) .build(); return model; } @@ -67,12 +120,13 @@ public static Code codeObjectForTesting() { null, null, null, - "pageLoadBlueprint.handler"); + "pageLoadBlueprint.handler", + "arn:aws:lambda:us-west-2:440056434621:layer:cwsyn-cfncanary-017e8100-8bee-4ba9-bf6e-2e9837592425:1"); return codeObjectForTesting; } public static VPCConfig vpcConfigForTesting() { - VPCConfig vpcConfig = new VPCConfig(); + VPCConfig vpcConfig = new VPCConfig(); List securityGroupIds = new ArrayList(); securityGroupIds.add("sg-085912345678492fb"); securityGroupIds.add("sg-085912345678492fc"); @@ -107,22 +161,46 @@ public static Schedule scheduleObjectForTesting() { } /* - ********************** Test Outputs ****************************** + ********************** Test Outputs ****************************** */ - public static Canary canaryResponseObjectForTesting(String canaryName) { + public static Canary canaryResponseObjectForTesting(String canaryName, boolean includeOptionals, boolean useDefaultS3Encryption) { + if (includeOptionals) { + Canary cfnCanary = Canary.builder() + .name(canaryName) + .id("test-id") + .artifactS3Location("cw-syn-results-440056434621-us-west-2/canary/canary-1254-38b-0a58c26fe372") + .code(codeOutputObjectForTesting()) + .engineArn("arn:aws:us-east-1:123456789101:function:testFunction:1") + .executionRoleArn("arn:aws:us-east-1:1234567891000:role/SyntheticsRole") + .schedule(canaryScheduleOutputForTesting()) + .runtimeVersion("syn-1.0") + .vpcConfig(canaryVpcConfigOutputForTesting()) + .status(canaryStatusForTesting()) + .runConfig(CanaryRunConfigOutput.builder().timeoutInSeconds(50).memoryInMB(960).activeTracing(true).build()) + .artifactConfig(ArtifactConfigOutput.builder() + .s3Encryption(S3EncryptionConfig.builder() + .encryptionMode("SSE") + .kmsKeyArn(useDefaultS3Encryption ? null : "arn:kms") + .build()) + .build()) + .successRetentionPeriodInDays(31) + .failureRetentionPeriodInDays(31) + .build(); + return cfnCanary; + } Canary cfnCanary = Canary.builder() .name(canaryName) + .id("test-id") .artifactS3Location("cw-syn-results-440056434621-us-west-2/canary/canary-1254-38b-0a58c26fe372") .code(codeOutputObjectForTesting()) .engineArn("arn:aws:us-east-1:123456789101:function:testFunction:1") .executionRoleArn("arn:aws:us-east-1:1234567891000:role/SyntheticsRole") .schedule(canaryScheduleOutputForTesting()) .runtimeVersion("syn-1.0") - .vpcConfig(canaryVpcConfigOutputForTesting()) .status(canaryStatusForTesting()) - .runConfig(CanaryRunConfigOutput.builder().timeoutInSeconds(50).build()) .build(); return cfnCanary; + } public static CanaryCodeOutput codeOutputObjectForTesting() { @@ -138,6 +216,7 @@ public static CanaryScheduleOutput canaryScheduleOutputForTesting() { .durationInSeconds(Long.valueOf("3600")).build(); return canaryScheduleOutput; } + public static CanaryScheduleOutput canaryScheduleOutputWithNullDurationForTesting() { CanaryScheduleOutput canaryScheduleOutput = CanaryScheduleOutput.builder() .expression("rate(1 min)") @@ -178,27 +257,27 @@ protected void configureGetCanaryResponse(CanaryState state, String stateReason) protected void configureGetCanaryResponse(Canary canary) { when(proxy.injectCredentialsAndInvokeV2(eq(GetCanaryRequest.builder().name(canary.name()).build()), any())) - .thenReturn(GetCanaryResponse.builder().canary(canary).build()); + .thenReturn(GetCanaryResponse.builder().canary(canary).build()); } protected void configureGetCanaryResponse(Throwable throwable) { when(proxy.injectCredentialsAndInvokeV2(eq(GetCanaryRequest.builder().name(CANARY_NAME).build()), any())) - .thenThrow(throwable); + .thenThrow(throwable); } protected static Canary createCanaryWithState(CanaryState state, String stateReason) { return Canary.builder() - .name(CANARY_NAME) - .executionRoleArn("test execution arn") - .code(codeOutputObjectForTesting()) - .status(CanaryStatus.builder() - .state(state) - .stateReason(stateReason) - .build()) - .runConfig(CanaryRunConfigOutput.builder().timeoutInSeconds(60).build()) - .schedule(canaryScheduleOutputForTesting()) - .runtimeVersion("syn-1.0") - .build(); + .name(CANARY_NAME) + .executionRoleArn("test execution arn") + .code(codeOutputObjectForTesting()) + .status(CanaryStatus.builder() + .state(state) + .stateReason(stateReason) + .build()) + .runConfig(CanaryRunConfigOutput.builder().timeoutInSeconds(60).build()) + .schedule(canaryScheduleOutputForTesting()) + .runtimeVersion("syn-1.0") + .build(); } protected static ResourceModel buildModel() { @@ -206,41 +285,80 @@ protected static ResourceModel buildModel() { } protected static ResourceModel buildModel(boolean useOptionalValues) { - return buildModel("syn-1.0", null, true, false, null); + return buildModel("syn-1.0", null, true, useOptionalValues, null); } protected static ResourceModel buildModel(String runtimeVersion, Boolean isActiveTracing) { return buildModel(runtimeVersion, isActiveTracing, true, true, null); } + protected static ResourceModel buildModel(String runtimeVersion, Boolean isActiveTracing, Boolean startCanaryAfterCreation, boolean useOptionalValues, Boolean deleteLambdaResources) { - final Code codeObjectForTesting = new Code(null, + + TestBase tb = new TestBase(); + + + tb.logger.log("resource model building with: " + runtimeVersion + " " + isActiveTracing + " " + startCanaryAfterCreation + " " + useOptionalValues); + + + final Code codeObjectForNodeJS = new Code(null, null, + null, + "var synthetics = require('Synthetics');\n" + + "const log = require('SyntheticsLogger');\n" + + "\n" + + "const pageLoadBlueprint = async function () {\n" + + "\n" + + " // INSERT URL here\n" + + " const URL = \"https://amazon.com\";\n" + + "\n" + + " let page = await synthetics.getPage();\n" + + " const response = await page.goto(URL, {waitUntil: 'domcontentloaded', timeout: 30000});\n" + + " //Wait for page to render.\n" + + " //Increase or decrease wait time based on endpoint being monitored.\n" + + " await page.waitFor(15000);\n" + + " await synthetics.takeScreenshot('loaded', 'loaded');\n" + + " let pageTitle = await page.title();\n" + + " log.info('Page title: ' + pageTitle);\n" + + " if (response.status() !== 200) {\n" + + " throw \"Failed to load page!\";\n" + + " }\n" + + "};\n" + + "\n" + + "exports.handler = async () => {\n" + " return await pageLoadBlueprint();\n" + "};", - "pageLoadBlueprint.handler"); + "pageLoadBlueprint.handler", + null); + + final Code codeObjectForPython = new Code(null, + null, + null, + "from aws_synthetics.selenium import synthetics_webdriver as syn_webdriver\nfrom aws_synthetics.common import synthetics_logger as logger\n\ndef main():\n url = \"https://etsy.com\"\n\n # Set screenshot option\n takeScreenshot = True\n\n browser = syn_webdriver.Chrome()\n browser.get(url)\n\n if takeScreenshot:\n browser.save_screenshot(\"loaded.png\")\n\n response_code = syn_webdriver.get_http_response(url)\n if not response_code or response_code < 200 or response_code > 299:\n raise Exception(\"Failed to load page!\")\n logger.info(\"Canary successfully executed\")\n\ndef handler(event, context):\n # user defined log statements using synthetics_logger\n logger.info(\"Selenium Python heartbeat canary\")\n return main()\n", + "pageLoadBlueprint.handler", + null); final Schedule scheduleForTesting = new Schedule(); scheduleForTesting.setDurationInSeconds("3600"); @@ -258,6 +376,25 @@ protected static ResourceModel buildModel(String runtimeVersion, Boolean isActiv RunConfig runConfig; List listTag = new ArrayList<>(); + Map environmentVariables = new HashMap<>(); + environmentVariables.put("key1", "value1"); + + + List baseScreenshots = new ArrayList<>(); + baseScreenshots.add(BaseScreenshot.builder() + .screenshotName("test.png") + .ignoreCoordinates(new ArrayList<>()) + .build()); + final VisualReference visualReference = new VisualReference(); + visualReference.setBaseCanaryRunId("95e5cbe3-44ab-4a75-b886-ad2be207d899"); + visualReference.setBaseScreenshots(baseScreenshots); + + final ArtifactConfig artifactConfig = new ArtifactConfig(); + artifactConfig.setS3Encryption(S3Encryption.builder() + .encryptionMode("SSE") + .kmsKeyArn("mock_kms_arn") + .build()); + if (useOptionalValues) { vpcConfig.setSubnetIds(subnetIds); vpcConfig.setSecurityGroupIds(securityGroups); @@ -265,18 +402,25 @@ protected static ResourceModel buildModel(String runtimeVersion, Boolean isActiv tagUpdate = Tag.builder().key("key2").value("value2").build(); listTag.add(tagUpdate); - runConfig = RunConfig.builder().timeoutInSeconds(600).memoryInMB(960).activeTracing(isActiveTracing).build(); + runConfig = RunConfig.builder() + .timeoutInSeconds(600) + .memoryInMB(960) + .activeTracing(isActiveTracing) + .environmentVariables(environmentVariables) + .build(); return ResourceModel.builder() .name(CANARY_NAME) .artifactS3Location("s3://cloudformation-created-bucket") - .code(codeObjectForTesting) + .code(Pattern.compile("^syn-python-*").matcher(runtimeVersion).matches() ? codeObjectForPython : codeObjectForNodeJS) .executionRoleArn("arn:aws:test::myaccount") .schedule(scheduleForTesting) .runtimeVersion(runtimeVersion) .startCanaryAfterCreation(startCanaryAfterCreation) .deleteLambdaResourcesOnCanaryDeletion(deleteLambdaResources) .vPCConfig(vpcConfig) + .visualReference(visualReference) + .artifactConfig(artifactConfig) .tags(listTag) .runConfig(runConfig) .failureRetentionPeriod(31) @@ -285,13 +429,79 @@ protected static ResourceModel buildModel(String runtimeVersion, Boolean isActiv } return ResourceModel.builder() - .name(CANARY_NAME) - .artifactS3Location("s3://cloudformation-created-bucket") - .code(codeObjectForTesting) - .executionRoleArn("arn:aws:test::myaccount") - .schedule(scheduleForTesting) - .runtimeVersion(runtimeVersion) - .startCanaryAfterCreation(startCanaryAfterCreation) - .build(); + .name(CANARY_NAME) + .artifactS3Location("s3://cloudformation-created-bucket") + .code(Pattern.compile("^syn-python-.*").matcher(runtimeVersion).matches() ? codeObjectForPython : codeObjectForNodeJS) + .executionRoleArn("arn:aws:test::myaccount") + .schedule(scheduleForTesting) + .runtimeVersion(runtimeVersion) + .startCanaryAfterCreation(startCanaryAfterCreation) + .build(); + } + + protected static CreateCanaryRequest buildCreateCanaryRequest(boolean includeOptionalFields, ResourceModel model) { + final CanaryCodeInput canaryCodeInput = CanaryCodeInput.builder() + .handler(model.getCode().getHandler()) + .s3Bucket(model.getCode().getS3Bucket()) + .s3Key(model.getCode().getS3Key()) + .s3Version(model.getCode().getS3ObjectVersion()) + .zipFile(model.getCode().getScript() != null ? ModelHelper.compressRawScript(model) : null) + .build(); + Long durationInSeconds = !Strings.isNullOrEmpty(model.getSchedule().getDurationInSeconds()) ? + Long.valueOf(model.getSchedule().getDurationInSeconds()) : null; + final CanaryScheduleInput canaryScheduleInput = CanaryScheduleInput.builder() + .expression(model.getSchedule().getExpression()) + .durationInSeconds(durationInSeconds) + .build(); + + if (includeOptionalFields) { + CanaryRunConfigInput canaryRunConfigInput = CanaryRunConfigInput.builder() + .timeoutInSeconds(model.getRunConfig().getTimeoutInSeconds()) + .memoryInMB(960) + .activeTracing(Boolean.TRUE.equals(model.getRunConfig().getActiveTracing())) + .environmentVariables(model.getRunConfig().getEnvironmentVariables()) + .build(); + + VpcConfigInput vpcConfigInput = null; + if (model.getVPCConfig() != null) { + vpcConfigInput = VpcConfigInput.builder() + .subnetIds(model.getVPCConfig().getSubnetIds()) + .securityGroupIds(model.getVPCConfig().getSecurityGroupIds()) + .build(); + } + return CreateCanaryRequest.builder() + .name(model.getName()) + .executionRoleArn(model.getExecutionRoleArn()) + .schedule(canaryScheduleInput) + .artifactS3Location(model.getArtifactS3Location()) + .runtimeVersion(model.getRuntimeVersion()) + .code(canaryCodeInput) + .tags(ModelHelper.buildTagInputMap(model)) + .vpcConfig(vpcConfigInput) + .failureRetentionPeriodInDays(model.getFailureRetentionPeriod()) + .successRetentionPeriodInDays(model.getSuccessRetentionPeriod()) + .runConfig(canaryRunConfigInput) + .artifactConfig(ModelHelper.getArtifactConfigInput(model.getArtifactConfig())) + .build(); + } + return CreateCanaryRequest.builder() + .name(model.getName()) + .executionRoleArn(model.getExecutionRoleArn()) + .schedule(canaryScheduleInput) + .artifactS3Location(model.getArtifactS3Location()) + .runtimeVersion(model.getRuntimeVersion()) + .code(canaryCodeInput) + .artifactConfig(ModelHelper.getArtifactConfigInput(model.getArtifactConfig())) + .build(); } + + public ResourceHandlerRequest buildResourceHandlerRequestWithName(String canaryName) { + final ResourceModel model = ResourceModel.builder().name(canaryName).build(); + + return ResourceHandlerRequest.builder() + .desiredResourceState(model) + .build(); + } + + } diff --git a/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/UpdateHandlerTest.java b/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/UpdateHandlerTest.java index e935359..182b8c8 100644 --- a/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/UpdateHandlerTest.java +++ b/aws-synthetics-canary/src/test/java/com/amazon/synthetics/canary/UpdateHandlerTest.java @@ -1,12 +1,27 @@ package com.amazon.synthetics.canary; -import org.apache.commons.collections.map.SingletonMap; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; -import software.amazon.awssdk.services.synthetics.model.*; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; +import software.amazon.awssdk.services.synthetics.model.ArtifactConfigOutput; +import software.amazon.awssdk.services.synthetics.model.Canary; +import software.amazon.awssdk.services.synthetics.model.CanaryRunConfigOutput; +import software.amazon.awssdk.services.synthetics.model.CanaryState; +import software.amazon.awssdk.services.synthetics.model.CanaryStatus; +import software.amazon.awssdk.services.synthetics.model.EncryptionMode; +import software.amazon.awssdk.services.synthetics.model.GetCanaryResponse; +import software.amazon.awssdk.services.synthetics.model.ResourceNotFoundException; +import software.amazon.awssdk.services.synthetics.model.S3EncryptionConfig; +import software.amazon.awssdk.services.synthetics.model.StartCanaryRequest; +import software.amazon.awssdk.services.synthetics.model.StopCanaryRequest; +import software.amazon.awssdk.services.synthetics.model.VisualReferenceOutput; import software.amazon.cloudformation.exceptions.CfnNotFoundException; -import software.amazon.cloudformation.proxy.*; +import software.amazon.cloudformation.proxy.HandlerErrorCode; +import software.amazon.cloudformation.proxy.OperationStatus; +import software.amazon.cloudformation.proxy.ProgressEvent; +import software.amazon.cloudformation.proxy.ResourceHandlerRequest; import java.util.ArrayList; import java.util.HashMap; @@ -119,6 +134,17 @@ public void handleRequest_inProgress_canaryStateIsReadyOrStopped_startCanaryAfte assertThat(response.getResourceModel()).isNotNull(); } + @ParameterizedTest + @EnumSource(value = CanaryState.class, names = { "READY", "STOPPED" }) + public void handleRequest_inProgress_canaryStateIsReadyOrStopped_startCanaryAfterCreationIsNull_returnsSuccess(CanaryState state) { + configureGetCanaryResponse(state); + + ProgressEvent response = handler.handleRequest( + proxy, REQUEST_NULL_START_CANARY, CallbackContext.builder().canaryUpdateStarted(true).build(), logger); + assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS); + assertThat(response.getResourceModel()).isNotNull(); + } + @ParameterizedTest @EnumSource(value = CanaryState.class, names = { "READY", "STOPPED" }) public void handleRequest_inProgress_canaryStateIsReadyOrStopped_startCanaryAfterCreationIsTrue_returnsInProgress(CanaryState state) { @@ -144,6 +170,17 @@ public void handleRequest_inProgress_canaryStateIsStarting_startCanaryAfterCreat assertThat(response.getResourceModel()).isNotNull(); } + @Test + public void handleRequest_inProgress_canaryStateIsStarting_startCanaryAfterCreationIsNull_returnsSuccess() { + configureGetCanaryResponse(CanaryState.STARTING); + + ProgressEvent response = handler.handleRequest( + proxy, REQUEST_NULL_START_CANARY, CallbackContext.builder().canaryUpdateStarted(true).build(), logger); + + assertThat(response.getStatus()).isEqualTo(OperationStatus.SUCCESS); + assertThat(response.getResourceModel()).isNotNull(); + } + @Test public void handleRequest_inProgress_canaryStateIsStarting_startCanaryAfterCreationIsTrue_returnsInProgress() { configureGetCanaryResponse(CanaryState.STARTING); @@ -168,13 +205,15 @@ public void handleRequest_inProgress_canaryStateIsRunning_initialStateIsRunning_ assertThat(response.getResourceModel()).isNotNull(); } - @Test - public void handleRequest_inProgress_canaryStateIsRunning_initialStateIsRunning_startCanaryAfterCreationIsFalse_stopsCanary() { + @ParameterizedTest + @NullSource + @ValueSource(booleans = {false}) + public void handleRequest_inProgress_canaryStateIsRunning_initialStateIsRunning_startCanaryAfterCreationIsFalseOrNull_stopsCanary(Boolean value) { configureGetCanaryResponse(CanaryState.RUNNING); - + ResourceHandlerRequest request = value == null ? REQUEST_NULL_START_CANARY : REQUEST; ProgressEvent response = handler.handleRequest( proxy, - REQUEST, + request, CallbackContext.builder().canaryUpdateStarted(true).initialCanaryState(CanaryState.RUNNING).build(), logger); assertThat(response.getStatus()).isEqualTo(OperationStatus.IN_PROGRESS); @@ -991,7 +1030,7 @@ public void handleRequest_updateArtifactEncryptionFromPresentToEmpty() { @Test public void handleRequest_updateArtifactEncryptionFromPresentToNull() { ResourceModel model = buildModel(); - + model.setArtifactConfig(null); final ResourceHandlerRequest request = ResourceHandlerRequest.builder() .desiredResourceState(model) .build();