fix(ecs): check for already suspended autoscaling on disable service op
MODIFY
clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/deploy/ops/DisableServiceAtomicOperation.java
clouddriver-ecs/src/main/java/com/netflix/spinnaker/clouddriver/ecs/deploy/ops/DisableServiceAtomicOperation.java
package com.netflix.spinnaker.clouddriver.ecs.deploy.ops;
import com.amazonaws.services.applicationautoscaling.AWSApplicationAutoScaling;
-import com.amazonaws.services.applicationautoscaling.model.RegisterScalableTargetRequest;
-import com.amazonaws.services.applicationautoscaling.model.ScalableDimension;
-import com.amazonaws.services.applicationautoscaling.model.ServiceNamespace;
-import com.amazonaws.services.applicationautoscaling.model.SuspendedState;
+import com.amazonaws.services.applicationautoscaling.model.*;
import com.amazonaws.services.ecs.AmazonECS;
import com.amazonaws.services.ecs.model.UpdateServiceRequest;
import com.netflix.spinnaker.clouddriver.ecs.deploy.description.ModifyServiceDescription;
@@ -48,6 +45,19 @@ public class DisableServiceAtomicOperation
String account = description.getAccount();
String cluster = getCluster(service, account);
+ DescribeScalableTargetsRequest describeRequest =
+ new DescribeScalableTargetsRequest()
+ .withServiceNamespace(ServiceNamespace.Ecs)
+ .withResourceIds(String.format("service/%s/%s", cluster, service))
+ .withScalableDimension(ScalableDimension.EcsServiceDesiredCount);
+ DescribeScalableTargetsResult describeResult =
+ autoScalingClient.describeScalableTargets(describeRequest);
+
+ if (isSuspended(describeResult)) {
+ updateTaskStatus(
+ String.format(
+ "Autoscaling already suspended on server group %s for %s.", service, account));
+ } else {
updateTaskStatus(
String.format("Suspending autoscaling on %s server group for %s.", service, account));
RegisterScalableTargetRequest suspendRequest =
@@ -63,6 +73,7 @@ public class DisableServiceAtomicOperation
autoScalingClient.registerScalableTarget(suspendRequest);
updateTaskStatus(
String.format("Autoscaling on server group %s suspended for %s.", service, account));
+ }
updateTaskStatus(String.format("Disabling %s server group for %s.", service, account));
UpdateServiceRequest request =
@@ -70,4 +81,26 @@ public class DisableServiceAtomicOperation
ecs.updateService(request);
updateTaskStatus(String.format("Server group %s disabled for %s.", service, account));
}
+
+ private boolean isSuspended(DescribeScalableTargetsResult describeResult) {
+ if (describeResult != null
+ && describeResult.getScalableTargets() != null
+ && describeResult.getScalableTargets().size() > 0) {
+ ScalableTarget target =
+ describeResult.getScalableTargets().stream()
+ .filter(
+ e ->
+ (e.getScalableDimension()
+ .equals(ScalableDimension.EcsServiceDesiredCount.toString())))
+ .findFirst()
+ .orElse(null);
+
+ return (target != null)
+ && target.getSuspendedState().getScheduledScalingSuspended()
+ && target.getSuspendedState().getDynamicScalingInSuspended()
+ && target.getSuspendedState().getDynamicScalingOutSuspended();
+ }
+
+ return false;
+ }
}

MODIFY
clouddriver-ecs/src/test/groovy/com/netflix/spinnaker/clouddriver/ecs/deploy/ops/DisableServiceAtomicOperationSpec.groovy
clouddriver-ecs/src/test/groovy/com/netflix/spinnaker/clouddriver/ecs/deploy/ops/DisableServiceAtomicOperationSpec.groovy
package com.netflix.spinnaker.clouddriver.ecs.deploy.ops
+import com.amazonaws.services.applicationautoscaling.model.DescribeScalableTargetsResult
+import com.amazonaws.services.applicationautoscaling.model.ScalableDimension
+import com.amazonaws.services.applicationautoscaling.model.ScalableTarget
+import com.amazonaws.services.applicationautoscaling.model.SuspendedState
import com.netflix.spinnaker.clouddriver.ecs.TestCredential
import com.netflix.spinnaker.clouddriver.ecs.deploy.description.ModifyServiceDescription
@@ -40,7 +44,45 @@ class DisableServiceAtomicOperationSpec extends CommonAtomicOperation {
operation.operate([])
then:
+ 1 * autoscaling.describeScalableTargets(_) >> new DescribeScalableTargetsResult()
+ .withScalableTargets(new ScalableTarget()
+ .withScalableDimension(ScalableDimension.EcsServiceDesiredCount)
+ .withSuspendedState(new SuspendedState()
+ .withScheduledScalingSuspended(false)
+ .withDynamicScalingInSuspended(false)
+ .withDynamicScalingOutSuspended(false)))
1 * autoscaling.registerScalableTarget(_)
1 * ecs.updateService(_)
}
+
+ void 'should not suspend autoscaling if it is already suspended'() {
+ given:
+ def operation = new DisableServiceAtomicOperation(new ModifyServiceDescription(
+ serverGroupName: "test-server-group",
+ credentials: TestCredential.named('Test', [:])
+ ))
+
+ operation.amazonClientProvider = amazonClientProvider
+ operation.credentialsRepository = credentialsRepository
+ operation.containerInformationService = containerInformationService
+
+ amazonClientProvider.getAmazonEcs(_, _, _) >> ecs
+ amazonClientProvider.getAmazonApplicationAutoScaling(_, _, _) >> autoscaling
+ containerInformationService.getClusterName(_, _, _) >> 'cluster-name'
+ credentialsRepository.getOne(_) >> TestCredential.named("test")
+
+ when:
+ operation.operate([])
+
+ then:
+ 1 * autoscaling.describeScalableTargets(_) >> new DescribeScalableTargetsResult()
+ .withScalableTargets(new ScalableTarget()
+ .withScalableDimension(ScalableDimension.EcsServiceDesiredCount)
+ .withSuspendedState(new SuspendedState()
+ .withScheduledScalingSuspended(true)
+ .withDynamicScalingInSuspended(true)
+ .withDynamicScalingOutSuspended(true)))
+ 0 * autoscaling.registerScalableTarget(_)
+ 1 * ecs.updateService(_)
+ }
}

fix(ecs): check for already suspended autoscaling on disable service op (#5218)