From 7e0b4c9642a2d7232ea26e6f22765c172ce68d27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20V=C3=A1zquez?= Date: Tue, 6 Jul 2021 11:43:42 -0500 Subject: [PATCH] feat(cloudfoundry): Uses last operation state & description (#4138) * feat(cloudfoundry): Uses last operation state & description to surface that something went wrong. * feat(cloudfoundry): Adds null check. * feat(cloudfoundry): Adds default message. Co-authored-by: Zach Smith <33258732+zachsmith1@users.noreply.github.com> --- .../CloudFoundryWaitForDeployServiceTask.java | 38 +++++++++++++++ ...oundryWaitForServiceOperationTaskTest.java | 4 +- ...udFoundryWaitForDeployServiceTaskTest.java | 48 +++++++++++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) diff --git a/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/tasks/providers/cf/CloudFoundryWaitForDeployServiceTask.java b/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/tasks/providers/cf/CloudFoundryWaitForDeployServiceTask.java index ac967cef23..e723ca620b 100644 --- a/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/tasks/providers/cf/CloudFoundryWaitForDeployServiceTask.java +++ b/orca-clouddriver/src/main/java/com/netflix/spinnaker/orca/clouddriver/tasks/providers/cf/CloudFoundryWaitForDeployServiceTask.java @@ -16,11 +16,14 @@ package com.netflix.spinnaker.orca.clouddriver.tasks.providers.cf; +import com.netflix.spinnaker.orca.api.pipeline.TaskResult; import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionStatus; +import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution; import com.netflix.spinnaker.orca.clouddriver.OortService; import com.netflix.spinnaker.orca.clouddriver.tasks.servicebroker.AbstractWaitForServiceTask; import java.util.Map; import java.util.Optional; +import javax.annotation.Nonnull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -31,6 +34,41 @@ public CloudFoundryWaitForDeployServiceTask(OortService oortService) { super(oortService); } + @Nonnull + @Override + public TaskResult execute(@Nonnull StageExecution stage) { + String cloudProvider = getCloudProvider(stage); + String account = stage.mapTo("/service.account", String.class); + String region = stage.mapTo("/service.region", String.class); + String serviceInstanceName = stage.mapTo("/service.instance.name", String.class); + + Map serviceInstance = + oortService.getServiceInstance(account, cloudProvider, region, serviceInstanceName); + + ExecutionStatus status = oortStatusToTaskStatus(serviceInstance); + + TaskResult.TaskResultBuilder taskResultBuilder = TaskResult.builder(status); + Optional.ofNullable(serviceInstance) + .ifPresent( + (si) -> { + String lastOperationDescription = + Optional.ofNullable(serviceInstance.get("lastOperationDescription")) + .orElse("Failed to get last operation description") + .toString(); + taskResultBuilder + .output( + "lastOperationStatus", + Optional.ofNullable(serviceInstance.get("status")).orElse("").toString()) + .output("lastOperationDescription", lastOperationDescription); + if (status == ExecutionStatus.TERMINAL) { + taskResultBuilder.output("failureMessage", lastOperationDescription); + } + }); + + return taskResultBuilder.build(); + } + + @Override protected ExecutionStatus oortStatusToTaskStatus(Map m) { return Optional.ofNullable(m) .map( diff --git a/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/providers/cf/AbstractCloudFoundryWaitForServiceOperationTaskTest.java b/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/providers/cf/AbstractCloudFoundryWaitForServiceOperationTaskTest.java index 2f806c0277..13c9a2e1a8 100644 --- a/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/providers/cf/AbstractCloudFoundryWaitForServiceOperationTaskTest.java +++ b/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/providers/cf/AbstractCloudFoundryWaitForServiceOperationTaskTest.java @@ -34,8 +34,8 @@ import javax.annotation.Nullable; class AbstractCloudFoundryWaitForServiceOperationTaskTest { - private final String operationType; - private final Function subjectConstructor; + protected final String operationType; + protected final Function subjectConstructor; AbstractCloudFoundryWaitForServiceOperationTaskTest( String operationType, Function subjectConstructor) { diff --git a/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/providers/cf/CloudFoundryWaitForDeployServiceTaskTest.java b/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/providers/cf/CloudFoundryWaitForDeployServiceTaskTest.java index 19e4ab066b..0fcee854ad 100644 --- a/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/providers/cf/CloudFoundryWaitForDeployServiceTaskTest.java +++ b/orca-clouddriver/src/test/java/com/netflix/spinnaker/orca/clouddriver/tasks/providers/cf/CloudFoundryWaitForDeployServiceTaskTest.java @@ -16,8 +16,20 @@ package com.netflix.spinnaker.orca.clouddriver.tasks.providers.cf; +import static com.netflix.spinnaker.orca.api.pipeline.models.ExecutionType.PIPELINE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.matches; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.netflix.spinnaker.orca.api.pipeline.TaskResult; import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionStatus; +import com.netflix.spinnaker.orca.clouddriver.OortService; +import com.netflix.spinnaker.orca.pipeline.model.PipelineExecutionImpl; +import com.netflix.spinnaker.orca.pipeline.model.StageExecutionImpl; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import org.junit.jupiter.api.Test; class CloudFoundryWaitForDeployServiceTaskTest @@ -44,6 +56,42 @@ void isRunningWhenOortResultIsInProgress() { ExecutionStatus.RUNNING, Collections.singletonMap("status", "IN_PROGRESS")); } + @Test + void addsLastOperationStatusAndDescriptinoWhenOortResultIsFailed() { + OortService oortService = mock(OortService.class); + String credentials = "my-account"; + String cloudProvider = "cloud"; + String region = "org > space"; + String serviceInstanceName = "service-instance-name"; + when(oortService.getServiceInstance( + matches(credentials), + matches(cloudProvider), + matches(region), + matches(serviceInstanceName))) + .thenReturn( + Map.of( + "status", "FAILED", + "lastOperationDescription", "Custom description")); + + CloudFoundryWaitForDeployServiceTask task = subjectConstructor.apply(oortService); + + Map context = new HashMap<>(); + context.put("cloudProvider", cloudProvider); + context.put("service.account", credentials); + context.put("service.region", region); + context.put("service.instance.name", serviceInstanceName); + + TaskResult result = + task.execute( + new StageExecutionImpl( + new PipelineExecutionImpl(PIPELINE, "orca"), operationType, context)); + + assertThat(result.getStatus().toString()).isEqualTo(ExecutionStatus.TERMINAL.toString()); + assertThat(result.getOutputs().get("lastOperationStatus")).isEqualTo("FAILED"); + assertThat(result.getOutputs().get("lastOperationDescription")).isEqualTo("Custom description"); + assertThat(result.getOutputs().get("failureMessage")).isEqualTo("Custom description"); + } + @Test void isRunningWhenOortResultsAreEmpty() { testOortServiceStatus(ExecutionStatus.RUNNING, Collections.emptyMap());