diff --git a/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/instance/WaitForUpInstancesTask.groovy b/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/instance/WaitForUpInstancesTask.groovy index 1feade9f48..b367471dc5 100644 --- a/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/instance/WaitForUpInstancesTask.groovy +++ b/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/instance/WaitForUpInstancesTask.groovy @@ -16,6 +16,7 @@ package com.netflix.spinnaker.orca.clouddriver.tasks.instance +import java.util.concurrent.TimeUnit import com.netflix.spinnaker.orca.clouddriver.utils.HealthHelper import com.netflix.spinnaker.orca.clouddriver.utils.HealthHelper.HealthCountSnapshot import com.netflix.spinnaker.orca.pipeline.model.Stage @@ -23,8 +24,6 @@ import groovy.util.logging.Slf4j import org.slf4j.MDC import org.springframework.stereotype.Component -import java.util.concurrent.TimeUnit - @Component @Slf4j class WaitForUpInstancesTask extends AbstractWaitingForInstancesTask { @@ -120,7 +119,7 @@ class WaitForUpInstancesTask extends AbstractWaitingForInstancesTask { Integer targetDesiredSize if (useConfiguredCapacity(stage, currentCapacity)) { - targetDesiredSize = ((Map) stage.context.capacity).desired + targetDesiredSize = ((Map) stage.context.capacity).desired as Integer splainer.add("setting targetDesiredSize=${targetDesiredSize} from the configured stage context.capacity=${stage.context.capacity}") } else { targetDesiredSize = currentCapacity.desired as Integer @@ -158,7 +157,9 @@ class WaitForUpInstancesTask extends AbstractWaitingForInstancesTask { // targetDesired from the configured capacity. This relaxes the need for clouddriver onDemand // cache updates while resizing serverGroups. static boolean useConfiguredCapacity(Stage stage, Map current) { - Map configured = stage.context.getOrDefault("capacity", [:]) as Map + Map configured = + (stage.context.getOrDefault("capacity", [:])) + .collectEntries { k, v -> [(k): v as Integer] } as Map if (configured.desired == null) { return false diff --git a/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/servergroup/WaitForCapacityMatchTask.groovy b/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/servergroup/WaitForCapacityMatchTask.groovy index df0a276ad2..206a17bc20 100644 --- a/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/servergroup/WaitForCapacityMatchTask.groovy +++ b/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/servergroup/WaitForCapacityMatchTask.groovy @@ -52,7 +52,7 @@ class WaitForCapacityMatchTask extends AbstractInstancesCheckTask { Integer desired if (WaitForUpInstancesTask.useConfiguredCapacity(stage, serverGroup.capacity as Map)) { - desired = ((Map) stage.context.capacity).desired + desired = ((Map) stage.context.capacity).desired as Integer splainer.add("using desired from stage.context.capacity ($desired)") } else { desired = ((Map)serverGroup.capacity).desired diff --git a/orca-clouddriver/src/test/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/instance/WaitForUpInstancesTaskSpec.groovy b/orca-clouddriver/src/test/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/instance/WaitForUpInstancesTaskSpec.groovy index fdb7ae39cd..6c112b855f 100644 --- a/orca-clouddriver/src/test/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/instance/WaitForUpInstancesTaskSpec.groovy +++ b/orca-clouddriver/src/test/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/instance/WaitForUpInstancesTaskSpec.groovy @@ -16,6 +16,7 @@ package com.netflix.spinnaker.orca.clouddriver.tasks.instance +import java.util.concurrent.TimeUnit import com.netflix.spinnaker.orca.ExecutionStatus import com.netflix.spinnaker.orca.clouddriver.OortService import com.netflix.spinnaker.orca.jackson.OrcaObjectMapper @@ -27,9 +28,6 @@ import retrofit.mime.TypedString import spock.lang.Specification import spock.lang.Subject import spock.lang.Unroll - -import java.util.concurrent.TimeUnit - import static com.netflix.spinnaker.orca.test.model.ExecutionBuilder.stage class WaitForUpInstancesTaskSpec extends Specification { @@ -52,60 +50,60 @@ class WaitForUpInstancesTaskSpec extends Specification { def pipeline = Execution.newPipeline("orca") task.objectMapper = mapper def response = new Response('oort', 200, 'ok', [], new TypedString(mapper.writeValueAsString([ - name : "front50", - serverGroups: [ + name : "front50", + serverGroups: [ + [ + region : "us-east-1", + name : "front50-v001", + asg : [ + desiredCapacity: 1 + ], + capacity : [ + desired: 1 + ], + instances: [ [ - region : "us-east-1", - name : "front50-v001", - asg : [ - desiredCapacity: 1 - ], - capacity : [ - desired: 1 - ], - instances: [ - [ - health: [ [ state : "Up"] ] - ] - ] - ], + health: [[state: "Up"]] + ] + ] + ], + [ + region : "us-west-1", + name : "front50-v001", + asg : [ + desiredCapacity: 1 + ], + capacity : [ + desired: 1 + ], + instances: [ [ - region : "us-west-1", - name : "front50-v001", - asg : [ - desiredCapacity: 1 - ], - capacity : [ - desired: 1 - ], - instances: [ - [ - health: [ [ state : "Down"] ] - ] - ] + health: [[state: "Down"]] ] ] - ]))) + ] + ] + ]))) def response2 = new Response('oort', 200, 'ok', [], new TypedString(mapper.writeValueAsString([ - name : "front50", - serverGroups: [ - [ - region : "us-west-1", - name : "front50-v001", - asg : [ - desiredCapacity: 1 - ], - capacity : [ - desired: 1 - ], - instances: [ - [ - health: [ [ state : "Up"] ] - ] - ] - ] + name : "front50", + serverGroups: [ + [ + region : "us-west-1", + name : "front50-v001", + asg : [ + desiredCapacity: 1 + ], + capacity : [ + desired: 1 + ], + instances: [ + [ + health: [[state: "Up"]] ] - ]))) + ] + ] + ] + ]))) task.oortService = Stub(OortService) { getCluster(*_) >>> [response, response2] } @@ -131,13 +129,13 @@ class WaitForUpInstancesTaskSpec extends Specification { @Unroll void 'should return false for hasSucceeded when server group is #serverGroup'() { setup: - def instances = [ [ health: [ [state: 'Up'] ] ] ] + def instances = [[health: [[state: 'Up']]]] expect: !task.hasSucceeded(new Stage(Execution.newPipeline("orca"), "", "", [:]), serverGroup, instances, null) where: - serverGroup << [null, [:], [asg: [], capacity : [],]] + serverGroup << [null, [:], [asg: [], capacity: [],]] } @@ -146,13 +144,13 @@ class WaitForUpInstancesTaskSpec extends Specification { expect: def instances = [] (1..healthy).each { - instances << [ health: [ [state: 'Up'] ] ] + instances << [health: [[state: 'Up']]] } def serverGroup = [ - asg: [ + asg : [ desiredCapacity: total ], - capacity : [ + capacity: [ desired: total ] ] @@ -192,66 +190,66 @@ class WaitForUpInstancesTaskSpec extends Specification { expect: def instances = [] (1..healthy).each { - instances << [ health: [ [state: 'Up'] ] ] + instances << [health: [[state: 'Up']]] } def serverGroup = [ - asg: [ - desiredCapacity: total - ], - capacity : [ - min: min, - desired: total, - max: max - ] + asg : [ + desiredCapacity: total + ], + capacity: [ + min : min, + desired: total, + max : max + ] ] hasSucceeded == task.hasSucceeded( new Stage(Execution.newPipeline("orca"), "", "", [ - desiredPercentage: percent - ] - ), serverGroup, instances, null + desiredPercentage: percent + ] + ), serverGroup, instances, null ) where: - percent | healthy | total | min | max || hasSucceeded + percent | healthy | total | min | max || hasSucceeded // 100 percent - 100 | 1 | 1 | 1 | 1 || true - 100 | 0 | 0 | 0 | 0 || true - 100 | 2 | 2 | 0 | 2 || true + 100 | 1 | 1 | 1 | 1 || true + 100 | 0 | 0 | 0 | 0 || true + 100 | 2 | 2 | 0 | 2 || true // zero percent (should always return true) - 0 | 1 | 2 | 1 | 2 || true - 0 | 0 | 100 | 1 | 100 || true + 0 | 1 | 2 | 1 | 2 || true + 0 | 0 | 100 | 1 | 100 || true // >= checks - 89 | 9 | 10 | 10 | 10 || true - 90 | 9 | 10 | 10 | 10 || true - 90 | 8 | 10 | 10 | 10 || false - 91 | 9 | 10 | 10 | 10 || false + 89 | 9 | 10 | 10 | 10 || true + 90 | 9 | 10 | 10 | 10 || true + 90 | 8 | 10 | 10 | 10 || false + 91 | 9 | 10 | 10 | 10 || false // verify ceiling - 90 | 10 | 11 | 10 | 11 || true - 90 | 8 | 9 | 9 | 9 || false + 90 | 10 | 11 | 10 | 11 || true + 90 | 8 | 9 | 9 | 9 || false } void 'should succeed when ASG desired size is reached, even though snapshotCapacity is larger'() { when: def serverGroup = [ - asg: [ + asg : [ desiredCapacity: 2 ], - capacity : [ + capacity: [ desired: 2 ] ] def context = [ - snapshotCapacity: [ desiredCapacity: 5 ] + snapshotCapacity: [desiredCapacity: 5] ] def instances = [ - [ health: [ [state: 'Up'] ] ], - [ health: [ [state: 'Up'] ] ] + [health: [[state: 'Up']]], + [health: [[state: 'Up']]] ] then: @@ -264,10 +262,10 @@ class WaitForUpInstancesTaskSpec extends Specification { void 'should succeed when spotPrice is set and the deployment strategy is None, even if no instances are up'() { when: def serverGroup = [ - asg: [ + asg : [ desiredCapacity: 2 ], - capacity : [ + capacity : [ desired: 2 ], launchConfig: [ @@ -275,7 +273,7 @@ class WaitForUpInstancesTaskSpec extends Specification { ] ] def context = [ - capacity: [ desired: 5 ], + capacity: [desired: 5], strategy: '' ] @@ -293,27 +291,27 @@ class WaitForUpInstancesTaskSpec extends Specification { void 'should return #result for #healthy instances when #description'() { when: def serverGroup = [ - asg: [ + asg : [ desiredCapacity: asg ], - capacity : [ + capacity: [ desired: asg ] ] def context = [ - source: [ useSourceCapacity: useSource ], + source: [useSourceCapacity: useSource], ] if (configured) { - context.capacity = [ desired: configured ] + context.capacity = [desired: configured] } if (snapshot) { - context.capacitySnapshot = [ desiredCapacity: snapshot ] + context.capacitySnapshot = [desiredCapacity: snapshot] } def instances = [] (1..healthy).each { - instances << [ health: [ [state: 'Up'] ] ] + instances << [health: [[state: 'Up']]] } then: @@ -380,17 +378,24 @@ class WaitForUpInstancesTaskSpec extends Specification { false || null | 2 | [min: 3, max: 3, desired: 3] | null // configured is used if present and min == max == desired true || null | 2 | [min: 3, max: 3, desired: 3] | [min: 2, max: 2, desired: 2] + true || null | 2 | [min: 3, max: 3, desired: 3] | [min: "2", max: "2", desired: "2"] // configured is used if current allows autoscaling but configured doesn't true || null | 2 | [min: 3, max: 3, desired: 3] | [min: 2, max: 500, desired: 2] + true || null | 2 | [min: 3, max: 3, desired: 3] | [min: "2", max: "500", desired: "2"] true || null | 2 | [min: 5, max: 5, desired: 5] | [min: 1, max: 5, desired: 2] + true || null | 2 | [min: 5, max: 5, desired: 5] | [min: "1", max: "5", desired: "2"] // useSourceCapacity with a snapshot is used over configured and current true || 3 | 3 | [min: 5, max: 5, desired: 5] | [min: 5, max: 5, desired: 5] + true || 3 | 3 | [min: 5, max: 5, desired: 5] | [min: "5", max: "5", desired: "5"] true || 3 | 3 | [min: 5, max: 5, desired: 5] | null false || 4 | 3 | [min: 5, max: 5, desired: 5] | null // sourceCapacity is ignored if > than the calculated target due to a scale down corner case true || 4 | 3 | [min: 3, max: 3, desired: 3] | null false || 4 | 3 | [min: 3, max: 3, desired: 3] | [min: 4, max: 4, desired: 4] + false || 4 | 3 | [min: 3, max: 3, desired: 3] | [min: "4", max: "4", desired: "4"] true || 5 | 4 | [min: 3, max: 3, desired: 3] | [min: 4, max: 4, desired: 4] + true || 5 | 4 | [min: 3, max: 3, desired: 3] | [min: "4", max: "4", desired: "4"] + true || 5 | 4 | [min: 3, max: 3, desired: 3] | [min: 4, max: 4, desired: "4"] } @Unroll @@ -414,12 +419,12 @@ class WaitForUpInstancesTaskSpec extends Specification { void 'should not trust an ASG desired capacity of zero unless zero has been seen 12 times'() { expect: def context = [ - zeroDesiredCapacityCount: counter, - capacitySnapshot: [ - desiredCapacity: snapshotCapacity - ] + zeroDesiredCapacityCount: counter, + capacitySnapshot : [ + desiredCapacity: snapshotCapacity + ] ] - def serverGroup = [asg: [desiredCapacity: 0], capacity : [desired: 0]] + def serverGroup = [asg: [desiredCapacity: 0], capacity: [desired: 0]] hasSucceeded == task.hasSucceeded(new Stage(Execution.newPipeline("orca"), "", "", context), serverGroup, [], null) where: @@ -442,89 +447,89 @@ class WaitForUpInstancesTaskSpec extends Specification { hasSucceeded || desiredCapacity | healthProviderNames | instances true || 0 | null | [] true || 0 | ['a'] | [] - true || "1" | ['a'] | [ [ health: [ [ type: 'a', state : "Up"] ] ] ] - false || 1 | null | [ [ health: [ [ type: 'a', state : "Down"] ] ] ] - true || 1 | null | [ [ health: [ [ type: 'a', state : "Up"] ] ] ] - false || 1 | ['a'] | [ [ health: [ [ type: 'a', state : "Down"] ] ] ] - true || 1 | ['a'] | [ [ health: [ [ type: 'a', state : "Up"] ] ] ] - false || 1 | ['b'] | [ [ health: [ [ type: 'a', state : "Down"] ] ] ] - false || 1 | ['b'] | [ [ health: [ [ type: 'a', state : "Up"] ] ] ] - true || 1 | ['Amazon'] | [ [ health: [ [ type: 'Amazon', healthClass: 'platform', state: "Unknown"] ] ] ] - false || 1 | ['Amazon'] | [ [ health: [ [ type: 'Amazon', state: "Down"] ] ] ] - true || 1 | ['GCE'] | [ [ health: [ [ type: 'GCE', healthClass: 'platform', state: "Unknown"] ] ] ] - false || 1 | ['GCE'] | [ [ health: [ [ type: 'GCE', state: "Down"] ] ] ] + true || "1" | ['a'] | [[health: [[type: 'a', state: "Up"]]]] + false || 1 | null | [[health: [[type: 'a', state: "Down"]]]] + true || 1 | null | [[health: [[type: 'a', state: "Up"]]]] + false || 1 | ['a'] | [[health: [[type: 'a', state: "Down"]]]] + true || 1 | ['a'] | [[health: [[type: 'a', state: "Up"]]]] + false || 1 | ['b'] | [[health: [[type: 'a', state: "Down"]]]] + false || 1 | ['b'] | [[health: [[type: 'a', state: "Up"]]]] + true || 1 | ['Amazon'] | [[health: [[type: 'Amazon', healthClass: 'platform', state: "Unknown"]]]] + false || 1 | ['Amazon'] | [[health: [[type: 'Amazon', state: "Down"]]]] + true || 1 | ['GCE'] | [[health: [[type: 'GCE', healthClass: 'platform', state: "Unknown"]]]] + false || 1 | ['GCE'] | [[health: [[type: 'GCE', state: "Down"]]]] // multiple health providers - true || 1 | ['Amazon'] | [ [ health: [ [ type: 'Amazon', healthClass: 'platform', state: "Unknown"], [ type: 'b', state : "Down"] ] ] ] - true || 1 | ['GCE'] | [ [ health: [ [ type: 'GCE', healthClass: 'platform', state: "Unknown"], [ type: 'b', state : "Down"] ] ] ] - true || 1 | null | [ [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Up"] ] ] ] - false || 1 | null | [ [ health: [ [ type: 'a', state : "Down"], [ type: 'b', state : "Down"] ] ] ] - false || 1 | null | [ [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Down"] ] ] ] - true || 1 | ['a'] | [ [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Down"] ] ] ] - false || 1 | ['b'] | [ [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Down"] ] ] ] - false || 1 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Down"] ] ] ] - true || 1 | ['a'] | [ [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Unknown"] ] ] ] - false || 1 | ['b'] | [ [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Unknown"] ] ] ] - true || 1 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Unknown"] ] ] ] - false || 1 | ['a'] | [ [ health: [ [ type: 'a', state : "Unknown"], [ type: 'b', state : "Down"] ] ] ] - false || 1 | ['b'] | [ [ health: [ [ type: 'a', state : "Unknown"], [ type: 'b', state : "Down"] ] ] ] - false || 1 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Unknown"], [ type: 'b', state : "Down"] ] ] ] + true || 1 | ['Amazon'] | [[health: [[type: 'Amazon', healthClass: 'platform', state: "Unknown"], [type: 'b', state: "Down"]]]] + true || 1 | ['GCE'] | [[health: [[type: 'GCE', healthClass: 'platform', state: "Unknown"], [type: 'b', state: "Down"]]]] + true || 1 | null | [[health: [[type: 'a', state: "Up"], [type: 'b', state: "Up"]]]] + false || 1 | null | [[health: [[type: 'a', state: "Down"], [type: 'b', state: "Down"]]]] + false || 1 | null | [[health: [[type: 'a', state: "Up"], [type: 'b', state: "Down"]]]] + true || 1 | ['a'] | [[health: [[type: 'a', state: "Up"], [type: 'b', state: "Down"]]]] + false || 1 | ['b'] | [[health: [[type: 'a', state: "Up"], [type: 'b', state: "Down"]]]] + false || 1 | ['a', 'b'] | [[health: [[type: 'a', state: "Up"], [type: 'b', state: "Down"]]]] + true || 1 | ['a'] | [[health: [[type: 'a', state: "Up"], [type: 'b', state: "Unknown"]]]] + false || 1 | ['b'] | [[health: [[type: 'a', state: "Up"], [type: 'b', state: "Unknown"]]]] + true || 1 | ['a', 'b'] | [[health: [[type: 'a', state: "Up"], [type: 'b', state: "Unknown"]]]] + false || 1 | ['a'] | [[health: [[type: 'a', state: "Unknown"], [type: 'b', state: "Down"]]]] + false || 1 | ['b'] | [[health: [[type: 'a', state: "Unknown"], [type: 'b', state: "Down"]]]] + false || 1 | ['a', 'b'] | [[health: [[type: 'a', state: "Unknown"], [type: 'b', state: "Down"]]]] // multiple instances - true || 2 | null | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Up"] ] ] ] - false || 2 | null | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Down"] ] ] ] - false || 2 | null | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Down"] ] ] ] - true || 2 | null | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'b', state : "Up"] ] ] ] - false || 2 | null | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'b', state : "Down"] ] ] ] - false || 2 | null | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'b', state : "Down"] ] ] ] - true || 2 | ['a'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Up"] ] ] ] - false || 2 | ['a'] | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Down"] ] ] ] - false || 2 | ['a'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Down"] ] ] ] - false || 2 | ['a'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'b', state : "Up"] ] ] ] - false || 2 | ['a'] | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'b', state : "Down"] ] ] ] - false || 2 | ['a'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'b', state : "Down"] ] ] ] - true || 2 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Up"] ] ] ] - false || 2 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Down"] ] ] ] - false || 2 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Down"] ] ] ] - true || 2 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'b', state : "Up"] ] ] ] - false || 2 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'b', state : "Down"] ] ] ] - false || 2 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'b', state : "Down"] ] ] ] + true || 2 | null | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Up"]]]] + false || 2 | null | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Down"]]]] + false || 2 | null | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Down"]]]] + true || 2 | null | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'b', state: "Up"]]]] + false || 2 | null | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'b', state: "Down"]]]] + false || 2 | null | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'b', state: "Down"]]]] + true || 2 | ['a'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Up"]]]] + false || 2 | ['a'] | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Down"]]]] + false || 2 | ['a'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Down"]]]] + false || 2 | ['a'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'b', state: "Up"]]]] + false || 2 | ['a'] | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'b', state: "Down"]]]] + false || 2 | ['a'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'b', state: "Down"]]]] + true || 2 | ['a', 'b'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Up"]]]] + false || 2 | ['a', 'b'] | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Down"]]]] + false || 2 | ['a', 'b'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Down"]]]] + true || 2 | ['a', 'b'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'b', state: "Up"]]]] + false || 2 | ['a', 'b'] | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'b', state: "Down"]]]] + false || 2 | ['a', 'b'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'b', state: "Down"]]]] // multiple instances with multiple health providers - true || 2 | null | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Up"] ] ] ] - false || 2 | null | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Down"] ] ] ] - true || 2 | null | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Unknown"] ] ] ] - false || 2 | null | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Down"], [ type: 'b', state : "Down"] ] ] ] - false || 2 | null | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Down"], [ type: 'b', state : "Unknown"] ] ] ] - false || 2 | null | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Up"] ] ] ] - false || 2 | null | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Down"] ] ] ] - false || 2 | null | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Unknown"] ] ] ] - false || 2 | null | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Down"], [ type: 'b', state : "Down"] ] ] ] - false || 2 | null | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Down"], [ type: 'b', state : "Unknown"] ] ] ] - true || 2 | ['a'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Up"] ] ] ] - true || 2 | ['a'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Down"] ] ] ] - true || 2 | ['a'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Unknown"] ] ] ] - false || 2 | ['a'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Down"], [ type: 'b', state : "Down"] ] ] ] - false || 2 | ['a'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Down"], [ type: 'b', state : "Unknown"] ] ] ] - false || 2 | ['a'] | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Up"] ] ] ] - false || 2 | ['a'] | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Down"] ] ] ] - false || 2 | ['a'] | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Unknown"] ] ] ] - false || 2 | ['a'] | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Down"], [ type: 'b', state : "Down"] ] ] ] - false || 2 | ['a'] | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Down"], [ type: 'b', state : "Unknown"] ] ] ] - true || 2 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Up"] ] ] ] - false || 2 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Down"] ] ] ] - true || 2 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Unknown"] ] ] ] - false || 2 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Down"], [ type: 'b', state : "Down"] ] ] ] - false || 2 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Up"] ] ], [ health: [ [ type: 'a', state : "Down"], [ type: 'b', state : "Unknown"] ] ] ] - false || 2 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Up"] ] ] ] - false || 2 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Down"] ] ] ] - false || 2 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Up"], [ type: 'b', state : "Unknown"] ] ] ] - false || 2 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Down"], [ type: 'b', state : "Down"] ] ] ] - false || 2 | ['a', 'b'] | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Down"], [ type: 'b', state : "Unknown"] ] ] ] + true || 2 | null | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Up"]]]] + false || 2 | null | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Down"]]]] + true || 2 | null | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Unknown"]]]] + false || 2 | null | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Down"], [type: 'b', state: "Down"]]]] + false || 2 | null | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Down"], [type: 'b', state: "Unknown"]]]] + false || 2 | null | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Up"]]]] + false || 2 | null | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Down"]]]] + false || 2 | null | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Unknown"]]]] + false || 2 | null | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Down"], [type: 'b', state: "Down"]]]] + false || 2 | null | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Down"], [type: 'b', state: "Unknown"]]]] + true || 2 | ['a'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Up"]]]] + true || 2 | ['a'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Down"]]]] + true || 2 | ['a'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Unknown"]]]] + false || 2 | ['a'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Down"], [type: 'b', state: "Down"]]]] + false || 2 | ['a'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Down"], [type: 'b', state: "Unknown"]]]] + false || 2 | ['a'] | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Up"]]]] + false || 2 | ['a'] | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Down"]]]] + false || 2 | ['a'] | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Unknown"]]]] + false || 2 | ['a'] | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Down"], [type: 'b', state: "Down"]]]] + false || 2 | ['a'] | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Down"], [type: 'b', state: "Unknown"]]]] + true || 2 | ['a', 'b'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Up"]]]] + false || 2 | ['a', 'b'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Down"]]]] + true || 2 | ['a', 'b'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Unknown"]]]] + false || 2 | ['a', 'b'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Down"], [type: 'b', state: "Down"]]]] + false || 2 | ['a', 'b'] | [[health: [[type: 'a', state: "Up"]]], [health: [[type: 'a', state: "Down"], [type: 'b', state: "Unknown"]]]] + false || 2 | ['a', 'b'] | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Up"]]]] + false || 2 | ['a', 'b'] | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Down"]]]] + false || 2 | ['a', 'b'] | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Up"], [type: 'b', state: "Unknown"]]]] + false || 2 | ['a', 'b'] | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Down"], [type: 'b', state: "Down"]]]] + false || 2 | ['a', 'b'] | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Down"], [type: 'b', state: "Unknown"]]]] // health providers ignored - true || 2 | [] | [ [ health: [ [ type: 'a', state : "Down"] ] ], [ health: [ [ type: 'a', state : "Down"], [ type: 'b', state : "Unknown"] ] ] ] - false || 2 | [] | [ [ health: [ [ type: 'a', state : "Down"], [ type: 'b', state : "Unknown"] ] ] ] + true || 2 | [] | [[health: [[type: 'a', state: "Down"]]], [health: [[type: 'a', state: "Down"], [type: 'b', state: "Unknown"]]]] + false || 2 | [] | [[health: [[type: 'a', state: "Down"], [type: 'b', state: "Unknown"]]]] } @Unroll @@ -532,7 +537,7 @@ class WaitForUpInstancesTaskSpec extends Specification { given: def stage = stage { context = [ - "kato.tasks": katoTasks + "kato.tasks": katoTasks ] } @@ -547,15 +552,15 @@ class WaitForUpInstancesTaskSpec extends Specification { [] || null [[:]] || null [ - [resultObjects: [[deployments: [ - deployment("app-v001", "us-west-2", 0, 1, 1), - deployment("app-v002", "us-west-2", 0, 2, 2), - deployment("app-v001", "us-east-1", 0, 3, 3), - ]]]] + [resultObjects: [[deployments: [ + deployment("app-v001", "us-west-2", 0, 1, 1), + deployment("app-v002", "us-west-2", 0, 2, 2), + deployment("app-v001", "us-east-1", 0, 3, 3), + ]]]] ] || [min: 0, max: 1, desired: 1] // should match on serverGroupName and location [ - [resultObjects: [[deployments: [deployment("app-v001", "us-west-2", 0, 1, 1)]]]], - [resultObjects: [[deployments: [deployment("app-v001", "us-west-2", 0, 2, 2)]]]], + [resultObjects: [[deployments: [deployment("app-v001", "us-west-2", 0, 1, 1)]]]], + [resultObjects: [[deployments: [deployment("app-v001", "us-west-2", 0, 2, 2)]]]], ] || [min: 0, max: 2, desired: 2] // should look for most recent katoTask result object } @@ -564,7 +569,7 @@ class WaitForUpInstancesTaskSpec extends Specification { given: def stage = stage { context = [ - "kato.tasks": katoTasks + "kato.tasks": katoTasks ] } @@ -587,7 +592,7 @@ class WaitForUpInstancesTaskSpec extends Specification { static Map deployment(String serverGroupName, String location, int min, int max, int desired) { return [ - serverGroupName: serverGroupName, location: location, capacity: [min: min, max: max, desired: desired] + serverGroupName: serverGroupName, location: location, capacity: [min: min, max: max, desired: desired] ] } diff --git a/orca-clouddriver/src/test/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/servergroup/WaitForCapacityMatchTaskSpec.groovy b/orca-clouddriver/src/test/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/servergroup/WaitForCapacityMatchTaskSpec.groovy index 99b2fe5271..d022c81064 100644 --- a/orca-clouddriver/src/test/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/servergroup/WaitForCapacityMatchTaskSpec.groovy +++ b/orca-clouddriver/src/test/groovy/com/netflix/spinnaker/orca/clouddriver/tasks/servergroup/WaitForCapacityMatchTaskSpec.groovy @@ -217,12 +217,17 @@ class WaitForCapacityMatchTaskSpec extends Specification { false || 5 | [min: 3, max: 3, desired: 3] | null true || 3 | [min: 3, max: 3, desired: 3] | null false || 5 | [min: 10, max: 10, desired: 10] | [min: 3, max: 3, desired: 3] + false || 5 | [min: 10, max: 10, desired: 10] | [min: "3", max: "3", desired: "3"] true || 5 | [min: 10, max: 10, desired: 10] | [min: 5, max: 5, desired: 5] + true || 5 | [min: 10, max: 10, desired: 10] | [min: "5", max: "5", desired: "5"] // scale up false || 5 | [min: 5, max: 5, desired: 5] | [min: 10, max: 10, desired: 10] + false || 5 | [min: 5, max: 5, desired: 5] | [min: "10", max: "10", desired: "10"] true || 3 | [min: 1, max: 1, desired: 1] | [min: 3, max: 3, desired: 3] + true || 3 | [min: 1, max: 1, desired: 1] | [min: "3", max: "3", desired: "3"] // asg value is used when autoscaling true || 4 | [min: 3, max: 10, desired: 4] | [min: 1, max: 50, desired: 5] + true || 4 | [min: 3, max: 10, desired: 4] | [min: "1", max: "50", desired: "5"] } private static Map makeInstance(id, healthState = 'Up') {