diff options
author | Jon Bratseth <bratseth@yahoo-inc.com> | 2017-06-01 15:41:00 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@yahoo-inc.com> | 2017-06-01 15:41:00 +0200 |
commit | a601c05887df1fd729e600e46ff264f920b79782 (patch) | |
tree | 73a3b29e95452d4bd782c9d7e91636263d3d72fe /config-model-api | |
parent | 8100948a577da7e399427695d6911c09d038ac04 (diff) |
Add required steps at construction
Diffstat (limited to 'config-model-api')
-rw-r--r-- | config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java | 112 | ||||
-rw-r--r-- | config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java | 23 |
2 files changed, 95 insertions, 40 deletions
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 3c00702bd8f..cf4bff64ccd 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 @@ -12,7 +12,7 @@ import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.io.Reader; -import java.io.UncheckedIOException; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -23,6 +23,9 @@ import java.util.Optional; * This may be used both for inspection as part of an application model and to answer * queries about deployment from the command line. A main method is included for the latter usage. * + * A deployment consists of a number of steps executed in the order listed in deployment xml, as + * well as some additional settings. + * * This is immutable. * * @author bratseth @@ -37,20 +40,57 @@ public class DeploymentSpec { private final Optional<String> globalServiceId; private final UpgradePolicy upgradePolicy; - private final List<DeclaredZone> zones; + private final List<ZoneDeployment> zones; private final String xmlForm; - public DeploymentSpec(Optional<String> globalServiceId, UpgradePolicy upgradePolicy, List<DeclaredZone> zones) { + public DeploymentSpec(Optional<String> globalServiceId, UpgradePolicy upgradePolicy, List<ZoneDeployment> zones) { this(globalServiceId, upgradePolicy, zones, null); } - private DeploymentSpec(Optional<String> globalServiceId, UpgradePolicy upgradePolicy, - List<DeclaredZone> zones, String xmlForm) { + private DeploymentSpec(Optional<String> globalServiceId, UpgradePolicy upgradePolicy, + List<ZoneDeployment> zones, String xmlForm) { this.globalServiceId = globalServiceId; this.upgradePolicy = upgradePolicy; - this.zones = ImmutableList.copyOf(zones); + this.zones = ImmutableList.copyOf(completeSteps(new ArrayList<>(zones))); this.xmlForm = xmlForm; } + + /** Adds missing required steps and reorders steps to a permissible order */ + private static List<ZoneDeployment> completeSteps(List<ZoneDeployment> steps) { + // Add staging if required and missing + if (steps.stream().anyMatch(step -> step.environment() == Environment.prod) && + steps.stream().noneMatch(step -> step.environment() == Environment.staging)) + steps.add(new ZoneDeployment(Environment.staging)); + + // Add test if required and missing + if (steps.stream().anyMatch(step -> step.environment() == Environment.staging) && + steps.stream().noneMatch(step -> step.environment() == Environment.test)) + steps.add(new ZoneDeployment(Environment.test)); + + // Enforce order test, staging, prod + ZoneDeployment testStep = remove(Environment.test, steps); + if (testStep != null) + steps.add(0, testStep); + ZoneDeployment stagingStep = remove(Environment.staging, steps); + if (stagingStep != null) + steps.add(1, stagingStep); + + return steps; + } + + /** + * Removes the first occurrence of a deployment step to the given environment and returns it. + * + * @param environment + * @return the removed step, or null if it is not present + */ + private static ZoneDeployment remove(Environment environment, List<ZoneDeployment> steps) { + for (int i = 0; i < steps.size(); i++) { + if (steps.get(i).environment() == environment) + return steps.remove(i); + } + return null; + } /** Returns the ID of the service to expose through global routing, if present */ public Optional<String> globalServiceId() { @@ -61,15 +101,15 @@ public class DeploymentSpec { public UpgradePolicy upgradePolicy() { return upgradePolicy; } /** Returns the zones this declares as a read-only list. */ - public List<DeclaredZone> zones() { return zones; } + public List<ZoneDeployment> zones() { return zones; } /** Returns the XML form of this spec, or null if it was not created by fromXml or is the empty spec */ public String xmlForm() { return xmlForm; } /** Returns whether this deployment spec specifies the given zone, either implicitly or explicitly */ public boolean includes(Environment environment, Optional<RegionName> region) { - for (DeclaredZone declaredZone : zones) - if (declaredZone.matches(environment, region)) return true; + for (ZoneDeployment zoneDeployment : zones) + if (zoneDeployment.matches(environment, region)) return true; return false; } @@ -93,7 +133,7 @@ public class DeploymentSpec { * @throws IllegalArgumentException if the XML is invalid */ public static DeploymentSpec fromXml(String xmlForm) { - List<DeclaredZone> zones = new ArrayList<>(); + List<ZoneDeployment> zones = new ArrayList<>(); Optional<String> globalServiceId = Optional.empty(); Element root = XML.getDocument(xmlForm).getDocumentElement(); for (Element environmentTag : XML.getChildren(root)) { @@ -101,12 +141,12 @@ public class DeploymentSpec { Environment environment = Environment.from(environmentTag.getTagName()); List<Element> regionTags = XML.getChildren(environmentTag, "region"); if (regionTags.isEmpty()) { - zones.add(new DeclaredZone(environment, Optional.empty(), false)); + zones.add(new ZoneDeployment(environment, Optional.empty(), false)); } else { for (Element regionTag : regionTags) { RegionName region = RegionName.from(XML.getValue(regionTag).trim()); boolean active = environment == Environment.prod && readActive(regionTag); - zones.add(new DeclaredZone(environment, Optional.of(region), active)); + zones.add(new ZoneDeployment(environment, Optional.of(region), active)); } } @@ -173,7 +213,7 @@ public class DeploymentSpec { } /** This may be invoked by a continuous build */ - public static void main (String[] args) { + public static void main(String[] args) { if (args.length != 2 && args.length != 3) { System.err.println("Usage: DeploymentSpec [file] [environment] [region]?" + "Returns 0 if the specified zone matches the deployment spec, 1 otherwise"); @@ -195,7 +235,26 @@ public class DeploymentSpec { } } - public static class DeclaredZone { + /** A delpoyment step */ + public abstract static class Step { + + } + + /** A deployment step which is to wait for some time before progressing to the next step */ + public static class Delay extends Step { + + private final Duration duration; + + public Delay(Duration duration) { + this.duration = duration; + } + + public Duration duration() { return duration; } + + } + + /** A deployment step which is to run deployment in a particular zone */ + public static class ZoneDeployment extends Step { private final Environment environment; @@ -203,7 +262,13 @@ public class DeploymentSpec { private final boolean active; - public DeclaredZone(Environment environment, Optional<RegionName> region, boolean active) { + public ZoneDeployment(Environment environment) { + this(environment, Optional.empty(), true); + } + + public ZoneDeployment(Environment environment, Optional<RegionName> region, boolean active) { + if (environment == Environment.prod && ! region.isPresent()) + throw new IllegalArgumentException("Prod environments must be specified with a region"); this.environment = environment; this.region = region; this.active = active; @@ -218,22 +283,7 @@ public class DeploymentSpec { public boolean active() { return active; } public boolean matches(Environment environment, Optional<RegionName> region) { - if (environment.equals(this.environment) && region.equals(this.region)) return true; - if ( ! region.isPresent() && prerequisite(environment)) return true; - return false; - } - - /** - * Returns whether deployment in the given environment is a prerequisite of deployment in this environment - * - * The required progression leading to prerequisites is test, staging, prod. - */ - private boolean prerequisite(Environment environment) { - if (this.environment == Environment.prod) - return environment == Environment.staging || environment == Environment.test; - if (this.environment == Environment.staging) - return environment == Environment.test; - return false; + return environment.equals(this.environment) && region.equals(this.region); } } 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 ffde5b21458..76d395ef95b 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 @@ -44,8 +44,9 @@ public class DeploymentSpecTest { ); DeploymentSpec spec = DeploymentSpec.fromXml(r); - assertEquals(1, spec.zones().size()); - assertEquals(Environment.staging, spec.zones().get(0).environment()); + assertEquals(2, spec.zones().size()); + assertEquals(Environment.test, spec.zones().get(0).environment()); + assertEquals(Environment.staging, spec.zones().get(1).environment()); assertTrue(spec.includes(Environment.test, Optional.empty())); assertFalse(spec.includes(Environment.test, Optional.of(RegionName.from("region1")))); assertTrue(spec.includes(Environment.staging, Optional.empty())); @@ -65,15 +66,19 @@ public class DeploymentSpecTest { ); DeploymentSpec spec = DeploymentSpec.fromXml(r); - assertEquals(2, spec.zones().size()); + assertEquals(4, spec.zones().size()); + + assertEquals(Environment.test, spec.zones().get(0).environment()); - assertEquals(Environment.prod, spec.zones().get(0).environment()); - assertEquals("us-east1", spec.zones().get(0).region().get().value()); - assertFalse(spec.zones().get(0).active()); + assertEquals(Environment.staging, spec.zones().get(1).environment()); + + assertEquals(Environment.prod, spec.zones().get(2).environment()); + assertEquals("us-east1", spec.zones().get(2).region().get().value()); + assertFalse(spec.zones().get(2).active()); - assertEquals(Environment.prod, spec.zones().get(1).environment()); - assertEquals("us-west1", spec.zones().get(1).region().get().value()); - assertTrue(spec.zones().get(1).active()); + assertEquals(Environment.prod, spec.zones().get(3).environment()); + assertEquals("us-west1", spec.zones().get(3).region().get().value()); + assertTrue(spec.zones().get(3).active()); assertTrue(spec.includes(Environment.test, Optional.empty())); assertFalse(spec.includes(Environment.test, Optional.of(RegionName.from("region1")))); |