diff options
author | Jon Marius Venstad <jonmv@users.noreply.github.com> | 2019-12-02 09:30:22 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-12-02 09:30:22 +0100 |
commit | e5bb149b5a59fb3979ddfc9a4a11397147798626 (patch) | |
tree | 086de6d2c5c86f37501655abf18d89bbd6ccfd9a | |
parent | e346de03f3ff1b37e9b7ce94399492fdebcd5ac6 (diff) | |
parent | 7adc4051326cb29a2dfcfa4e034451aae8ee8118 (diff) |
Merge pull request #11461 from vespa-engine/jvenstad/test-steps-in-deployment-spec
Jvenstad/test steps in deployment spec
18 files changed, 813 insertions, 219 deletions
diff --git a/config-model-api/abi-spec.json b/config-model-api/abi-spec.json index edf83fe4475..d8482499a93 100644 --- a/config-model-api/abi-spec.json +++ b/config-model-api/abi-spec.json @@ -188,7 +188,7 @@ "fields": [] }, "com.yahoo.config.application.api.DeploymentInstanceSpec": { - "superClass": "com.yahoo.config.application.api.DeploymentSpec$Step", + "superClass": "com.yahoo.config.application.api.DeploymentSpec$Steps", "interfaces": [], "attributes": [ "public" @@ -196,20 +196,16 @@ "methods": [ "public void <init>(com.yahoo.config.provision.InstanceName, java.util.List, com.yahoo.config.application.api.DeploymentSpec$UpgradePolicy, java.util.List, java.util.Optional, java.util.Optional, java.util.Optional, com.yahoo.config.application.api.Notifications, java.util.List)", "public com.yahoo.config.provision.InstanceName name()", - "public java.time.Duration delay()", - "public java.util.List steps()", "public com.yahoo.config.application.api.DeploymentSpec$UpgradePolicy upgradePolicy()", "public java.util.List changeBlocker()", "public java.util.Optional globalServiceId()", "public boolean canUpgradeAt(java.time.Instant)", "public boolean canChangeRevisionAt(java.time.Instant)", - "public java.util.List zones()", - "public boolean deploysTo(com.yahoo.config.provision.Environment, java.util.Optional)", "public java.util.Optional athenzDomain()", "public java.util.Optional athenzService(com.yahoo.config.provision.Environment, com.yahoo.config.provision.RegionName)", "public com.yahoo.config.application.api.Notifications notifications()", "public java.util.List endpoints()", - "public boolean includes(com.yahoo.config.provision.Environment, java.util.Optional)", + "public boolean deploysTo(com.yahoo.config.provision.Environment, com.yahoo.config.provision.RegionName)", "public boolean equals(java.lang.Object)", "public int hashCode()", "public java.lang.String toString()" @@ -231,6 +227,23 @@ ], "fields": [] }, + "com.yahoo.config.application.api.DeploymentSpec$DeclaredTest": { + "superClass": "com.yahoo.config.application.api.DeploymentSpec$Step", + "interfaces": [], + "attributes": [ + "public" + ], + "methods": [ + "public void <init>(com.yahoo.config.provision.RegionName)", + "public boolean concerns(com.yahoo.config.provision.Environment, java.util.Optional)", + "public boolean isTest()", + "public com.yahoo.config.provision.RegionName region()", + "public boolean equals(java.lang.Object)", + "public int hashCode()", + "public java.lang.String toString()" + ], + "fields": [] + }, "com.yahoo.config.application.api.DeploymentSpec$DeclaredZone": { "superClass": "com.yahoo.config.application.api.DeploymentSpec$Step", "interfaces": [], @@ -246,7 +259,8 @@ "public java.util.Optional testerFlavor()", "public java.util.Optional athenzService()", "public java.util.List zones()", - "public boolean deploysTo(com.yahoo.config.provision.Environment, java.util.Optional)", + "public boolean concerns(com.yahoo.config.provision.Environment, java.util.Optional)", + "public boolean isTest()", "public int hashCode()", "public boolean equals(java.lang.Object)", "public java.lang.String toString()" @@ -262,22 +276,21 @@ "methods": [ "public void <init>(java.time.Duration)", "public java.time.Duration delay()", - "public boolean deploysTo(com.yahoo.config.provision.Environment, java.util.Optional)", + "public boolean concerns(com.yahoo.config.provision.Environment, java.util.Optional)", "public java.lang.String toString()" ], "fields": [] }, - "com.yahoo.config.application.api.DeploymentSpec$ParallelZones": { - "superClass": "com.yahoo.config.application.api.DeploymentSpec$Step", + "com.yahoo.config.application.api.DeploymentSpec$ParallelSteps": { + "superClass": "com.yahoo.config.application.api.DeploymentSpec$Steps", "interfaces": [], "attributes": [ "public" ], "methods": [ "public void <init>(java.util.List)", - "public java.util.List zones()", - "public java.util.List steps()", - "public boolean deploysTo(com.yahoo.config.provision.Environment, java.util.Optional)", + "public java.time.Duration delay()", + "public boolean isOrdered()", "public boolean equals(java.lang.Object)", "public int hashCode()", "public java.lang.String toString()" @@ -293,11 +306,32 @@ ], "methods": [ "public void <init>()", - "public final boolean deploysTo(com.yahoo.config.provision.Environment)", - "public abstract boolean deploysTo(com.yahoo.config.provision.Environment, java.util.Optional)", + "public final boolean concerns(com.yahoo.config.provision.Environment)", + "public boolean deploysTo(com.yahoo.config.provision.Environment, java.util.Optional)", + "public abstract boolean concerns(com.yahoo.config.provision.Environment, java.util.Optional)", "public java.util.List zones()", "public java.time.Duration delay()", - "public java.util.List steps()" + "public java.util.List steps()", + "public boolean isTest()", + "public boolean isOrdered()" + ], + "fields": [] + }, + "com.yahoo.config.application.api.DeploymentSpec$Steps": { + "superClass": "com.yahoo.config.application.api.DeploymentSpec$Step", + "interfaces": [], + "attributes": [ + "public" + ], + "methods": [ + "public void <init>(java.util.List)", + "public java.util.List zones()", + "public java.util.List steps()", + "public boolean concerns(com.yahoo.config.provision.Environment, java.util.Optional)", + "public java.time.Duration delay()", + "public boolean equals(java.lang.Object)", + "public int hashCode()", + "public java.lang.String toString()" ], "fields": [] }, @@ -331,7 +365,6 @@ "public java.util.List steps()", "public java.util.Optional athenzDomain()", "public java.util.Optional athenzService()", - "public java.util.Optional athenzService(com.yahoo.config.provision.InstanceName, com.yahoo.config.provision.Environment, com.yahoo.config.provision.RegionName)", "public java.lang.String xmlForm()", "public java.util.Optional instance(com.yahoo.config.provision.InstanceName)", "public com.yahoo.config.application.api.DeploymentInstanceSpec requireInstance(java.lang.String)", @@ -572,4 +605,4 @@ "public static final com.yahoo.config.application.api.ValidationOverrides all" ] } -} +}
\ No newline at end of file diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentInstanceSpec.java b/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentInstanceSpec.java index f96400fc9a9..2c397c95d5b 100644 --- a/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentInstanceSpec.java +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentInstanceSpec.java @@ -7,7 +7,6 @@ import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.RegionName; -import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.HashSet; @@ -22,12 +21,11 @@ import java.util.stream.Collectors; * * @author bratseth */ -public class DeploymentInstanceSpec extends DeploymentSpec.Step { +public class DeploymentInstanceSpec extends DeploymentSpec.Steps { /** The name of the instance this step deploys */ private final InstanceName name; - private final List<DeploymentSpec.Step> steps; private final DeploymentSpec.UpgradePolicy upgradePolicy; private final List<DeploymentSpec.ChangeBlocker> changeBlockers; private final Optional<String> globalServiceId; @@ -45,34 +43,54 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Step { Optional<AthenzService> athenzService, Notifications notifications, List<Endpoint> endpoints) { + super(steps); this.name = name; - this.steps = steps; this.upgradePolicy = upgradePolicy; this.changeBlockers = changeBlockers; this.globalServiceId = globalServiceId; this.athenzDomain = athenzDomain; this.athenzService = athenzService; this.notifications = notifications; - this.endpoints = List.copyOf(validateEndpoints(endpoints, this.steps)); - validateZones(this.steps); - validateEndpoints(this.steps, globalServiceId, this.endpoints); + this.endpoints = List.copyOf(validateEndpoints(endpoints, steps())); + validateZones(new HashSet<>(), this); + validateEndpoints(steps(), globalServiceId, this.endpoints); validateAthenz(); } public InstanceName name() { return name; } - /** Throw an IllegalArgumentException if any production zone is declared multiple times */ - private void validateZones(List<DeploymentSpec.Step> steps) { - Set<DeploymentSpec.DeclaredZone> zones = new HashSet<>(); - - for (DeploymentSpec.Step step : steps) - for (DeploymentSpec.DeclaredZone zone : step.zones()) - ensureUnique(zone, zones); - } + /** + * Throws an IllegalArgumentException if any production deployment or test is declared multiple times, + * or if any production test is declared not after its corresponding deployment. + * + * @param deployments previously seen deployments + * @param step step whose members to validate + * @return all contained tests + */ + private static Set<RegionName> validateZones(Set<RegionName> deployments, DeploymentSpec.Step step) { + if ( ! step.steps().isEmpty()) { + Set<RegionName> oldDeployments = Set.copyOf(deployments); + Set<RegionName> tests = new HashSet<>(); + for (DeploymentSpec.Step nested : step.steps()) { + for (RegionName test : validateZones(deployments, nested)) { + if ( ! (step.isOrdered() ? deployments : oldDeployments).contains(test)) + throw new IllegalArgumentException("tests for prod." + test + " must be after the corresponding deployment in deployment.xml"); + + if ( ! tests.add(test)) + throw new IllegalArgumentException("tests for prod." + test + " arelisted twice in deployment.xml"); + } + } + return tests; + } + if (step.concerns(Environment.prod)) { + if (step.isTest()) + return Set.of(((DeploymentSpec.DeclaredTest) step).region()); - private void ensureUnique(DeploymentSpec.DeclaredZone zone, Set<DeploymentSpec.DeclaredZone> zones) { - if ( ! zones.add(zone)) - throw new IllegalArgumentException(zone + " is listed twice in deployment.xml"); + RegionName region = ((DeploymentSpec.DeclaredZone) step).region().get(); + if ( ! deployments.add(region)) + throw new IllegalArgumentException("prod." + region + " is listed twice in deployment.xml"); + } + return Set.of(); } /** Validates the endpoints and makes sure default values are respected */ @@ -80,7 +98,7 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Step { Objects.requireNonNull(endpoints, "Missing endpoints parameter"); var productionRegions = steps.stream() - .filter(step -> step.deploysTo(Environment.prod)) + .filter(step -> step.concerns(Environment.prod)) .flatMap(step -> step.zones().stream()) .flatMap(zone -> zone.region().stream()) .map(RegionName::value) @@ -143,15 +161,6 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Step { } } - @Override - public Duration delay() { - return Duration.ofSeconds(steps.stream().mapToLong(step -> (step.delay().getSeconds())).sum()); - } - - /** Returns the deployment steps inside this in the order they will be performed */ - @Override - public List<DeploymentSpec.Step> steps() { return steps; } - /** Returns the upgrade policy of this, which is defaultPolicy if none is specified */ public DeploymentSpec.UpgradePolicy upgradePolicy() { return upgradePolicy; } @@ -173,32 +182,16 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Step { .noneMatch(block -> block.window().includes(instant)); } - /** Returns all the deployment steps which are zones in the order they are declared */ - public List<DeploymentSpec.DeclaredZone> zones() { - return steps.stream() - .flatMap(step -> step.zones().stream()) - .collect(Collectors.toList()); - } - - /** Returns whether this deployment spec specifies the given zone, either implicitly or explicitly */ - @Override - public boolean deploysTo(Environment environment, Optional<RegionName> region) { - for (DeploymentSpec.Step step : steps) - if (step.deploysTo(environment, region)) return true; - return false; - } - /** Returns the athenz domain if configured */ public Optional<AthenzDomain> athenzDomain() { return athenzDomain; } /** Returns the athenz service for environment/region if configured */ public Optional<AthenzService> athenzService(Environment environment, RegionName region) { - AthenzService athenzService = zones().stream() - .filter(zone -> zone.deploysTo(environment, Optional.of(region))) - .findFirst() - .flatMap(DeploymentSpec.DeclaredZone::athenzService) - .orElse(this.athenzService.orElse(null)); - return Optional.ofNullable(athenzService); + return zones().stream() + .filter(zone -> zone.concerns(environment, Optional.of(region))) + .findFirst() + .flatMap(DeploymentSpec.DeclaredZone::athenzService) + .or(() -> this.athenzService); } /** Returns the notification configuration of these instances */ @@ -207,23 +200,9 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Step { /** Returns the rotations configuration of these instances */ public List<Endpoint> endpoints() { return endpoints; } - /** Returns whether this instances deployment specifies the given zone, either implicitly or explicitly */ - public boolean includes(Environment environment, Optional<RegionName> region) { - for (DeploymentSpec.Step step : steps) - if (step.deploysTo(environment, region)) return true; - return false; - } - - DeploymentInstanceSpec withSteps(List<DeploymentSpec.Step> steps) { - return new DeploymentInstanceSpec(name, - steps, - upgradePolicy, - changeBlockers, - globalServiceId, - athenzDomain, - athenzService, - notifications, - endpoints); + /** Returns whether this instance deploys to the given zone, either implicitly or explicitly */ + public boolean deploysTo(Environment environment, RegionName region) { + return zones().stream().anyMatch(zone -> zone.concerns(environment, Optional.of(region))); } @Override @@ -234,7 +213,7 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Step { return globalServiceId.equals(other.globalServiceId) && upgradePolicy == other.upgradePolicy && changeBlockers.equals(other.changeBlockers) && - steps.equals(other.steps) && + steps().equals(other.steps()) && athenzDomain.equals(other.athenzDomain) && athenzService.equals(other.athenzService) && notifications.equals(other.notifications) && @@ -243,7 +222,7 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Step { @Override public int hashCode() { - return Objects.hash(globalServiceId, upgradePolicy, changeBlockers, steps, athenzDomain, athenzService, notifications, endpoints); + return Objects.hash(globalServiceId, upgradePolicy, changeBlockers, steps(), athenzDomain, athenzService, notifications, endpoints); } @Override diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java b/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java index ea7df677e0f..8c05b47e8e8 100644 --- a/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java @@ -13,10 +13,12 @@ import java.io.Reader; import java.time.Duration; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Specifies the environments and regions to which an application should be deployed. @@ -75,14 +77,14 @@ public class DeploymentSpec { List<Step> steps = new ArrayList<>(inputSteps); // Add staging if required and missing - if (steps.stream().anyMatch(step -> step.deploysTo(Environment.prod)) && - steps.stream().noneMatch(step -> step.deploysTo(Environment.staging))) { + if (steps.stream().anyMatch(step -> step.concerns(Environment.prod)) && + steps.stream().noneMatch(step -> step.concerns(Environment.staging))) { steps.add(new DeploymentSpec.DeclaredZone(Environment.staging)); } // Add test if required and missing - if (steps.stream().anyMatch(step -> step.deploysTo(Environment.staging)) && - steps.stream().noneMatch(step -> step.deploysTo(Environment.test))) { + if (steps.stream().anyMatch(step -> step.concerns(Environment.staging)) && + steps.stream().noneMatch(step -> step.concerns(Environment.test))) { steps.add(new DeploymentSpec.DeclaredZone(Environment.test)); } @@ -162,13 +164,6 @@ public class DeploymentSpec { // to have environment, instance or region variants on those. public Optional<AthenzService> athenzService() { return this.athenzService; } - // TODO remove when 7.135 is the oldest version - public Optional<AthenzService> athenzService(InstanceName instanceName, Environment environment, RegionName region) { - Optional<DeploymentInstanceSpec> instance = instance(instanceName); - if (instance.isEmpty()) return this.athenzService; - return instance.get().athenzService(environment, region).or(() -> this.athenzService); - } - /** Returns the XML form of this spec, or null if it was not created by fromXml, nor is empty */ public String xmlForm() { return xmlForm; } @@ -205,11 +200,15 @@ public class DeploymentSpec { private static List<DeploymentInstanceSpec> instances(List<DeploymentSpec.Step> steps) { return steps.stream() - .flatMap(step -> step instanceof ParallelZones ? ((ParallelZones) step).steps.stream() : List.of(step).stream()) - .filter(step -> step instanceof DeploymentInstanceSpec).map(DeploymentInstanceSpec.class::cast) + .flatMap(DeploymentSpec::flatten) .collect(Collectors.toList()); } + private static Stream<DeploymentInstanceSpec> flatten(Step step) { + if (step instanceof DeploymentInstanceSpec) return Stream.of((DeploymentInstanceSpec) step); + return step.steps().stream().flatMap(DeploymentSpec::flatten); + } + /** * Creates a deployment spec from XML. * @@ -272,23 +271,37 @@ public class DeploymentSpec { /** A deployment step */ public abstract static class Step { - /** Returns whether this step deploys to the given region */ - public final boolean deploysTo(Environment environment) { - return deploysTo(environment, Optional.empty()); + /** Returns whether this step specifies the given environment. */ + public final boolean concerns(Environment environment) { + return concerns(environment, Optional.empty()); } - /** Returns whether this step deploys to the given environment, and (if specified) region */ - public abstract boolean deploysTo(Environment environment, Optional<RegionName> region); + /** Returns whether this step specifies the given environment, and, optionally, region. */ + // TODO jonmv: Remove when 7.147 is the oldest version. + public boolean deploysTo(Environment environment, Optional<RegionName> region) { + return concerns(environment, region); + } + + /** Returns whether this step specifies the given environment, and, optionally, region. */ + public abstract boolean concerns(Environment environment, Optional<RegionName> region); - /** Returns the zones deployed to in this step */ + /** Returns the zones deployed to in this step. */ public List<DeclaredZone> zones() { return Collections.emptyList(); } - /** The delay introduced by this step (beyond the time it takes to execute the step). Default is zero. */ + /** The delay introduced by this step (beyond the time it takes to execute the step). */ public Duration delay() { return Duration.ZERO; } - /** Returns all the steps nested in this. This default implementatiino returns an empty list. */ + /** Returns any steps nested in this. */ public List<Step> steps() { return List.of(); } + /** Returns whether this step is a test step. */ + public boolean isTest() { return false; } + + /** Returns whether the nested steps in this, if any, should be performed in declaration order. */ + public boolean isOrdered() { + return true; + } + } /** A deployment step which is to wait for some time before progressing to the next step */ @@ -304,7 +317,7 @@ public class DeploymentSpec { public Duration delay() { return duration; } @Override - public boolean deploysTo(Environment environment, Optional<RegionName> region) { return false; } + public boolean concerns(Environment environment, Optional<RegionName> region) { return false; } @Override public String toString() { @@ -355,13 +368,16 @@ public class DeploymentSpec { public List<DeclaredZone> zones() { return Collections.singletonList(this); } @Override - public boolean deploysTo(Environment environment, Optional<RegionName> region) { + public boolean concerns(Environment environment, Optional<RegionName> region) { if (environment != this.environment) return false; if (region.isPresent() && ! region.equals(this.region)) return false; return true; } @Override + public boolean isTest() { return environment.isTest(); } + + @Override public int hashCode() { return Objects.hash(environment, region); } @@ -383,39 +399,82 @@ public class DeploymentSpec { } - /** A deployment step which is to run multiple steps (zones or instances) in parallel */ - public static class ParallelZones extends Step { + /** A declared production test */ + public static class DeclaredTest extends Step { + + private final RegionName region; + + public DeclaredTest(RegionName region) { + this.region = Objects.requireNonNull(region); + } + + @Override + public boolean concerns(Environment environment, Optional<RegionName> region) { + return region.map(this.region::equals).orElse(true) && environment == Environment.prod; + } + + @Override + public boolean isTest() { return true; } + + /** Returns the region this test is for. */ + public RegionName region() { + return region; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DeclaredTest that = (DeclaredTest) o; + return region.equals(that.region); + } + + @Override + public int hashCode() { + return Objects.hash(region); + } + + @Override + public String toString() { + return "tests for prod." + region; + } + + } + + /** A container for several steps, by default in serial order */ + public static class Steps extends Step { private final List<Step> steps; - public ParallelZones(List<Step> steps) { + public Steps(List<Step> steps) { this.steps = List.copyOf(steps); } - /** Returns the steps inside this which are zones */ @Override public List<DeclaredZone> zones() { - return this.steps.stream() - .filter(step -> step instanceof DeclaredZone) - .map(DeclaredZone.class::cast) - .collect(Collectors.toList()); + return steps.stream() + .flatMap(step -> step.zones().stream()) + .collect(Collectors.toUnmodifiableList()); } - /** Returns all the steps nested in this */ @Override public List<Step> steps() { return steps; } @Override - public boolean deploysTo(Environment environment, Optional<RegionName> region) { - return steps().stream().anyMatch(zone -> zone.deploysTo(environment, region)); + public boolean concerns(Environment environment, Optional<RegionName> region) { + return steps.stream().anyMatch(step -> step.concerns(environment, region)); + } + + @Override + public Duration delay() { + return steps.stream().map(Step::delay).reduce(Duration.ZERO, Duration::plus); } @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof ParallelZones)) return false; - ParallelZones that = (ParallelZones) o; - return Objects.equals(steps, that.steps); + if (o == null || getClass() != o.getClass()) return false; + return steps.equals(((Steps) o).steps); } @Override @@ -425,7 +484,43 @@ public class DeploymentSpec { @Override public String toString() { - return steps.size() + " parallel steps"; + return steps.size() + " steps"; + } + + } + + /** A container for multiple other steps, which are executed in parallel */ + public static class ParallelSteps extends Steps { + + public ParallelSteps(List<Step> steps) { + super(steps); + } + + @Override + public Duration delay() { + return steps().stream().map(Step::delay).max(Comparator.naturalOrder()).orElse(Duration.ZERO); + } + + @Override + public boolean isOrdered() { + return false; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if ( ! (o instanceof ParallelSteps)) return false; + return Objects.equals(steps(), ((ParallelSteps) o).steps()); + } + + @Override + public int hashCode() { + return Objects.hash(steps()); + } + + @Override + public String toString() { + return steps().size() + " parallel steps"; } } diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java b/config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java index bc17ee0cb2b..c14e6ce5966 100644 --- a/config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java @@ -3,10 +3,12 @@ package com.yahoo.config.application.api.xml; import com.yahoo.config.application.api.DeploymentInstanceSpec; import com.yahoo.config.application.api.DeploymentSpec; +import com.yahoo.config.application.api.DeploymentSpec.DeclaredTest; import com.yahoo.config.application.api.DeploymentSpec.DeclaredZone; import com.yahoo.config.application.api.DeploymentSpec.Delay; -import com.yahoo.config.application.api.DeploymentSpec.ParallelZones; +import com.yahoo.config.application.api.DeploymentSpec.ParallelSteps; import com.yahoo.config.application.api.DeploymentSpec.Step; +import com.yahoo.config.application.api.DeploymentSpec.Steps; import com.yahoo.config.application.api.Endpoint; import com.yahoo.config.application.api.Notifications; import com.yahoo.config.application.api.Notifications.Role; @@ -20,6 +22,7 @@ import com.yahoo.config.provision.RegionName; import com.yahoo.io.IOUtils; import com.yahoo.text.XML; import org.w3c.dom.Element; +import org.w3c.dom.Node; import java.io.IOException; import java.io.Reader; @@ -32,8 +35,10 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author bratseth @@ -50,6 +55,7 @@ public class DeploymentSpecXmlReader { private static final String regionTag = "region"; private static final String delayTag = "delay"; private static final String parallelTag = "parallel"; + private static final String stepsTag = "steps"; private static final String endpointsTag = "endpoints"; private static final String endpointTag = "endpoint"; private static final String notificationsTag = "notifications"; @@ -118,7 +124,7 @@ public class DeploymentSpecXmlReader { * * @param instanceNameString a comma-separated list of the names of the instances this is for * @param instanceTag the element having the content of this instance - * @param parentTag the parent of instanceTag (or the same, if this instances is implicitly defined which means instanceTag is the root) + * @param parentTag the parent of instanceTag (or the same, if this instance is implicitly defined, which means instanceTag is the root) * @return the instances specified, one for each instance name element */ private List<DeploymentInstanceSpec> readInstanceContent(String instanceNameString, @@ -169,6 +175,7 @@ public class DeploymentSpecXmlReader { } // Consume the given tag as 0-N steps. 0 if it is not a step, >1 if it contains multiple nested steps that should be flattened + @SuppressWarnings("fallthrough") private List<Step> readNonInstanceSteps(Element stepTag, MutableOptional<String> globalServiceId, Element parentTag) { Optional<AthenzService> athenzService = stringAttribute(athenzServiceAttribute, stepTag) .or(() -> stringAttribute(athenzServiceAttribute, parentTag)) @@ -182,7 +189,11 @@ public class DeploymentSpecXmlReader { throw new IllegalArgumentException("Attribute 'global-service-id' is only valid on 'prod' tag."); switch (stepTag.getTagName()) { - case testTag: case stagingTag: + case testTag: + if (Stream.iterate(stepTag, Objects::nonNull, Node::getParentNode) + .anyMatch(node -> prodTag.equals(node.getNodeName()))) + return List.of(new DeclaredTest(RegionName.from(XML.getValue(stepTag).trim()))); + case stagingTag: return List.of(new DeclaredZone(Environment.from(stepTag.getTagName()), Optional.empty(), false, athenzService, testerFlavor)); case prodTag: // regions, delay and parallel may be nested within, but we can flatten them return XML.getChildren(stepTag).stream() @@ -193,9 +204,13 @@ public class DeploymentSpecXmlReader { longAttribute("minutes", stepTag) * 60 + longAttribute("seconds", stepTag)))); case parallelTag: // regions and instances may be nested within - return List.of(new ParallelZones(XML.getChildren(stepTag).stream() + return List.of(new ParallelSteps(XML.getChildren(stepTag).stream() .flatMap(child -> readSteps(child, globalServiceId, parentTag).stream()) .collect(Collectors.toList()))); + case stepsTag: // regions and instances may be nested within + return List.of(new Steps(XML.getChildren(stepTag).stream() + .flatMap(child -> readSteps(child, globalServiceId, parentTag).stream()) + .collect(Collectors.toList()))); case regionTag: return List.of(readDeclaredZone(Environment.prod, athenzService, testerFlavor, stepTag)); default: diff --git a/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java b/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java index 987e13c0e86..584664b6ef1 100644 --- a/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java +++ b/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java @@ -3,11 +3,11 @@ package com.yahoo.config.application.api; import com.google.common.collect.ImmutableSet; import com.yahoo.config.provision.Environment; -import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.RegionName; import org.junit.Test; import java.io.StringReader; +import java.time.Duration; import java.time.Instant; import java.time.ZoneId; import java.util.Collections; @@ -42,11 +42,11 @@ public class DeploymentSpecTest { assertEquals(specXml, spec.xmlForm()); assertEquals(1, spec.requireInstance("default").steps().size()); assertFalse(spec.majorVersion().isPresent()); - assertTrue(spec.requireInstance("default").steps().get(0).deploysTo(Environment.test)); - assertTrue(spec.requireInstance("default").includes(Environment.test, Optional.empty())); - assertFalse(spec.requireInstance("default").includes(Environment.test, Optional.of(RegionName.from("region1")))); - assertFalse(spec.requireInstance("default").includes(Environment.staging, Optional.empty())); - assertFalse(spec.requireInstance("default").includes(Environment.prod, Optional.empty())); + assertTrue(spec.requireInstance("default").steps().get(0).concerns(Environment.test)); + assertTrue(spec.requireInstance("default").deploysTo(Environment.test, Optional.empty())); + assertFalse(spec.requireInstance("default").deploysTo(Environment.test, Optional.of(RegionName.from("region1")))); + assertFalse(spec.requireInstance("default").deploysTo(Environment.staging, Optional.empty())); + assertFalse(spec.requireInstance("default").deploysTo(Environment.prod, Optional.empty())); assertFalse(spec.requireInstance("default").globalServiceId().isPresent()); } @@ -79,11 +79,11 @@ public class DeploymentSpecTest { DeploymentSpec spec = DeploymentSpec.fromXml(r); assertEquals(2, spec.steps().size()); assertEquals(1, spec.requireInstance("default").steps().size()); - assertTrue(spec.steps().get(0).deploysTo(Environment.test)); - assertTrue(spec.requireInstance("default").steps().get(0).deploysTo(Environment.staging)); - assertFalse(spec.requireInstance("default").includes(Environment.test, Optional.empty())); - assertTrue(spec.requireInstance("default").includes(Environment.staging, Optional.empty())); - assertFalse(spec.requireInstance("default").includes(Environment.prod, Optional.empty())); + assertTrue(spec.steps().get(0).concerns(Environment.test)); + assertTrue(spec.requireInstance("default").steps().get(0).concerns(Environment.staging)); + assertFalse(spec.requireInstance("default").deploysTo(Environment.test, Optional.empty())); + assertTrue(spec.requireInstance("default").deploysTo(Environment.staging, Optional.empty())); + assertFalse(spec.requireInstance("default").deploysTo(Environment.prod, Optional.empty())); assertFalse(spec.requireInstance("default").globalServiceId().isPresent()); } @@ -104,21 +104,21 @@ public class DeploymentSpecTest { assertEquals(3, spec.steps().size()); assertEquals(2, spec.requireInstance("default").steps().size()); - assertTrue(spec.steps().get(0).deploysTo(Environment.test)); + assertTrue(spec.steps().get(0).concerns(Environment.test)); - assertTrue(spec.steps().get(1).deploysTo(Environment.staging)); + assertTrue(spec.steps().get(1).concerns(Environment.staging)); - assertTrue(spec.requireInstance("default").steps().get(0).deploysTo(Environment.prod, Optional.of(RegionName.from("us-east1")))); + assertTrue(spec.requireInstance("default").steps().get(0).concerns(Environment.prod, Optional.of(RegionName.from("us-east1")))); assertFalse(((DeploymentSpec.DeclaredZone)spec.requireInstance("default").steps().get(0)).active()); - assertTrue(spec.requireInstance("default").steps().get(1).deploysTo(Environment.prod, Optional.of(RegionName.from("us-west1")))); + assertTrue(spec.requireInstance("default").steps().get(1).concerns(Environment.prod, Optional.of(RegionName.from("us-west1")))); assertTrue(((DeploymentSpec.DeclaredZone)spec.requireInstance("default").steps().get(1)).active()); - assertFalse(spec.requireInstance("default").includes(Environment.test, Optional.empty())); - assertFalse(spec.requireInstance("default").includes(Environment.staging, Optional.empty())); - assertTrue(spec.requireInstance("default").includes(Environment.prod, Optional.of(RegionName.from("us-east1")))); - assertTrue(spec.requireInstance("default").includes(Environment.prod, Optional.of(RegionName.from("us-west1")))); - assertFalse(spec.requireInstance("default").includes(Environment.prod, Optional.of(RegionName.from("no-such-region")))); + assertFalse(spec.requireInstance("default").deploysTo(Environment.test, Optional.empty())); + assertFalse(spec.requireInstance("default").deploysTo(Environment.staging, Optional.empty())); + assertTrue(spec.requireInstance("default").deploysTo(Environment.prod, Optional.of(RegionName.from("us-east1")))); + assertTrue(spec.requireInstance("default").deploysTo(Environment.prod, Optional.of(RegionName.from("us-west1")))); + assertFalse(spec.requireInstance("default").deploysTo(Environment.prod, Optional.of(RegionName.from("no-such-region")))); assertFalse(spec.requireInstance("default").globalServiceId().isPresent()); assertEquals(DeploymentSpec.UpgradePolicy.defaultPolicy, spec.requireInstance("default").upgradePolicy()); @@ -145,6 +145,84 @@ public class DeploymentSpecTest { } @Test + public void productionTests() { + StringReader r = new StringReader( + "<deployment version='1.0'>" + + " <instance id='default'>" + + " <test/>" + + " <staging/>" + + " <prod>" + + " <region active='false'>us-east-1</region>" + + " <region active='true'>us-west-1</region>" + + " <delay hours='1' />" + + " <test>us-west-1</test>" + + " <test>us-east-1</test>" + + " </prod>" + + " </instance>" + + "</deployment>" + ); + + DeploymentSpec spec = DeploymentSpec.fromXml(r); + List<DeploymentSpec.Step> instanceSteps = spec.steps().get(0).steps(); + assertEquals(7, instanceSteps.size()); + assertEquals("test", instanceSteps.get(0).toString()); + assertEquals("staging", instanceSteps.get(1).toString()); + assertEquals("prod.us-east-1", instanceSteps.get(2).toString()); + assertEquals("prod.us-west-1", instanceSteps.get(3).toString()); + assertEquals("delay PT1H", instanceSteps.get(4).toString()); + assertEquals("tests for prod.us-west-1", instanceSteps.get(5).toString()); + assertEquals("tests for prod.us-east-1", instanceSteps.get(6).toString()); + } + + @Test(expected = IllegalArgumentException.class) + public void duplicateProductionTest() { + StringReader r = new StringReader( + "<deployment version='1.0'>" + + " <instance id='default'>" + + " <prod>" + + " <region active='true'>us-east1</region>" + + " <test>us-east1</test>" + + " <test>us-east1</test>" + + " </prod>" + + " </instance>" + + "</deployment>" + ); + DeploymentSpec.fromXml(r); + } + + @Test(expected = IllegalArgumentException.class) + public void productionTestBeforeDeployment() { + StringReader r = new StringReader( + "<deployment version='1.0'>" + + " <instance id='default'>" + + " <prod>" + + " <test>us-east1</test>" + + " <region active='true'>us-east1</region>" + + " </prod>" + + " </instance>" + + "</deployment>" + ); + DeploymentSpec.fromXml(r); + } + + @Test(expected = IllegalArgumentException.class) + public void productionTestInParallelWithDeployment() { + StringReader r = new StringReader( + "<deployment version='1.0'>" + + " <instance id='default'>" + + " <prod>" + + " <parallel>" + + " <region active='true'>us-east1</region>" + + " <test>us-east1</test>" + + " </parallel>" + + " </prod>" + + " </instance>" + + "</deployment>" + ); + DeploymentSpec.fromXml(r); + } + + @Test public void maximalProductionSpecMultipleInstances() { StringReader r = new StringReader( "<deployment version='1.0'>" + @@ -173,7 +251,7 @@ public class DeploymentSpecTest { assertEquals(1, instance2.steps().size()); assertEquals(1, instance2.zones().size()); - assertTrue(instance2.steps().get(0).deploysTo(Environment.prod, Optional.of(RegionName.from("us-central1")))); + assertTrue(instance2.steps().get(0).concerns(Environment.prod, Optional.of(RegionName.from("us-central1")))); } @Test @@ -202,25 +280,25 @@ public class DeploymentSpecTest { assertEquals(5, instance.steps().size()); assertEquals(4, instance.zones().size()); - assertTrue(instance.steps().get(0).deploysTo(Environment.test)); + assertTrue(instance.steps().get(0).concerns(Environment.test)); - assertTrue(instance.steps().get(1).deploysTo(Environment.staging)); + assertTrue(instance.steps().get(1).concerns(Environment.staging)); - assertTrue(instance.steps().get(2).deploysTo(Environment.prod, Optional.of(RegionName.from("us-east1")))); + assertTrue(instance.steps().get(2).concerns(Environment.prod, Optional.of(RegionName.from("us-east1")))); assertFalse(((DeploymentSpec.DeclaredZone)instance.steps().get(2)).active()); assertTrue(instance.steps().get(3) instanceof DeploymentSpec.Delay); assertEquals(3 * 60 * 60 + 30 * 60, instance.steps().get(3).delay().getSeconds()); - assertTrue(instance.steps().get(4).deploysTo(Environment.prod, Optional.of(RegionName.from("us-west1")))); + assertTrue(instance.steps().get(4).concerns(Environment.prod, Optional.of(RegionName.from("us-west1")))); assertTrue(((DeploymentSpec.DeclaredZone)instance.steps().get(4)).active()); - assertTrue(instance.includes(Environment.test, Optional.empty())); - assertFalse(instance.includes(Environment.test, Optional.of(RegionName.from("region1")))); - assertTrue(instance.includes(Environment.staging, Optional.empty())); - assertTrue(instance.includes(Environment.prod, Optional.of(RegionName.from("us-east1")))); - assertTrue(instance.includes(Environment.prod, Optional.of(RegionName.from("us-west1")))); - assertFalse(instance.includes(Environment.prod, Optional.of(RegionName.from("no-such-region")))); + assertTrue(instance.deploysTo(Environment.test, Optional.empty())); + assertFalse(instance.deploysTo(Environment.test, Optional.of(RegionName.from("region1")))); + assertTrue(instance.deploysTo(Environment.staging, Optional.empty())); + assertTrue(instance.deploysTo(Environment.prod, Optional.of(RegionName.from("us-east1")))); + assertTrue(instance.deploysTo(Environment.prod, Optional.of(RegionName.from("us-west1")))); + assertFalse(instance.deploysTo(Environment.prod, Optional.of(RegionName.from("no-such-region")))); assertFalse(instance.globalServiceId().isPresent()); } @@ -372,10 +450,10 @@ public class DeploymentSpecTest { "</deployment>" ); DeploymentSpec spec = DeploymentSpec.fromXml(r); - DeploymentSpec.ParallelZones parallelZones = ((DeploymentSpec.ParallelZones) spec.requireInstance("default").steps().get(1)); - assertEquals(2, parallelZones.zones().size()); - assertEquals(RegionName.from("us-central-1"), parallelZones.zones().get(0).region().get()); - assertEquals(RegionName.from("us-east-3"), parallelZones.zones().get(1).region().get()); + DeploymentSpec.ParallelSteps parallelSteps = ((DeploymentSpec.ParallelSteps) spec.requireInstance("default").steps().get(1)); + assertEquals(2, parallelSteps.zones().size()); + assertEquals(RegionName.from("us-central-1"), parallelSteps.zones().get(0).region().get()); + assertEquals(RegionName.from("us-east-3"), parallelSteps.zones().get(1).region().get()); } @Test @@ -419,6 +497,77 @@ public class DeploymentSpecTest { } @Test + public void testNestedParallelAndSteps() { + StringReader r = new StringReader( + "<deployment athenz-domain='domain'>" + + " <instance id='instance' athenz-service='in-service'>" + + " <prod>" + + " <parallel>" + + " <region active='true'>us-west-1</region>" + + " <steps>" + + " <region active='true'>us-east-3</region>" + + " <delay hours='2' />" + + " <region active='true'>eu-west-1</region>" + + " <delay hours='2' />" + + " </steps>" + + " <steps>" + + " <delay hours='3' />" + + " <region active='true'>aws-us-east-1a</region>" + + " <parallel>" + + " <region active='true' athenz-service='no-service'>ap-northeast-1</region>" + + " <region active='true'>ap-southeast-2</region>" + + " </parallel>" + + " </steps>" + + " <delay hours='3' minutes='30' />" + + " </parallel>" + + " <region active='true'>us-north-7</region>" + + " </prod>" + + " </instance>" + + "</deployment>" + ); + + DeploymentSpec spec = DeploymentSpec.fromXml(r); + List<DeploymentSpec.Step> steps = spec.steps(); + assertEquals(3, steps.size()); + assertEquals("test", steps.get(0).toString()); + assertEquals("staging", steps.get(1).toString()); + assertEquals("instance 'instance'", steps.get(2).toString()); + assertEquals(Duration.ofHours(4), steps.get(2).delay()); + + List<DeploymentSpec.Step> instanceSteps = steps.get(2).steps(); + assertEquals(2, instanceSteps.size()); + assertEquals("4 parallel steps", instanceSteps.get(0).toString()); + assertEquals("prod.us-north-7", instanceSteps.get(1).toString()); + + List<DeploymentSpec.Step> parallelSteps = instanceSteps.get(0).steps(); + assertEquals(4, parallelSteps.size()); + assertEquals("prod.us-west-1", parallelSteps.get(0).toString()); + assertEquals("4 steps", parallelSteps.get(1).toString()); + assertEquals("3 steps", parallelSteps.get(2).toString()); + assertEquals("delay PT3H30M", parallelSteps.get(3).toString()); + + List<DeploymentSpec.Step> firstSerialSteps = parallelSteps.get(1).steps(); + assertEquals(4, firstSerialSteps.size()); + assertEquals("prod.us-east-3", firstSerialSteps.get(0).toString()); + assertEquals("delay PT2H", firstSerialSteps.get(1).toString()); + assertEquals("prod.eu-west-1", firstSerialSteps.get(2).toString()); + assertEquals("delay PT2H", firstSerialSteps.get(3).toString()); + + List<DeploymentSpec.Step> secondSerialSteps = parallelSteps.get(2).steps(); + assertEquals(3, secondSerialSteps.size()); + assertEquals("delay PT3H", secondSerialSteps.get(0).toString()); + assertEquals("prod.aws-us-east-1a", secondSerialSteps.get(1).toString()); + assertEquals("2 parallel steps", secondSerialSteps.get(2).toString()); + + List<DeploymentSpec.Step> innerParallelSteps = secondSerialSteps.get(2).steps(); + assertEquals(2, innerParallelSteps.size()); + assertEquals("prod.ap-northeast-1", innerParallelSteps.get(0).toString()); + assertEquals("no-service", spec.requireInstance("instance").athenzService(Environment.prod, RegionName.from("ap-northeast-1")).get().value()); + assertEquals("prod.ap-southeast-2", innerParallelSteps.get(1).toString()); + assertEquals("in-service", spec.requireInstance("instance").athenzService(Environment.prod, RegionName.from("ap-southeast-2")).get().value()); + } + + @Test public void testParallelInstances() { StringReader r = new StringReader( "<deployment>" + @@ -486,8 +635,8 @@ public class DeploymentSpecTest { " <region active='true'>us-west-1</region>" + " <parallel>" + " <region active='true'>us-west-1</region>" + - " <region active='true'>us-central-1</region>" + - " <region active='true'>us-east-3</region>" + + " <region active='true'>us-central-1</region>" + + " <region active='true'>us-east-3</region>" + " </parallel>" + " </prod>" + " </instance>" + diff --git a/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecWithoutInstanceTest.java b/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecWithoutInstanceTest.java index 31a30e5bd83..13ec7bf1a8b 100644 --- a/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecWithoutInstanceTest.java +++ b/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecWithoutInstanceTest.java @@ -3,11 +3,11 @@ package com.yahoo.config.application.api; import com.google.common.collect.ImmutableSet; import com.yahoo.config.provision.Environment; -import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.RegionName; import org.junit.Test; import java.io.StringReader; +import java.time.Duration; import java.time.Instant; import java.time.ZoneId; import java.util.Collections; @@ -40,11 +40,11 @@ public class DeploymentSpecWithoutInstanceTest { assertEquals(specXml, spec.xmlForm()); assertEquals(1, spec.steps().size()); assertFalse(spec.majorVersion().isPresent()); - assertTrue(spec.steps().get(0).deploysTo(Environment.test)); - assertTrue(spec.requireInstance("default").includes(Environment.test, Optional.empty())); - assertFalse(spec.requireInstance("default").includes(Environment.test, Optional.of(RegionName.from("region1")))); - assertFalse(spec.requireInstance("default").includes(Environment.staging, Optional.empty())); - assertFalse(spec.requireInstance("default").includes(Environment.prod, Optional.empty())); + assertTrue(spec.steps().get(0).concerns(Environment.test)); + assertTrue(spec.requireInstance("default").deploysTo(Environment.test, Optional.empty())); + assertFalse(spec.requireInstance("default").deploysTo(Environment.test, Optional.of(RegionName.from("region1")))); + assertFalse(spec.requireInstance("default").deploysTo(Environment.staging, Optional.empty())); + assertFalse(spec.requireInstance("default").deploysTo(Environment.prod, Optional.empty())); assertFalse(spec.requireInstance("default").globalServiceId().isPresent()); } @@ -73,11 +73,11 @@ public class DeploymentSpecWithoutInstanceTest { DeploymentSpec spec = DeploymentSpec.fromXml(r); assertEquals(2, spec.steps().size()); assertEquals(1, spec.requireInstance("default").steps().size()); - assertTrue(spec.steps().get(0).deploysTo(Environment.test)); - assertTrue(spec.requireInstance("default").steps().get(0).deploysTo(Environment.staging)); - assertFalse(spec.requireInstance("default").includes(Environment.test, Optional.empty())); - assertTrue(spec.requireInstance("default").includes(Environment.staging, Optional.empty())); - assertFalse(spec.requireInstance("default").includes(Environment.prod, Optional.empty())); + assertTrue(spec.steps().get(0).concerns(Environment.test)); + assertTrue(spec.requireInstance("default").steps().get(0).concerns(Environment.staging)); + assertFalse(spec.requireInstance("default").deploysTo(Environment.test, Optional.empty())); + assertTrue(spec.requireInstance("default").deploysTo(Environment.staging, Optional.empty())); + assertFalse(spec.requireInstance("default").deploysTo(Environment.prod, Optional.empty())); assertFalse(spec.requireInstance("default").globalServiceId().isPresent()); } @@ -96,21 +96,21 @@ public class DeploymentSpecWithoutInstanceTest { assertEquals(3, spec.steps().size()); assertEquals(2, spec.requireInstance("default").steps().size()); - assertTrue(spec.steps().get(0).deploysTo(Environment.test)); + assertTrue(spec.steps().get(0).concerns(Environment.test)); - assertTrue(spec.steps().get(1).deploysTo(Environment.staging)); + assertTrue(spec.steps().get(1).concerns(Environment.staging)); - assertTrue(spec.requireInstance("default").steps().get(0).deploysTo(Environment.prod, Optional.of(RegionName.from("us-east1")))); + assertTrue(spec.requireInstance("default").steps().get(0).concerns(Environment.prod, Optional.of(RegionName.from("us-east1")))); assertFalse(((DeploymentSpec.DeclaredZone)spec.requireInstance("default").steps().get(0)).active()); - assertTrue(spec.requireInstance("default").steps().get(1).deploysTo(Environment.prod, Optional.of(RegionName.from("us-west1")))); + assertTrue(spec.requireInstance("default").steps().get(1).concerns(Environment.prod, Optional.of(RegionName.from("us-west1")))); assertTrue(((DeploymentSpec.DeclaredZone)spec.requireInstance("default").steps().get(1)).active()); - assertFalse(spec.requireInstance("default").includes(Environment.test, Optional.empty())); - assertFalse(spec.requireInstance("default").includes(Environment.staging, Optional.empty())); - assertTrue(spec.requireInstance("default").includes(Environment.prod, Optional.of(RegionName.from("us-east1")))); - assertTrue(spec.requireInstance("default").includes(Environment.prod, Optional.of(RegionName.from("us-west1")))); - assertFalse(spec.requireInstance("default").includes(Environment.prod, Optional.of(RegionName.from("no-such-region")))); + assertFalse(spec.requireInstance("default").deploysTo(Environment.test, Optional.empty())); + assertFalse(spec.requireInstance("default").deploysTo(Environment.staging, Optional.empty())); + assertTrue(spec.requireInstance("default").deploysTo(Environment.prod, Optional.of(RegionName.from("us-east1")))); + assertTrue(spec.requireInstance("default").deploysTo(Environment.prod, Optional.of(RegionName.from("us-west1")))); + assertFalse(spec.requireInstance("default").deploysTo(Environment.prod, Optional.of(RegionName.from("no-such-region")))); assertFalse(spec.requireInstance("default").globalServiceId().isPresent()); assertEquals(DeploymentSpec.UpgradePolicy.defaultPolicy, spec.requireInstance("default").upgradePolicy()); @@ -134,29 +134,99 @@ public class DeploymentSpecWithoutInstanceTest { assertEquals(5, spec.requireInstance("default").steps().size()); assertEquals(4, spec.requireInstance("default").zones().size()); - assertTrue(spec.requireInstance("default").steps().get(0).deploysTo(Environment.test)); + assertTrue(spec.requireInstance("default").steps().get(0).concerns(Environment.test)); - assertTrue(spec.requireInstance("default").steps().get(1).deploysTo(Environment.staging)); + assertTrue(spec.requireInstance("default").steps().get(1).concerns(Environment.staging)); - assertTrue(spec.requireInstance("default").steps().get(2).deploysTo(Environment.prod, Optional.of(RegionName.from("us-east1")))); + assertTrue(spec.requireInstance("default").steps().get(2).concerns(Environment.prod, Optional.of(RegionName.from("us-east1")))); assertFalse(((DeploymentSpec.DeclaredZone)spec.requireInstance("default").steps().get(2)).active()); assertTrue(spec.requireInstance("default").steps().get(3) instanceof DeploymentSpec.Delay); assertEquals(3 * 60 * 60 + 30 * 60, spec.requireInstance("default").steps().get(3).delay().getSeconds()); - assertTrue(spec.requireInstance("default").steps().get(4).deploysTo(Environment.prod, Optional.of(RegionName.from("us-west1")))); + assertTrue(spec.requireInstance("default").steps().get(4).concerns(Environment.prod, Optional.of(RegionName.from("us-west1")))); assertTrue(((DeploymentSpec.DeclaredZone)spec.requireInstance("default").steps().get(4)).active()); - assertTrue(spec.requireInstance("default").includes(Environment.test, Optional.empty())); - assertFalse(spec.requireInstance("default").includes(Environment.test, Optional.of(RegionName.from("region1")))); - assertTrue(spec.requireInstance("default").includes(Environment.staging, Optional.empty())); - assertTrue(spec.requireInstance("default").includes(Environment.prod, Optional.of(RegionName.from("us-east1")))); - assertTrue(spec.requireInstance("default").includes(Environment.prod, Optional.of(RegionName.from("us-west1")))); - assertFalse(spec.requireInstance("default").includes(Environment.prod, Optional.of(RegionName.from("no-such-region")))); + assertTrue(spec.requireInstance("default").deploysTo(Environment.test, Optional.empty())); + assertFalse(spec.requireInstance("default").deploysTo(Environment.test, Optional.of(RegionName.from("region1")))); + assertTrue(spec.requireInstance("default").deploysTo(Environment.staging, Optional.empty())); + assertTrue(spec.requireInstance("default").deploysTo(Environment.prod, Optional.of(RegionName.from("us-east1")))); + assertTrue(spec.requireInstance("default").deploysTo(Environment.prod, Optional.of(RegionName.from("us-west1")))); + assertFalse(spec.requireInstance("default").deploysTo(Environment.prod, Optional.of(RegionName.from("no-such-region")))); assertFalse(spec.requireInstance("default").globalServiceId().isPresent()); } @Test + public void productionTests() { + StringReader r = new StringReader( + "<deployment version='1.0'>" + + " <test/>" + + " <staging/>" + + " <prod>" + + " <region active='false'>us-east-1</region>" + + " <region active='true'>us-west-1</region>" + + " <delay hours='1' />" + + " <test>us-west-1</test>" + + " <test>us-east-1</test>" + + " </prod>" + + "</deployment>" + ); + + DeploymentSpec spec = DeploymentSpec.fromXml(r); + List<DeploymentSpec.Step> instanceSteps = spec.steps().get(0).steps(); + assertEquals(7, instanceSteps.size()); + assertEquals("test", instanceSteps.get(0).toString()); + assertEquals("staging", instanceSteps.get(1).toString()); + assertEquals("prod.us-east-1", instanceSteps.get(2).toString()); + assertEquals("prod.us-west-1", instanceSteps.get(3).toString()); + assertEquals("delay PT1H", instanceSteps.get(4).toString()); + assertEquals("tests for prod.us-west-1", instanceSteps.get(5).toString()); + assertEquals("tests for prod.us-east-1", instanceSteps.get(6).toString()); + } + + @Test(expected = IllegalArgumentException.class) + public void duplicateProductionTests() { + StringReader r = new StringReader( + "<deployment version='1.0'>" + + " <prod>" + + " <region active='true'>us-east1</region>" + + " <test>us-east1</test>" + + " <test>us-east1</test>" + + " </prod>" + + "</deployment>" + ); + DeploymentSpec.fromXml(r); + } + + @Test(expected = IllegalArgumentException.class) + public void productionTestBeforeDeployment() { + StringReader r = new StringReader( + "<deployment version='1.0'>" + + " <prod>" + + " <test>us-east1</test>" + + " <region active='true'>us-east1</region>" + + " </prod>" + + "</deployment>" + ); + DeploymentSpec.fromXml(r); + } + + @Test(expected = IllegalArgumentException.class) + public void productionTestInParallelWithDeployment() { + StringReader r = new StringReader( + "<deployment version='1.0'>" + + " <prod>" + + " <parallel>" + + " <region active='true'>us-east1</region>" + + " <test>us-east1</test>" + + " </parallel>" + + " </prod>" + + "</deployment>" + ); + DeploymentSpec.fromXml(r); + } + + @Test public void productionSpecWithGlobalServiceId() { StringReader r = new StringReader( "<deployment version='1.0'>" + @@ -272,10 +342,79 @@ public class DeploymentSpecWithoutInstanceTest { "</deployment>" ); DeploymentSpec spec = DeploymentSpec.fromXml(r); - DeploymentSpec.ParallelZones parallelZones = ((DeploymentSpec.ParallelZones) spec.requireInstance("default").steps().get(1)); - assertEquals(2, parallelZones.zones().size()); - assertEquals(RegionName.from("us-central-1"), parallelZones.zones().get(0).region().get()); - assertEquals(RegionName.from("us-east-3"), parallelZones.zones().get(1).region().get()); + DeploymentSpec.ParallelSteps parallelSteps = ((DeploymentSpec.ParallelSteps) spec.requireInstance("default").steps().get(1)); + assertEquals(2, parallelSteps.zones().size()); + assertEquals(RegionName.from("us-central-1"), parallelSteps.zones().get(0).region().get()); + assertEquals(RegionName.from("us-east-3"), parallelSteps.zones().get(1).region().get()); + } + + @Test + public void testNestedParallelAndSteps() { + StringReader r = new StringReader( + "<deployment athenz-domain='domain' athenz-service='service'>" + + " <prod>" + + " <parallel>" + + " <region active='true'>us-west-1</region>" + + " <steps>" + + " <region active='true'>us-east-3</region>" + + " <delay hours='2' />" + + " <region active='true'>eu-west-1</region>" + + " <delay hours='2' />" + + " </steps>" + + " <steps>" + + " <delay hours='3' />" + + " <region active='true'>aws-us-east-1a</region>" + + " <parallel>" + + " <region active='true' athenz-service='no-service'>ap-northeast-1</region>" + + " <region active='true'>ap-southeast-2</region>" + + " </parallel>" + + " </steps>" + + " <delay hours='3' minutes='30' />" + + " </parallel>" + + " <region active='true'>us-north-7</region>" + + " </prod>" + + "</deployment>" + ); + + DeploymentSpec spec = DeploymentSpec.fromXml(r); + List<DeploymentSpec.Step> steps = spec.steps(); + assertEquals(3, steps.size()); + assertEquals("test", steps.get(0).toString()); + assertEquals("staging", steps.get(1).toString()); + assertEquals("instance 'default'", steps.get(2).toString()); + assertEquals(Duration.ofHours(4), steps.get(2).delay()); + + List<DeploymentSpec.Step> instanceSteps = steps.get(2).steps(); + assertEquals(2, instanceSteps.size()); + assertEquals("4 parallel steps", instanceSteps.get(0).toString()); + assertEquals("prod.us-north-7", instanceSteps.get(1).toString()); + + List<DeploymentSpec.Step> parallelSteps = instanceSteps.get(0).steps(); + assertEquals(4, parallelSteps.size()); + assertEquals("prod.us-west-1", parallelSteps.get(0).toString()); + assertEquals("4 steps", parallelSteps.get(1).toString()); + assertEquals("3 steps", parallelSteps.get(2).toString()); + assertEquals("delay PT3H30M", parallelSteps.get(3).toString()); + + List<DeploymentSpec.Step> firstSerialSteps = parallelSteps.get(1).steps(); + assertEquals(4, firstSerialSteps.size()); + assertEquals("prod.us-east-3", firstSerialSteps.get(0).toString()); + assertEquals("delay PT2H", firstSerialSteps.get(1).toString()); + assertEquals("prod.eu-west-1", firstSerialSteps.get(2).toString()); + assertEquals("delay PT2H", firstSerialSteps.get(3).toString()); + + List<DeploymentSpec.Step> secondSerialSteps = parallelSteps.get(2).steps(); + assertEquals(3, secondSerialSteps.size()); + assertEquals("delay PT3H", secondSerialSteps.get(0).toString()); + assertEquals("prod.aws-us-east-1a", secondSerialSteps.get(1).toString()); + assertEquals("2 parallel steps", secondSerialSteps.get(2).toString()); + + List<DeploymentSpec.Step> innerParallelSteps = secondSerialSteps.get(2).steps(); + assertEquals(2, innerParallelSteps.size()); + assertEquals("prod.ap-northeast-1", innerParallelSteps.get(0).toString()); + assertEquals("no-service", spec.requireInstance("default").athenzService(Environment.prod, RegionName.from("ap-northeast-1")).get().value()); + assertEquals("prod.ap-southeast-2", innerParallelSteps.get(1).toString()); + assertEquals("service", spec.requireInstance("default").athenzService(Environment.prod, RegionName.from("ap-southeast-2")).get().value()); } @Test diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java index e5e22c3a260..188a9b4765c 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java @@ -236,7 +236,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { Optional<DeploymentInstanceSpec> instance = spec.instance(app.getApplicationId().instance()); if (instance.isEmpty()) return false; return instance.get().zones().stream() - .anyMatch(declaredZone -> declaredZone.deploysTo(zone.environment(), Optional.of(zone.region())) && + .anyMatch(declaredZone -> declaredZone.concerns(zone.environment(), Optional.of(zone.region())) && declaredZone.active()); } diff --git a/config-model/src/main/resources/schema/deployment.rnc b/config-model/src/main/resources/schema/deployment.rnc index 1e1d9ad3aa9..6a8bc8f77b9 100644 --- a/config-model/src/main/resources/schema/deployment.rnc +++ b/config-model/src/main/resources/schema/deployment.rnc @@ -25,6 +25,11 @@ StepExceptInstance = Staging? & Prod* +PrimitiveStep = + Delay* & + Region* & + ProdTest* + Instance = element instance { attribute id { xsd:string } & attribute athenz-domain { xsd:string }? & @@ -32,8 +37,14 @@ Instance = element instance { StepExceptInstance } -ParallelRegions = element parallel { - Region* +ParallelSteps = element parallel { + SerialSteps* & + PrimitiveStep* +} + +SerialSteps = element steps { + ParallelSteps* & + PrimitiveStep* } ParallelInstances = element parallel { @@ -81,11 +92,17 @@ Prod = element prod { attribute tester-flavor { xsd:string }? & Region* & Delay* & - ParallelRegions* + ProdTest* & + ParallelSteps* +} + +ProdTest = element test { + text } Region = element region { attribute active { xsd:boolean } & + attribute athenz-service { xsd:string }? & text } diff --git a/config-model/src/test/cfg/application/app_complicated_deployment_spec/deployment.xml b/config-model/src/test/cfg/application/app_complicated_deployment_spec/deployment.xml new file mode 100644 index 00000000000..36c1d449021 --- /dev/null +++ b/config-model/src/test/cfg/application/app_complicated_deployment_spec/deployment.xml @@ -0,0 +1,48 @@ +<deployment version='1.0' athenz-domain='domain'> + <staging /> + <parallel> + <instance id='instance' athenz-service='in-service'> + <prod> + <parallel> + <region active='true'>us-west-1</region> + <steps> + <region active='true'>us-east-3</region> + <delay hours='2' /> + <region active='true'>eu-west-1</region> + <delay hours='2' /> + </steps> + <steps> + <delay hours='3' /> + <region active='true'>aws-us-east-1a</region> + <parallel> + <region active='true' athenz-service='no-service'>ap-northeast-1</region> + <region active='true'>ap-southeast-2</region> + </parallel> + </steps> + <delay hours='3' minutes='30' /> + </parallel> + <region active='true'>us-north-7</region> + </prod> + </instance> + <instance id='other'> + <upgrade policy='conservative' /> + <block-change days='sat' hours='10' time-zone='CET' /> + <test /> + <prod> + <region active='true'>eu-central-2</region> + </prod> + <notifications when='failing'> + <email role='author' /> + <email address='john@dev' when='failing-commit' /> + <email address='jane@dev' /> + </notifications> + <endpoints> + <endpoint id='foo' container-id='bar'> + <region>us-east</region> + </endpoint> + <endpoint id='nalle' container-id='frosk' /> + <endpoint container-id='quux' /> + </endpoints> + </instance> + </parallel> +</deployment> diff --git a/config-model/src/test/cfg/application/app_complicated_deployment_spec/hosts.xml b/config-model/src/test/cfg/application/app_complicated_deployment_spec/hosts.xml new file mode 100644 index 00000000000..a7fc725d377 --- /dev/null +++ b/config-model/src/test/cfg/application/app_complicated_deployment_spec/hosts.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<hosts> + <host name="bogusname1"> + <alias>node1</alias> + </host> + <host name="bogusname2"> + <alias>node2</alias> + </host> + <host name="bogusname3"> + <alias>node3</alias> + </host> + <host name="bogusname4"> + <alias>node4</alias> + </host> + <host name="bogusname5"> + <alias>node5</alias> + </host> + +</hosts> + diff --git a/config-model/src/test/cfg/application/app_complicated_deployment_spec/searchdefinitions/music.sd b/config-model/src/test/cfg/application/app_complicated_deployment_spec/searchdefinitions/music.sd new file mode 100644 index 00000000000..693afbd308d --- /dev/null +++ b/config-model/src/test/cfg/application/app_complicated_deployment_spec/searchdefinitions/music.sd @@ -0,0 +1,37 @@ +# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +# A basic search definition - called music, should be saved to music.sd +search music { + + # It contains one document type only - called music as well + document music { + + field title type string { + indexing: summary | index # How this field should be indexed + # index-to: title, default # Create two indexes + rank-type: about # Type of ranking settings to apply + } + + field artist type string { + indexing: summary | attribute | index + # index-to: artist, default + rank-type:about + } + + field year type int { + indexing: summary | attribute + } + + # Increase rank score of popular documents regardless of query + field popularity type int { + indexing: summary | attribute + } + + field url type uri { + indexing: summary | index + } + + field cover type raw {} + + } + +} diff --git a/config-model/src/test/cfg/application/app_complicated_deployment_spec/services.xml b/config-model/src/test/cfg/application/app_complicated_deployment_spec/services.xml new file mode 100644 index 00000000000..9ae7f122e81 --- /dev/null +++ b/config-model/src/test/cfg/application/app_complicated_deployment_spec/services.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<services version="1.0"> + + <service version="1.0" name="myservice" command="mycmd1.sh"> + <config name="a.myconfig"> + <mysetting>bar</mysetting> + </config> + <node hostalias="node1"> + <config name="a.myconfig"> + <mysetting>baz</mysetting> + </config> + </node> + <node hostalias="node2"/> + <node hostalias="node3"/> + <node hostalias="node3"/> + </service> + + <service version="1.0" name="myotherservice" command="/home/vespa/bin/mycmd2.sh --ytest $FOO_BAR"> + <config name="a.myconfig"> + <mysetting>bar2</mysetting> + </config> + <node hostalias="node3"> + <config name="a.myconfig"> + <mysetting>baz2</mysetting> + </config> + </node> + <node hostalias="node4"/> + <node hostalias="node5"/> + </service> + + <admin version="2.0"> + <adminserver hostalias="node1"/> + <slobroks> + <slobrok hostalias="node1"/> + <slobrok hostalias="node2"/> + </slobroks> + </admin> + + <container version="1.0"> + <nodes> + <node hostalias="node1" /> + </nodes> + + <search/> + <document-api/> + </container> + + <content id="music" version="1.0"> + <redundancy>1</redundancy> + <documents> + <document type="music" mode="index" /> + </documents> + <nodes> + <node hostalias="node1" distribution-key="0" /> + </nodes> + </content> + +</services> diff --git a/config-model/src/test/cfg/application/invalid_parallel_deployment_xml/deployment.xml b/config-model/src/test/cfg/application/invalid_parallel_deployment_xml/deployment.xml index 2f6e9b7a1a6..8e13f0abb53 100644 --- a/config-model/src/test/cfg/application/invalid_parallel_deployment_xml/deployment.xml +++ b/config-model/src/test/cfg/application/invalid_parallel_deployment_xml/deployment.xml @@ -4,9 +4,7 @@ <staging/> <prod global-service-id="query"> <parallel> - <region active="true">us-east-3</region> - <delay hours="1"/> - <region active="true">us-west-1</region> + <instance id="hello" /> </parallel> </prod> </deployment> diff --git a/config-model/src/test/java/com/yahoo/config/model/ApplicationDeployTest.java b/config-model/src/test/java/com/yahoo/config/model/ApplicationDeployTest.java index 8a8b23bb0c8..cb1577417b4 100644 --- a/config-model/src/test/java/com/yahoo/config/model/ApplicationDeployTest.java +++ b/config-model/src/test/java/com/yahoo/config/model/ApplicationDeployTest.java @@ -212,7 +212,14 @@ public class ApplicationDeployTest { } @Test - public void testThatAppWithIllegalEmptyProdRegion() throws IOException { + public void testComplicatedDeploymentSpec() throws IOException { + File tmpDir = tmpFolder.getRoot(); + IOUtils.copyDirectory(new File(TESTDIR, "app_complicated_deployment_spec"), tmpDir); + ApplicationPackageTester.create(tmpDir.getAbsolutePath()); + } + + @Test + public void testAppWithEmptyProdRegion() throws IOException { File tmpDir = tmpFolder.getRoot(); IOUtils.copyDirectory(new File(TESTDIR, "empty_prod_region_in_deployment_xml"), tmpDir); ApplicationPackageTester.create(tmpDir.getAbsolutePath()); @@ -226,7 +233,7 @@ public class ApplicationDeployTest { ApplicationPackageTester.create(tmpDir.getAbsolutePath()); fail("Expected exception"); } catch (IllegalArgumentException e) { - assertEquals("XML error in deployment.xml: element \"delay\" not allowed here; expected the element end-tag or element \"region\" [8:25], input:\n", e.getMessage()); + assertEquals("XML error in deployment.xml: element \"instance\" not allowed here; expected the element end-tag or element \"delay\", \"region\", \"steps\" or \"test\" [7:30], input:\n", e.getMessage()); } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java index 3f985e95a4f..72e2f073453 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java @@ -19,9 +19,6 @@ import com.yahoo.vespa.athenz.api.AthenzPrincipal; import com.yahoo.vespa.athenz.api.AthenzService; import com.yahoo.vespa.athenz.api.AthenzUser; import com.yahoo.vespa.curator.Lock; -import com.yahoo.vespa.flags.BooleanFlag; -import com.yahoo.vespa.flags.FetchVector; -import com.yahoo.vespa.flags.Flags; import com.yahoo.vespa.hosted.controller.api.ActivateResult; import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions; import com.yahoo.vespa.hosted.controller.api.application.v4.model.EndpointStatus; @@ -456,7 +453,7 @@ public class ApplicationController { } for (InstanceName instance : declaredInstances) - if (applicationPackage.deploymentSpec().requireInstance(instance).deploysTo(Environment.prod)) + if (applicationPackage.deploymentSpec().requireInstance(instance).concerns(Environment.prod)) application = withRotation(applicationPackage.deploymentSpec(), application, instance); store(application); @@ -616,8 +613,8 @@ public class ApplicationController { List<ZoneId> deploymentsToRemove = application.get().require(instance).productionDeployments().values().stream() .map(Deployment::zone) .filter(zone -> deploymentSpec.instance(instance).isEmpty() - || ! deploymentSpec.requireInstance(instance).includes(zone.environment(), - Optional.of(zone.region()))) + || ! deploymentSpec.requireInstance(instance).deploysTo(zone.environment(), + zone.region())) .collect(Collectors.toList()); if (deploymentsToRemove.isEmpty()) @@ -647,7 +644,8 @@ public class ApplicationController { for (JobType job : JobList.from(instance).production().mapToList(JobStatus::type)) { ZoneId zone = job.zone(controller.system()); if (deploymentSpec.instance(instance.name()) - .map(spec -> spec.includes(zone.environment(), Optional.of(zone.region()))) + // TODO jonmv: Properly convert to job here. + .map(spec -> spec.deploysTo(zone.environment(), zone.region())) .orElse(false)) continue; instance = instance.withoutDeploymentJob(job); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentSteps.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentSteps.java index 1b722c80ec1..ddfcf17b0be 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentSteps.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentSteps.java @@ -4,7 +4,6 @@ package com.yahoo.vespa.hosted.controller.deployment; import com.yahoo.config.application.api.DeploymentInstanceSpec; import com.yahoo.config.application.api.DeploymentSpec; import com.yahoo.config.provision.Environment; -import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; @@ -91,7 +90,7 @@ public class DeploymentSteps { } private boolean isTest(DeploymentSpec.Step step) { - return step.deploysTo(Environment.test) || step.deploysTo(Environment.staging); + return step.concerns(Environment.test) || step.concerns(Environment.staging); } /** Resolve job from deployment zone */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java index 972e96e94f2..a5a7f6940a3 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java @@ -700,7 +700,7 @@ public class InternalStepRunner implements StepRunner { private static Optional<String> testerFlavorFor(RunId id, DeploymentSpec spec) { for (DeploymentSpec.Step step : spec.steps()) - if (step.deploysTo(id.type().environment())) + if (step.concerns(id.type().environment())) return step.zones().get(0).testerFlavor(); throw new IllegalStateException("No step deploys to the zone this run is for!"); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepository.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepository.java index 6ea7417dffd..8ca8d31b32b 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepository.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepository.java @@ -83,7 +83,7 @@ public class RotationRepository { instance.name() + "'"); } long productionZones = deploymentSpec.requireInstance(instance.name()).zones().stream() - .filter(zone -> zone.deploysTo(Environment.prod)) + .filter(zone -> zone.concerns(Environment.prod)) .count(); if (productionZones < 2) { throw new IllegalArgumentException("global-service-id is set but less than 2 prod zones are defined " + |