summaryrefslogtreecommitdiffstats
path: root/config-model-api
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@yahoo-inc.com>2017-06-01 15:41:00 +0200
committerJon Bratseth <bratseth@yahoo-inc.com>2017-06-01 15:41:00 +0200
commita601c05887df1fd729e600e46ff264f920b79782 (patch)
tree73a3b29e95452d4bd782c9d7e91636263d3d72fe /config-model-api
parent8100948a577da7e399427695d6911c09d038ac04 (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.java112
-rw-r--r--config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java23
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"))));