summaryrefslogtreecommitdiffstats
path: root/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java
diff options
context:
space:
mode:
Diffstat (limited to 'config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java')
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java83
1 files changed, 79 insertions, 4 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 db383a1bea5..e604f27a33a 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,6 +13,7 @@ import java.io.FileReader;
import java.io.Reader;
import java.time.Duration;
import java.time.Instant;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -52,7 +53,13 @@ public class DeploymentSpec {
public DeploymentSpec(List<Step> steps,
Optional<Integer> majorVersion,
String xmlForm) {
- this.steps = steps;
+ if (singleInstance(steps)) { // TODO: Remove this clause after November 2019
+ var singleInstance = (DeploymentInstanceSpec)steps.get(0);
+ this.steps = List.of(singleInstance.withSteps(completeSteps(singleInstance.steps())));
+ }
+ else {
+ this.steps = List.copyOf(completeSteps(steps));
+ }
this.majorVersion = majorVersion;
this.xmlForm = xmlForm;
validateTotalDelay(steps);
@@ -77,6 +84,50 @@ public class DeploymentSpec {
xmlForm);
}
+ /** Adds missing required steps and reorders steps to a permissible order */
+ private static List<DeploymentSpec.Step> completeSteps(List<DeploymentSpec.Step> inputSteps) {
+ 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))) {
+ 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))) {
+ steps.add(new DeploymentSpec.DeclaredZone(Environment.test));
+ }
+
+ // Enforce order test, staging, prod
+ DeploymentSpec.DeclaredZone testStep = remove(Environment.test, steps);
+ if (testStep != null)
+ steps.add(0, testStep);
+ DeploymentSpec.DeclaredZone 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.
+ *
+ * @return the removed step, or null if it is not present
+ */
+ private static DeploymentSpec.DeclaredZone remove(Environment environment, List<DeploymentSpec.Step> steps) {
+ for (int i = 0; i < steps.size(); i++) {
+ if ( ! (steps.get(i) instanceof DeploymentSpec.DeclaredZone)) continue;
+ DeploymentSpec.DeclaredZone zoneStep = (DeploymentSpec.DeclaredZone)steps.get(i);
+ if (zoneStep.environment() == environment) {
+ steps.remove(i);
+ return zoneStep;
+ }
+ }
+ return null;
+ }
+
/** Throw an IllegalArgumentException if the total delay exceeds 24 hours */
private void validateTotalDelay(List<Step> steps) {
long totalDelaySeconds = steps.stream().mapToLong(step -> (step.delay().getSeconds())).sum();
@@ -87,7 +138,7 @@ public class DeploymentSpec {
// TODO: Remove after October 2019
private DeploymentInstanceSpec defaultInstance() {
- if (instances().size() == 1) return (DeploymentInstanceSpec)steps.get(0);
+ if (singleInstance(steps)) return (DeploymentInstanceSpec)steps.get(0);
throw new IllegalArgumentException("This deployment spec does not support the legacy API " +
"as it has multiple instances: " +
instances().stream().map(Step::toString).collect(Collectors.joining(",")));
@@ -113,7 +164,7 @@ public class DeploymentSpec {
/** Returns the deployment steps of this in the order they will be performed */
public List<Step> steps() {
- if (steps.size() == 1) return defaultInstance().steps(); // TODO: Remove line after November 2019
+ if (singleInstance(steps)) return defaultInstance().steps(); // TODO: Remove line after November 2019
return steps;
}
@@ -146,6 +197,11 @@ public class DeploymentSpec {
return defaultInstance().deploysTo(environment, region);
}
+ // TODO: Remove after November 2019
+ private static boolean singleInstance(List<DeploymentSpec.Step> steps) {
+ return steps.size() == 1 && steps.get(0) instanceof DeploymentInstanceSpec;
+ }
+
/** Returns the instance step containing the given instance name, or null if not present */
public DeploymentInstanceSpec instance(String name) {
return instance(InstanceName.from(name));
@@ -281,6 +337,9 @@ public class DeploymentSpec {
/** The delay introduced by this step (beyond the time it takes to execute the step). Default is zero. */
public Duration delay() { return Duration.ZERO; }
+ /** Returns all the steps nested in this. This default implementatiino returns an empty list. */
+ public List<Step> steps() { return List.of(); }
+
}
/** A deployment step which is to wait for some time before progressing to the next step */
@@ -301,6 +360,11 @@ public class DeploymentSpec {
@Override
public boolean deploysTo(Environment environment, Optional<RegionName> region) { return false; }
+ @Override
+ public String toString() {
+ return "delay " + duration;
+ }
+
}
/** A deployment step which is to run deployment in a particular zone */
@@ -400,11 +464,12 @@ public class DeploymentSpec {
}
/** Returns all the steps nested in this */
+ @Override
public List<Step> steps() { return steps; }
@Override
public boolean deploysTo(Environment environment, Optional<RegionName> region) {
- return zones().stream().anyMatch(zone -> zone.deploysTo(environment, region));
+ return steps().stream().anyMatch(zone -> zone.deploysTo(environment, region));
}
@Override
@@ -420,6 +485,11 @@ public class DeploymentSpec {
return Objects.hash(steps);
}
+ @Override
+ public String toString() {
+ return steps.size() + " parallel steps";
+ }
+
}
/** Controls when this application will be upgraded to new Vespa versions */
@@ -448,6 +518,11 @@ public class DeploymentSpec {
public boolean blocksRevisions() { return revision; }
public boolean blocksVersions() { return version; }
public TimeWindow window() { return window; }
+
+ @Override
+ public String toString() {
+ return "change blocker revision=" + revision + " version=" + version + " window=" + window;
+ }
}