summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2019-11-06 14:31:10 +0100
committerGitHub <noreply@github.com>2019-11-06 14:31:10 +0100
commit4187b1391bf383028a3ffd1480aacd70c1db287b (patch)
tree29d6249bed368f51752941c7207e94315305e736
parentb678895ffe2a92cbcf720a698e2a58f1d22c2d9a (diff)
parentaeedefb11672970b69f97cbe9307aba9180cf620 (diff)
Merge pull request #11216 from vespa-engine/jvenstad/remove-more-deprecated-deployment-spec-usage
Jvenstad/remove more deprecated deployment spec usage
-rw-r--r--config-model-api/abi-spec.json9
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java117
-rw-r--r--config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecDeprecatedAPITest.java355
-rw-r--r--config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java42
-rw-r--r--config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecWithoutInstanceTest.java34
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationPackageValidator.java20
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java1
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentSteps.java51
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java34
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java20
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java137
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandler.java5
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java16
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java11
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java7
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java34
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java9
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/metric/ConfigServerMetricsTest.java3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java15
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java1
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java10
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application2-with-patches.json4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application2.json4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance-with-routing-policy.json2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance-without-change-multiple-deployments.json2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance.json2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs.json22
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-job.json4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandlerTest.java11
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiTest.java9
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiTest.java11
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java38
35 files changed, 324 insertions, 724 deletions
diff --git a/config-model-api/abi-spec.json b/config-model-api/abi-spec.json
index f4796372722..edf83fe4475 100644
--- a/config-model-api/abi-spec.json
+++ b/config-model-api/abi-spec.json
@@ -239,8 +239,6 @@
],
"methods": [
"public void <init>(com.yahoo.config.provision.Environment)",
- "public void <init>(com.yahoo.config.provision.Environment, java.util.Optional, boolean)",
- "public void <init>(com.yahoo.config.provision.Environment, java.util.Optional, boolean, java.util.Optional)",
"public void <init>(com.yahoo.config.provision.Environment, java.util.Optional, boolean, java.util.Optional, java.util.Optional)",
"public com.yahoo.config.provision.Environment environment()",
"public java.util.Optional region()",
@@ -263,7 +261,6 @@
],
"methods": [
"public void <init>(java.time.Duration)",
- "public java.time.Duration duration()",
"public java.time.Duration delay()",
"public boolean deploysTo(com.yahoo.config.provision.Environment, java.util.Optional)",
"public java.lang.String toString()"
@@ -330,15 +327,12 @@
],
"methods": [
"public void <init>(java.util.List, java.util.Optional, java.util.Optional, java.util.Optional, java.lang.String)",
- "public void <init>(java.util.Optional, com.yahoo.config.application.api.DeploymentSpec$UpgradePolicy, java.util.Optional, java.util.List, java.util.List, java.lang.String, java.util.Optional, java.util.Optional, com.yahoo.config.application.api.Notifications, java.util.List)",
"public java.util.Optional majorVersion()",
"public java.util.List steps()",
- "public java.util.List zones()",
"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 boolean includes(com.yahoo.config.provision.Environment, java.util.Optional)",
"public java.util.Optional instance(com.yahoo.config.provision.InstanceName)",
"public com.yahoo.config.application.api.DeploymentInstanceSpec requireInstance(java.lang.String)",
"public com.yahoo.config.application.api.DeploymentInstanceSpec requireInstance(com.yahoo.config.provision.InstanceName)",
@@ -349,8 +343,7 @@
"public static com.yahoo.config.application.api.DeploymentSpec fromXml(java.lang.String, boolean)",
"public static java.lang.String toMessageString(java.lang.Throwable)",
"public boolean equals(java.lang.Object)",
- "public int hashCode()",
- "public static void main(java.lang.String[])"
+ "public int hashCode()"
],
"fields": [
"public static final com.yahoo.config.application.api.DeploymentSpec empty"
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 25fd766c8c2..ea7df677e0f 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
@@ -9,11 +9,8 @@ import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.RegionName;
-import java.io.BufferedReader;
-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;
@@ -36,16 +33,19 @@ import java.util.stream.Collectors;
public class DeploymentSpec {
/** The empty deployment spec, specifying no zones or rotation, and defaults for all settings */
- public static final DeploymentSpec empty = new DeploymentSpec(Optional.empty(),
- UpgradePolicy.defaultPolicy,
+ public static final DeploymentSpec empty = new DeploymentSpec(List.of(new DeploymentInstanceSpec(InstanceName.from("default"),
+ Collections.emptyList(),
+ UpgradePolicy.defaultPolicy,
+ Collections.emptyList(),
+ Optional.empty(),
+ Optional.empty(),
+ Optional.empty(),
+ Notifications.none(),
+ List.of())),
Optional.empty(),
- Collections.emptyList(),
- Collections.emptyList(),
- "<deployment version='1.0'/>",
Optional.empty(),
Optional.empty(),
- Notifications.none(),
- List.of());
+ "<deployment version='1.0'/>");
private final List<Step> steps;
@@ -61,13 +61,7 @@ public class DeploymentSpec {
Optional<AthenzDomain> athenzDomain,
Optional<AthenzService> athenzService,
String xmlForm) {
- if (hasSingleInstance(steps)) { // TODO: Remove this clause after November 2019
- var singleInstance = singleInstance(steps);
- this.steps = List.of(singleInstance.withSteps(completeSteps(singleInstance.steps())));
- }
- else {
- this.steps = List.copyOf(completeSteps(steps));
- }
+ this.steps = List.copyOf(completeSteps(steps));
this.majorVersion = majorVersion;
this.athenzDomain = athenzDomain;
this.athenzService = athenzService;
@@ -76,27 +70,6 @@ public class DeploymentSpec {
validateUpgradePoliciesOfIncreasingConservativeness(steps);
}
- // TODO: Remove after October 2019
- public DeploymentSpec(Optional<String> globalServiceId, UpgradePolicy upgradePolicy, Optional<Integer> majorVersion,
- List<ChangeBlocker> changeBlockers, List<Step> steps, String xmlForm,
- Optional<AthenzDomain> athenzDomain, Optional<AthenzService> athenzService,
- Notifications notifications,
- List<Endpoint> endpoints) {
- this(List.of(new DeploymentInstanceSpec(InstanceName.from("default"),
- steps,
- upgradePolicy,
- changeBlockers,
- globalServiceId,
- athenzDomain,
- athenzService,
- notifications,
- endpoints)),
- majorVersion,
- athenzDomain,
- athenzService,
- 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);
@@ -166,36 +139,14 @@ public class DeploymentSpec {
}
}
- // TODO: Remove after October 2019
- private DeploymentInstanceSpec singleInstance() {
- return singleInstance(steps);
- }
-
- // TODO: Remove after October 2019
- private static DeploymentInstanceSpec singleInstance(List<DeploymentSpec.Step> steps) {
- List<DeploymentInstanceSpec> instances = instances(steps);
- if (instances.size() == 1) return instances.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(",")));
- }
-
/** Returns the major version this application is pinned to, or empty (default) to allow all major versions */
public Optional<Integer> majorVersion() { return majorVersion; }
/** Returns the deployment steps of this in the order they will be performed */
public List<Step> steps() {
- if (hasSingleInstance(steps)) return singleInstance().steps(); // TODO: Remove line after November 2019
return steps;
}
- // TODO: Remove after November 2019
- public List<DeclaredZone> zones() {
- return singleInstance().steps().stream()
- .flatMap(step -> step.zones().stream())
- .collect(Collectors.toList());
- }
-
/** Returns the Athenz domain set on the root tag, if any */
public Optional<AthenzDomain> athenzDomain() { return athenzDomain; }
@@ -221,16 +172,6 @@ public class DeploymentSpec {
/** Returns the XML form of this spec, or null if it was not created by fromXml, nor is empty */
public String xmlForm() { return xmlForm; }
- // TODO: Remove after November 2019
- public boolean includes(Environment environment, Optional<RegionName> region) {
- return singleInstance().deploysTo(environment, region);
- }
-
- // TODO: Remove after November 2019
- private static boolean hasSingleInstance(List<DeploymentSpec.Step> steps) {
- return instances(steps).size() == 1;
- }
-
/** Returns the instance step containing the given instance name */
public Optional<DeploymentInstanceSpec> instance(InstanceName name) {
for (DeploymentInstanceSpec instance : instances()) {
@@ -328,29 +269,6 @@ public class DeploymentSpec {
return Objects.hash(majorVersion, steps, xmlForm);
}
- /** This may be invoked by a continuous build */
- 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");
- System.exit(1);
- }
-
- try (BufferedReader reader = new BufferedReader(new FileReader(args[0]))) {
- DeploymentSpec spec = DeploymentSpec.fromXml(reader);
- Environment environment = Environment.from(args[1]);
- Optional<RegionName> region = args.length == 3 ? Optional.of(RegionName.from(args[2])) : Optional.empty();
- if (spec.includes(environment, region))
- System.exit(0);
- else
- System.exit(1);
- }
- catch (Exception e) {
- System.err.println("Exception checking deployment spec: " + toMessageString(e));
- System.exit(1);
- }
- }
-
/** A deployment step */
public abstract static class Step {
@@ -382,9 +300,6 @@ public class DeploymentSpec {
this.duration = duration;
}
- // TODO: Remove after October 2019
- public Duration duration() { return duration; }
-
@Override
public Duration delay() { return duration; }
@@ -408,15 +323,7 @@ public class DeploymentSpec {
private final Optional<String> testerFlavor;
public DeclaredZone(Environment environment) {
- this(environment, Optional.empty(), false);
- }
-
- public DeclaredZone(Environment environment, Optional<RegionName> region, boolean active) {
- this(environment, region, active, Optional.empty(), Optional.empty());
- }
-
- public DeclaredZone(Environment environment, Optional<RegionName> region, boolean active, Optional<AthenzService> athenzService) {
- this(environment, region, active, athenzService, Optional.empty());
+ this(environment, Optional.empty(), false, Optional.empty(), Optional.empty());
}
public DeclaredZone(Environment environment, Optional<RegionName> region, boolean active,
diff --git a/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecDeprecatedAPITest.java b/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecDeprecatedAPITest.java
deleted file mode 100644
index 317a4224726..00000000000
--- a/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecDeprecatedAPITest.java
+++ /dev/null
@@ -1,355 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.config.application.api;
-
-import com.google.common.collect.ImmutableSet;
-import com.yahoo.config.provision.Environment;
-import com.yahoo.config.provision.RegionName;
-import org.junit.Test;
-
-import java.io.StringReader;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static com.yahoo.config.application.api.Notifications.Role.author;
-import static com.yahoo.config.application.api.Notifications.When.failing;
-import static com.yahoo.config.application.api.Notifications.When.failingCommit;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-/**
- * @author bratseth
- */
-// TODO: Remove after November 2019
-public class DeploymentSpecDeprecatedAPITest {
-
- @Test
- public void testSpec() {
- String specXml = "<deployment version='1.0'>" +
- " <test/>" +
- "</deployment>";
-
- StringReader r = new StringReader(specXml);
- DeploymentSpec spec = DeploymentSpec.fromXml(r);
- assertEquals(specXml, spec.xmlForm());
- assertEquals(1, spec.steps().size());
- assertFalse(spec.majorVersion().isPresent());
- assertTrue(spec.steps().get(0).deploysTo(Environment.test));
- assertTrue(spec.includes(Environment.test, Optional.empty()));
- assertFalse(spec.includes(Environment.test, Optional.of(RegionName.from("region1"))));
- assertFalse(spec.includes(Environment.staging, Optional.empty()));
- assertFalse(spec.includes(Environment.prod, Optional.empty()));
- }
-
- @Test
- public void testSpecPinningMajorVersion() {
- String specXml = "<deployment version='1.0' major-version='6'>" +
- " <test/>" +
- "</deployment>";
-
- StringReader r = new StringReader(specXml);
- DeploymentSpec spec = DeploymentSpec.fromXml(r);
- assertEquals(specXml, spec.xmlForm());
- assertEquals(1, spec.steps().size());
- assertTrue(spec.majorVersion().isPresent());
- assertEquals(6, (int)spec.majorVersion().get());
- }
-
- @Test
- public void stagingSpec() {
- StringReader r = new StringReader(
- "<deployment version='1.0'>" +
- " <staging/>" +
- "</deployment>"
- );
-
- DeploymentSpec spec = DeploymentSpec.fromXml(r);
- assertEquals(2, spec.steps().size());
- assertTrue(spec.steps().get(0).deploysTo(Environment.test));
- assertTrue(spec.steps().get(1).deploysTo(Environment.staging));
- assertTrue(spec.includes(Environment.test, Optional.empty()));
- assertFalse(spec.includes(Environment.test, Optional.of(RegionName.from("region1"))));
- assertTrue(spec.includes(Environment.staging, Optional.empty()));
- assertFalse(spec.includes(Environment.prod, Optional.empty()));
- }
-
- @Test
- public void minimalProductionSpec() {
- StringReader r = new StringReader(
- "<deployment version='1.0'>" +
- " <prod>" +
- " <region active='false'>us-east1</region>" +
- " <region active='true'>us-west1</region>" +
- " </prod>" +
- "</deployment>"
- );
-
- DeploymentSpec spec = DeploymentSpec.fromXml(r);
- assertEquals(4, spec.steps().size());
-
- assertTrue(spec.steps().get(0).deploysTo(Environment.test));
-
- assertTrue(spec.steps().get(1).deploysTo(Environment.staging));
-
- assertTrue(spec.steps().get(2).deploysTo(Environment.prod, Optional.of(RegionName.from("us-east1"))));
- assertFalse(((DeploymentSpec.DeclaredZone)spec.steps().get(2)).active());
-
- assertTrue(spec.steps().get(3).deploysTo(Environment.prod, Optional.of(RegionName.from("us-west1"))));
- assertTrue(((DeploymentSpec.DeclaredZone)spec.steps().get(3)).active());
-
- assertTrue(spec.includes(Environment.test, Optional.empty()));
- assertFalse(spec.includes(Environment.test, Optional.of(RegionName.from("region1"))));
- assertTrue(spec.includes(Environment.staging, Optional.empty()));
- assertTrue(spec.includes(Environment.prod, Optional.of(RegionName.from("us-east1"))));
- assertTrue(spec.includes(Environment.prod, Optional.of(RegionName.from("us-west1"))));
- assertFalse(spec.includes(Environment.prod, Optional.of(RegionName.from("no-such-region"))));
- }
-
- @Test
- public void deploymentSpecWithTest() {
- StringReader r = new StringReader(
- "<deployment version='1.0'>" +
- " <test/>" +
- " <staging/>" +
- " <prod>" +
- " <region active='false'>us-east1</region>" +
- " <region active='true'>us-west1</region>" +
- " </prod>" +
- "</deployment>"
- );
-
- DeploymentSpec spec = DeploymentSpec.fromXml(r);
- assertEquals("[test, staging, prod.us-east1, prod.us-west1]", spec.steps().toString());
- }
-
- @Test
- public void deploymentSpecWithTestInsideInstance() {
- StringReader r = new StringReader(
- "<deployment version='1.0'>" +
- " <instance id='instance1'>" +
- " <test/>" +
- " <staging/>" +
- " <prod>" +
- " <region active='false'>us-east1</region>" +
- " <region active='true'>us-west1</region>" +
- " </prod>" +
- " </instance>" +
- "</deployment>"
- );
-
- DeploymentSpec spec = DeploymentSpec.fromXml(r);
- assertEquals("[test, staging, prod.us-east1, prod.us-west1]", spec.steps().toString());
- }
-
- @Test
- public void deploymentSpecWithTestOutsideInstance() {
- StringReader r = new StringReader(
- "<deployment version='1.0'>" +
- " <test/>" +
- " <staging/>" +
- " <instance id='instance1'>" +
- " <prod>" +
- " <region active='false'>us-east1</region>" +
- " <region active='true'>us-west1</region>" +
- " </prod>" +
- " </instance>" +
- "</deployment>"
- );
-
- DeploymentSpec spec = DeploymentSpec.fromXml(r);
- assertEquals("[test, staging, prod.us-east1, prod.us-west1]", spec.steps().toString());
- }
-
- @Test
- public void maximalProductionSpec() {
- StringReader r = new StringReader(
- "<deployment version='1.0'>" +
- " <test/>" +
- " <staging/>" +
- " <prod>" +
- " <region active='false'>us-east1</region>" +
- " <delay hours='3' minutes='30'/>" +
- " <region active='true'>us-west1</region>" +
- " </prod>" +
- "</deployment>"
- );
-
- DeploymentSpec spec = DeploymentSpec.fromXml(r);
- assertEquals(5, spec.steps().size());
- assertEquals(4, spec.zones().size());
-
- assertTrue(spec.steps().get(0).deploysTo(Environment.test));
-
- assertTrue(spec.steps().get(1).deploysTo(Environment.staging));
-
- assertTrue(spec.steps().get(2).deploysTo(Environment.prod, Optional.of(RegionName.from("us-east1"))));
- assertFalse(((DeploymentSpec.DeclaredZone)spec.steps().get(2)).active());
-
- assertTrue(spec.steps().get(3) instanceof DeploymentSpec.Delay);
- assertEquals(3 * 60 * 60 + 30 * 60, ((DeploymentSpec.Delay)spec.steps().get(3)).duration().getSeconds());
-
- assertTrue(spec.steps().get(4).deploysTo(Environment.prod, Optional.of(RegionName.from("us-west1"))));
- assertTrue(((DeploymentSpec.DeclaredZone)spec.steps().get(4)).active());
-
- assertTrue(spec.includes(Environment.test, Optional.empty()));
- assertFalse(spec.includes(Environment.test, Optional.of(RegionName.from("region1"))));
- assertTrue(spec.includes(Environment.staging, Optional.empty()));
- assertTrue(spec.includes(Environment.prod, Optional.of(RegionName.from("us-east1"))));
- assertTrue(spec.includes(Environment.prod, Optional.of(RegionName.from("us-west1"))));
- assertFalse(spec.includes(Environment.prod, Optional.of(RegionName.from("no-such-region"))));
- }
-
- @Test
- public void productionSpecWithGlobalServiceId() {
- StringReader r = new StringReader(
- "<deployment version='1.0'>" +
- " <prod global-service-id='query'>" +
- " <region active='true'>us-east-1</region>" +
- " <region active='true'>us-west-1</region>" +
- " </prod>" +
- "</deployment>"
- );
-
- DeploymentSpec spec = DeploymentSpec.fromXml(r);
- }
-
- @Test(expected=IllegalArgumentException.class)
- public void globalServiceIdInTest() {
- StringReader r = new StringReader(
- "<deployment version='1.0'>" +
- " <test global-service-id='query' />" +
- "</deployment>"
- );
- DeploymentSpec spec = DeploymentSpec.fromXml(r);
- }
-
- @Test(expected=IllegalArgumentException.class)
- public void globalServiceIdInStaging() {
- StringReader r = new StringReader(
- "<deployment version='1.0'>" +
- " <staging global-service-id='query' />" +
- "</deployment>"
- );
- DeploymentSpec spec = DeploymentSpec.fromXml(r);
- }
-
- @Test
- public void maxDelayExceeded() {
- try {
- StringReader r = new StringReader(
- "<deployment>" +
- " <upgrade policy='canary'/>" +
- " <prod>" +
- " <region active='true'>us-west-1</region>" +
- " <delay hours='23'/>" +
- " <region active='true'>us-central-1</region>" +
- " <delay minutes='59' seconds='61'/>" +
- " <region active='true'>us-east-3</region>" +
- " </prod>" +
- "</deployment>"
- );
- DeploymentSpec.fromXml(r);
- fail("Expected exception due to exceeding the max total delay");
- }
- catch (IllegalArgumentException e) {
- // success
- assertEquals("The total delay specified is PT24H1S but max 24 hours is allowed", e.getMessage());
- }
- }
-
- @Test
- public void testEmpty() {
- assertTrue(DeploymentSpec.empty.steps().isEmpty());
- assertEquals("<deployment version='1.0'/>", DeploymentSpec.empty.xmlForm());
- }
-
- @Test
- public void productionSpecWithParallelDeployments() {
- StringReader r = new StringReader(
- "<deployment>\n" +
- " <prod> \n" +
- " <region active='true'>us-west-1</region>\n" +
- " <parallel>\n" +
- " <region active='true'>us-central-1</region>\n" +
- " <region active='true'>us-east-3</region>\n" +
- " </parallel>\n" +
- " </prod>\n" +
- "</deployment>"
- );
- DeploymentSpec spec = DeploymentSpec.fromXml(r);
- DeploymentSpec.ParallelZones parallelZones = ((DeploymentSpec.ParallelZones) spec.steps().get(3));
- 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());
- }
-
- @Test
- public void productionSpecWithDuplicateRegions() {
- StringReader r = new StringReader(
- "<deployment>\n" +
- " <prod>\n" +
- " <region active='true'>us-west-1</region>\n" +
- " <parallel>\n" +
- " <region active='true'>us-west-1</region>\n" +
- " <region active='true'>us-central-1</region>\n" +
- " <region active='true'>us-east-3</region>\n" +
- " </parallel>\n" +
- " </prod>\n" +
- "</deployment>"
- );
- try {
- DeploymentSpec.fromXml(r);
- fail("Expected exception");
- } catch (IllegalArgumentException e) {
- assertEquals("prod.us-west-1 is listed twice in deployment.xml", e.getMessage());
- }
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void deploymentSpecWithIllegallyOrderedDeploymentSpec1() {
- StringReader r = new StringReader(
- "<deployment>\n" +
- " <block-change days='sat' hours='10' time-zone='CET'/>\n" +
- " <prod>\n" +
- " <region active='true'>us-west-1</region>\n" +
- " </prod>\n" +
- " <block-change days='mon,tue' hours='15-16'/>\n" +
- "</deployment>"
- );
- DeploymentSpec.fromXml(r);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void deploymentSpecWithIllegallyOrderedDeploymentSpec2() {
- StringReader r = new StringReader(
- "<deployment>\n" +
- " <block-change days='sat' hours='10' time-zone='CET'/>\n" +
- " <test/>\n" +
- " <prod>\n" +
- " <region active='true'>us-west-1</region>\n" +
- " </prod>\n" +
- "</deployment>"
- );
- DeploymentSpec.fromXml(r);
- }
-
- @Test
- public void customTesterFlavor() {
- DeploymentSpec spec = DeploymentSpec.fromXml("<deployment>\n" +
- " <test tester-flavor=\"d-1-4-20\" />\n" +
- " <prod tester-flavor=\"d-2-8-50\">\n" +
- " <region active=\"false\">us-north-7</region>\n" +
- " </prod>\n" +
- "</deployment>");
- assertEquals(Optional.of("d-1-4-20"), spec.steps().get(0).zones().get(0).testerFlavor());
- assertEquals(Optional.empty(), spec.steps().get(1).zones().get(0).testerFlavor());
- assertEquals(Optional.of("d-2-8-50"), spec.steps().get(2).zones().get(0).testerFlavor());
- }
-
-}
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 f181b02a071..987e13c0e86 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
@@ -77,11 +77,11 @@ public class DeploymentSpecTest {
);
DeploymentSpec spec = DeploymentSpec.fromXml(r);
- assertEquals(2, spec.requireInstance("default").steps().size());
- assertTrue(spec.requireInstance("default").steps().get(0).deploysTo(Environment.test));
- assertTrue(spec.requireInstance("default").steps().get(1).deploysTo(Environment.staging));
- assertTrue(spec.requireInstance("default").includes(Environment.test, Optional.empty()));
- assertFalse(spec.requireInstance("default").includes(Environment.test, Optional.of(RegionName.from("region1"))));
+ 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()));
assertFalse(spec.requireInstance("default").globalServiceId().isPresent());
@@ -101,21 +101,21 @@ public class DeploymentSpecTest {
);
DeploymentSpec spec = DeploymentSpec.fromXml(r);
- assertEquals(4, spec.requireInstance("default").steps().size());
+ assertEquals(3, spec.steps().size());
+ assertEquals(2, spec.requireInstance("default").steps().size());
- assertTrue(spec.requireInstance("default").steps().get(0).deploysTo(Environment.test));
+ assertTrue(spec.steps().get(0).deploysTo(Environment.test));
- assertTrue(spec.requireInstance("default").steps().get(1).deploysTo(Environment.staging));
+ assertTrue(spec.steps().get(1).deploysTo(Environment.staging));
- assertTrue(spec.requireInstance("default").steps().get(2).deploysTo(Environment.prod, Optional.of(RegionName.from("us-east1"))));
- assertFalse(((DeploymentSpec.DeclaredZone)spec.requireInstance("default").steps().get(2)).active());
+ assertTrue(spec.requireInstance("default").steps().get(0).deploysTo(Environment.prod, Optional.of(RegionName.from("us-east1"))));
+ assertFalse(((DeploymentSpec.DeclaredZone)spec.requireInstance("default").steps().get(0)).active());
- assertTrue(spec.requireInstance("default").steps().get(3).deploysTo(Environment.prod, Optional.of(RegionName.from("us-west1"))));
- assertTrue(((DeploymentSpec.DeclaredZone)spec.requireInstance("default").steps().get(3)).active());
+ assertTrue(spec.requireInstance("default").steps().get(1).deploysTo(Environment.prod, Optional.of(RegionName.from("us-west1"))));
+ assertTrue(((DeploymentSpec.DeclaredZone)spec.requireInstance("default").steps().get(1)).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()));
+ 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"))));
@@ -351,7 +351,8 @@ public class DeploymentSpecTest {
@Test
public void testEmpty() {
assertFalse(DeploymentSpec.empty.requireInstance("default").globalServiceId().isPresent());
- assertTrue(DeploymentSpec.empty.steps().isEmpty());
+ assertTrue(DeploymentSpec.empty.requireInstance("default").steps().isEmpty());
+ assertEquals(1, DeploymentSpec.empty.steps().size());
assertEquals("<deployment version='1.0'/>", DeploymentSpec.empty.xmlForm());
}
@@ -371,7 +372,7 @@ public class DeploymentSpecTest {
"</deployment>"
);
DeploymentSpec spec = DeploymentSpec.fromXml(r);
- DeploymentSpec.ParallelZones parallelZones = ((DeploymentSpec.ParallelZones) spec.requireInstance("default").steps().get(3));
+ 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());
@@ -902,6 +903,7 @@ public class DeploymentSpecTest {
DeploymentSpec spec = DeploymentSpec.fromXml("<deployment>" +
" <instance id='default'>" +
" <test tester-flavor=\"d-1-4-20\" />" +
+ " <staging />" +
" <prod tester-flavor=\"d-2-8-50\">" +
" <region active=\"false\">us-north-7</region>" +
" </prod>" +
@@ -995,9 +997,9 @@ public class DeploymentSpecTest {
" </prod>" +
" <endpoints>" +
" <endpoint id=\"foo\" container-id=\"bar\">" +
- " <region>us-east</region>" +
- " </endpoint>" +
- " <endpoint id=\"nalle\" container-id=\"frosk\" />" +
+ " <region>us-east</region>" +
+ " </endpoint>" +
+ " <endpoint id=\"nalle\" container-id=\"frosk\" />" +
" <endpoint container-id=\"quux\" />" +
" </endpoints>" +
" </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 95e13ee2748..31a30e5bd83 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
@@ -72,10 +72,10 @@ public class DeploymentSpecWithoutInstanceTest {
DeploymentSpec spec = DeploymentSpec.fromXml(r);
assertEquals(2, spec.steps().size());
- assertTrue(spec.requireInstance("default").steps().get(0).deploysTo(Environment.test));
- assertTrue(spec.requireInstance("default").steps().get(1).deploysTo(Environment.staging));
- assertTrue(spec.requireInstance("default").includes(Environment.test, Optional.empty()));
- assertFalse(spec.requireInstance("default").includes(Environment.test, Optional.of(RegionName.from("region1"))));
+ 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()));
assertFalse(spec.requireInstance("default").globalServiceId().isPresent());
@@ -93,21 +93,21 @@ public class DeploymentSpecWithoutInstanceTest {
);
DeploymentSpec spec = DeploymentSpec.fromXml(r);
- assertEquals(4, spec.requireInstance("default").steps().size());
+ assertEquals(3, spec.steps().size());
+ assertEquals(2, spec.requireInstance("default").steps().size());
- assertTrue(spec.requireInstance("default").steps().get(0).deploysTo(Environment.test));
+ assertTrue(spec.steps().get(0).deploysTo(Environment.test));
- assertTrue(spec.requireInstance("default").steps().get(1).deploysTo(Environment.staging));
+ assertTrue(spec.steps().get(1).deploysTo(Environment.staging));
- assertTrue(spec.requireInstance("default").steps().get(2).deploysTo(Environment.prod, Optional.of(RegionName.from("us-east1"))));
- assertFalse(((DeploymentSpec.DeclaredZone)spec.requireInstance("default").steps().get(2)).active());
+ assertTrue(spec.requireInstance("default").steps().get(0).deploysTo(Environment.prod, Optional.of(RegionName.from("us-east1"))));
+ assertFalse(((DeploymentSpec.DeclaredZone)spec.requireInstance("default").steps().get(0)).active());
- assertTrue(spec.requireInstance("default").steps().get(3).deploysTo(Environment.prod, Optional.of(RegionName.from("us-west1"))));
- assertTrue(((DeploymentSpec.DeclaredZone)spec.requireInstance("default").steps().get(3)).active());
+ assertTrue(spec.requireInstance("default").steps().get(1).deploysTo(Environment.prod, Optional.of(RegionName.from("us-west1"))));
+ assertTrue(((DeploymentSpec.DeclaredZone)spec.requireInstance("default").steps().get(1)).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()));
+ 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"))));
@@ -253,7 +253,8 @@ public class DeploymentSpecWithoutInstanceTest {
@Test
public void testEmpty() {
assertFalse(DeploymentSpec.empty.requireInstance("default").globalServiceId().isPresent());
- assertTrue(DeploymentSpec.empty.steps().isEmpty());
+ assertEquals(1, DeploymentSpec.empty.steps().size());
+ assertTrue(DeploymentSpec.empty.requireInstance("default").steps().isEmpty());
assertEquals("<deployment version='1.0'/>", DeploymentSpec.empty.xmlForm());
}
@@ -271,7 +272,7 @@ public class DeploymentSpecWithoutInstanceTest {
"</deployment>"
);
DeploymentSpec spec = DeploymentSpec.fromXml(r);
- DeploymentSpec.ParallelZones parallelZones = ((DeploymentSpec.ParallelZones) spec.requireInstance("default").steps().get(3));
+ 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());
@@ -470,6 +471,7 @@ public class DeploymentSpecWithoutInstanceTest {
public void customTesterFlavor() {
DeploymentSpec spec = DeploymentSpec.fromXml("<deployment>\n" +
" <test tester-flavor=\"d-1-4-20\" />\n" +
+ " <staging />\n" +
" <prod tester-flavor=\"d-2-8-50\">\n" +
" <region active=\"false\">us-north-7</region>\n" +
" </prod>\n" +
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationPackageValidator.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationPackageValidator.java
index 5ee269f8448..d7347b46f52 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationPackageValidator.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationPackageValidator.java
@@ -50,15 +50,17 @@ public class ApplicationPackageValidator {
/** Verify that each of the production zones listed in the deployment spec exist in this system */
private void validateSteps(DeploymentSpec deploymentSpec) {
- new DeploymentSteps(deploymentSpec, controller::system).jobs();
- deploymentSpec.instances().stream().flatMap(instance -> instance.zones().stream())
- .filter(zone -> zone.environment() == Environment.prod)
- .forEach(zone -> {
- if ( ! controller.zoneRegistry().hasZone(ZoneId.from(zone.environment(),
- zone.region().orElse(null)))) {
- throw new IllegalArgumentException("Zone " + zone + " in deployment spec was not found in this system!");
- }
- });
+ for (var spec : deploymentSpec.instances()) {
+ new DeploymentSteps(spec, controller::system).jobs();
+ spec.zones().stream()
+ .filter(zone -> zone.environment() == Environment.prod)
+ .forEach(zone -> {
+ if ( ! controller.zoneRegistry().hasZone(ZoneId.from(zone.environment(),
+ zone.region().orElseThrow()))) {
+ throw new IllegalArgumentException("Zone " + zone + " in deployment spec was not found in this system!");
+ }
+ });
+ }
}
/** Verify that no single endpoint contains regions in different clouds */
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
index 83d1b5ef803..9a6daf026c3 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
@@ -145,7 +145,6 @@ public class Endpoint {
}
private static String instancePart(ApplicationId application, ZoneId zone, String separator) {
- if (zone == null) return ""; // Always omit instance for global endpoints
if (application.instance().isDefault()) return ""; // Skip "default"
return application.instance().value() + separator;
}
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 33db6b95db1..1b722c80ec1 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
@@ -1,12 +1,14 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
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.application.Deployment;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
+import com.yahoo.vespa.hosted.controller.application.Deployment;
import com.yahoo.vespa.hosted.controller.application.JobStatus;
import java.util.Collection;
@@ -16,8 +18,8 @@ import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
-import static java.util.Collections.singletonList;
import static java.util.Comparator.comparingInt;
import static java.util.stream.Collectors.collectingAndThen;
@@ -28,19 +30,20 @@ import static java.util.stream.Collectors.collectingAndThen;
*/
public class DeploymentSteps {
- private final DeploymentSpec spec;
+ private final DeploymentInstanceSpec spec;
private final Supplier<SystemName> system;
- public DeploymentSteps(DeploymentSpec spec, Supplier<SystemName> system) {
+ public DeploymentSteps(DeploymentInstanceSpec spec, Supplier<SystemName> system) {
this.spec = Objects.requireNonNull(spec, "spec cannot be null");
this.system = Objects.requireNonNull(system, "system cannot be null");
}
- /** Returns jobs for this, in the order they are declared */
+ /** Returns jobs for this, in the order they should run */
public List<JobType> jobs() {
- return spec.steps().stream()
- .flatMap(step -> toJobs(step).stream())
- .collect(collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
+ return Stream.concat(production().isEmpty() ? Stream.of() : Stream.of(JobType.systemTest, JobType.stagingTest),
+ spec.steps().stream().flatMap(step -> toJobs(step).stream()))
+ .distinct()
+ .collect(Collectors.toUnmodifiableList());
}
/** Returns job status sorted according to deployment spec */
@@ -48,7 +51,7 @@ public class DeploymentSteps {
List<JobType> sortedJobs = jobs();
return jobStatus.stream()
.sorted(comparingInt(job -> sortedJobs.indexOf(job.type())))
- .collect(collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
+ .collect(Collectors.toUnmodifiableList());
}
/** Returns deployments sorted according to declared zones */
@@ -56,7 +59,7 @@ public class DeploymentSteps {
List<ZoneId> productionZones = spec.zones().stream()
.filter(z -> z.region().isPresent())
.map(z -> ZoneId.from(z.environment(), z.region().get()))
- .collect(Collectors.toList());
+ .collect(Collectors.toUnmodifiableList());
return deployments.stream()
.sorted(comparingInt(deployment -> productionZones.indexOf(deployment.zone())))
.collect(collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
@@ -67,34 +70,28 @@ public class DeploymentSteps {
return step.zones().stream()
.map(this::toJob)
.flatMap(Optional::stream)
- .collect(collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
+ .collect(Collectors.toUnmodifiableList());
}
- /** Returns test jobs in this */
+ /** Returns test jobs to run for this spec */
public List<JobType> testJobs() {
- return toJobs(test());
+ return jobs().stream().filter(JobType::isTest).collect(Collectors.toUnmodifiableList());
}
- /** Returns production jobs in this */
+ /** Returns declared production jobs in this */
public List<JobType> productionJobs() {
return toJobs(production());
}
- /** Returns test steps in this */
- public List<DeploymentSpec.Step> test() {
- if (spec.steps().isEmpty()) {
- return singletonList(new DeploymentSpec.DeclaredZone(Environment.test));
- }
+ /** Returns declared production steps in this */
+ public List<DeploymentSpec.Step> production() {
return spec.steps().stream()
- .filter(step -> step.deploysTo(Environment.test) || step.deploysTo(Environment.staging))
- .collect(collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
+ .filter(step -> ! isTest(step))
+ .collect(Collectors.toUnmodifiableList());
}
- /** Returns production steps in this */
- public List<DeploymentSpec.Step> production() {
- return spec.steps().stream()
- .filter(step -> step.deploysTo(Environment.prod) || step.zones().isEmpty())
- .collect(collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
+ private boolean isTest(DeploymentSpec.Step step) {
+ return step.deploysTo(Environment.test) || step.deploysTo(Environment.staging);
}
/** Resolve job from deployment zone */
@@ -106,7 +103,7 @@ public class DeploymentSteps {
private List<JobType> toJobs(List<DeploymentSpec.Step> steps) {
return steps.stream()
.flatMap(step -> toJobs(step).stream())
- .collect(collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
+ .collect(Collectors.toUnmodifiableList());
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java
index a1a66570299..ca2db96c14d 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
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.application.api.DeploymentSpec.Step;
import com.yahoo.config.provision.ApplicationId;
@@ -77,7 +78,6 @@ public class DeploymentTrigger {
*/
public static final Duration maxPause = Duration.ofDays(3);
-
private final static Logger log = Logger.getLogger(DeploymentTrigger.class.getName());
private final Controller controller;
@@ -92,7 +92,7 @@ public class DeploymentTrigger {
this.jobs = controller.jobController();
}
- public DeploymentSteps steps(DeploymentSpec spec) {
+ public DeploymentSteps steps(DeploymentInstanceSpec spec) {
return new DeploymentSteps(spec, controller::system);
}
@@ -333,7 +333,7 @@ public class DeploymentTrigger {
.<Instant>flatMap(job -> job.lastSuccess().map(JobRun::at)));
String reason = "New change available";
List<Job> testJobs = null; // null means "uninitialised", while empty means "don't run any jobs".
- DeploymentSteps steps = steps(application.deploymentSpec());
+ DeploymentSteps steps = steps(application.deploymentSpec().requireInstance(instance.name()));
if (change.hasTargets()) {
for (Step step : steps.production()) {
@@ -541,19 +541,23 @@ public class DeploymentTrigger {
}
private Change remainingChange(Application application) {
- DeploymentSteps steps = steps(application.deploymentSpec());
- List<JobType> jobs = steps.production().isEmpty()
- ? steps.testJobs()
- : steps.productionJobs();
-
Change change = application.change();
- for (Instance instance : application.instances().values()) {
- if (jobs.stream().allMatch(job -> isComplete(application.change().withoutApplication(), application.change(), instance, job)))
- change = change.withoutPlatform();
- if (jobs.stream().allMatch(job -> isComplete(application.change().withoutPlatform(), application.change(), instance, job)))
- change = change.withoutApplication();
- }
+ if (application.deploymentSpec().instances().stream()
+ .allMatch(spec -> {
+ DeploymentSteps steps = new DeploymentSteps(spec, controller::system);
+ return (steps.productionJobs().isEmpty() ? steps.testJobs() : steps.productionJobs())
+ .stream().allMatch(job -> isComplete(application.change().withoutApplication(), application.change(), application.require(spec.name()), job));
+ }))
+ change = change.withoutPlatform();
+
+ if (application.deploymentSpec().instances().stream()
+ .allMatch(spec -> {
+ DeploymentSteps steps = new DeploymentSteps(spec, controller::system);
+ return (steps.productionJobs().isEmpty() ? steps.testJobs() : steps.productionJobs())
+ .stream().allMatch(job -> isComplete(application.change().withoutPlatform(), application.change(), application.require(spec.name()), job));
+ }))
+ change = change.withoutApplication();
return change;
}
@@ -575,7 +579,7 @@ public class DeploymentTrigger {
private List<Job> testJobs(DeploymentSpec deploymentSpec, Change change, Instance instance, Versions versions,
String reason, Instant availableSince, Predicate<JobType> condition) {
List<Job> jobs = new ArrayList<>();
- for (JobType jobType : steps(deploymentSpec).testJobs()) {
+ for (JobType jobType : new DeploymentSteps(deploymentSpec.requireInstance(instance.name()), controller::system).testJobs()) { // TODO jonmv: Allow cross-instance validation
Optional<JobRun> completion = successOn(instance, jobType, versions)
.filter(run -> versions.sourcesMatchIfPresent(run) || jobType == systemTest);
if (completion.isEmpty() && condition.test(jobType))
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java
index cc483dff765..3deca255bad 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java
@@ -469,7 +469,9 @@ public class JobController {
/** Returns a URI which points at a badge showing current status for all jobs for the given application. */
public URI overviewBadge(ApplicationId id) {
- DeploymentSteps steps = new DeploymentSteps(controller.applications().requireApplication(TenantAndApplicationId.from(id)).deploymentSpec(), controller::system);
+ DeploymentSteps steps = new DeploymentSteps(controller.applications().requireApplication(TenantAndApplicationId.from(id))
+ .deploymentSpec().requireInstance(id.instance()),
+ controller::system);
return badges.overview(id,
steps.jobs().stream()
.map(type -> last(id, type))
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
index 08142a9a399..75f489d3345 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
@@ -6,7 +6,9 @@ import com.yahoo.config.application.api.DeploymentInstanceSpec;
import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.config.application.api.ValidationOverrides;
import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.InstanceName;
+import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.security.KeyUtils;
import com.yahoo.slime.ArrayTraverser;
@@ -43,6 +45,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -578,10 +581,7 @@ public class ApplicationSerializer {
var endpointId = EndpointId.of(inspector.field(assignedRotationEndpointField).asString());
var rotationId = new RotationId(inspector.field(assignedRotationRotationField).asString());
var regions = deploymentSpec.instance(instance)
- .map(spec -> spec.endpoints().stream()
- .filter(endpoint -> endpoint.endpointId().equals(endpointId.id()))
- .flatMap(endpoint -> endpoint.regions().stream())
- .collect(Collectors.toSet()))
+ .map(spec -> globalEndpointRegions(spec, endpointId))
.orElse(Set.of());
assignedRotations.putIfAbsent(endpointId, new AssignedRotation(clusterId, endpointId, rotationId, regions));
});
@@ -589,4 +589,16 @@ public class ApplicationSerializer {
return List.copyOf(assignedRotations.values());
}
+ private Set<RegionName> globalEndpointRegions(DeploymentInstanceSpec spec, EndpointId endpointId) {
+ if (spec.globalServiceId().isPresent())
+ return spec.zones().stream()
+ .flatMap(zone -> zone.region().stream())
+ .collect(Collectors.toSet());
+
+ return spec.endpoints().stream()
+ .filter(endpoint -> endpoint.endpointId().equals(endpointId.id()))
+ .flatMap(endpoint -> endpoint.regions().stream())
+ .collect(Collectors.toSet());
+ }
+
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index 4537f480706..5cb7d7a6065 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -71,6 +71,7 @@ import com.yahoo.vespa.hosted.controller.application.JobStatus;
import com.yahoo.vespa.hosted.controller.application.RoutingPolicy;
import com.yahoo.vespa.hosted.controller.application.SystemApplication;
import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
+import com.yahoo.vespa.hosted.controller.deployment.DeploymentSteps;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger.ChangesToCancel;
import com.yahoo.vespa.hosted.controller.deployment.TestConfigSerializer;
@@ -717,35 +718,38 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
private void toSlime(Cursor object, Instance instance, DeploymentSpec deploymentSpec, HttpRequest request) {
object.setString("instance", instance.name().value());
- // Jobs sorted according to deployment spec
- List<JobStatus> jobStatus = controller.applications().deploymentTrigger()
- .steps(deploymentSpec)
- .sortedJobs(instance.deploymentJobs().jobStatus().values());
-
-
- Cursor deploymentJobsArray = object.setArray("deploymentJobs");
- for (JobStatus job : jobStatus) {
- Cursor jobObject = deploymentJobsArray.addObject();
- jobObject.setString("type", job.type().jobName());
- jobObject.setBool("success", job.isSuccess());
- job.lastTriggered().ifPresent(jobRun -> toSlime(jobRun, jobObject.setObject("lastTriggered")));
- job.lastCompleted().ifPresent(jobRun -> toSlime(jobRun, jobObject.setObject("lastCompleted")));
- job.firstFailing().ifPresent(jobRun -> toSlime(jobRun, jobObject.setObject("firstFailing")));
- job.lastSuccess().ifPresent(jobRun -> toSlime(jobRun, jobObject.setObject("lastSuccess")));
- }
- // Change blockers
- Cursor changeBlockers = object.setArray("changeBlockers");
- deploymentSpec.instance(instance.name()).ifPresent(spec -> spec.changeBlocker().forEach(changeBlocker -> {
- Cursor changeBlockerObject = changeBlockers.addObject();
- changeBlockerObject.setBool("versions", changeBlocker.blocksVersions());
- changeBlockerObject.setBool("revisions", changeBlocker.blocksRevisions());
- changeBlockerObject.setString("timeZone", changeBlocker.window().zone().getId());
- Cursor days = changeBlockerObject.setArray("days");
- changeBlocker.window().days().stream().map(DayOfWeek::getValue).forEach(days::addLong);
- Cursor hours = changeBlockerObject.setArray("hours");
- changeBlocker.window().hours().forEach(hours::addLong);
- }));
+ if (deploymentSpec.instance(instance.name()).isPresent()) {
+ // Jobs sorted according to deployment spec
+ List<JobStatus> jobStatus = controller.applications().deploymentTrigger()
+ .steps(deploymentSpec.requireInstance(instance.name()))
+ .sortedJobs(instance.deploymentJobs().jobStatus().values());
+
+
+ Cursor deploymentJobsArray = object.setArray("deploymentJobs");
+ for (JobStatus job : jobStatus) {
+ Cursor jobObject = deploymentJobsArray.addObject();
+ jobObject.setString("type", job.type().jobName());
+ jobObject.setBool("success", job.isSuccess());
+ job.lastTriggered().ifPresent(jobRun -> toSlime(jobRun, jobObject.setObject("lastTriggered")));
+ job.lastCompleted().ifPresent(jobRun -> toSlime(jobRun, jobObject.setObject("lastCompleted")));
+ job.firstFailing().ifPresent(jobRun -> toSlime(jobRun, jobObject.setObject("firstFailing")));
+ job.lastSuccess().ifPresent(jobRun -> toSlime(jobRun, jobObject.setObject("lastSuccess")));
+ }
+
+ // Change blockers
+ Cursor changeBlockers = object.setArray("changeBlockers");
+ deploymentSpec.instance(instance.name()).ifPresent(spec -> spec.changeBlocker().forEach(changeBlocker -> {
+ Cursor changeBlockerObject = changeBlockers.addObject();
+ changeBlockerObject.setBool("versions", changeBlocker.blocksVersions());
+ changeBlockerObject.setBool("revisions", changeBlocker.blocksRevisions());
+ changeBlockerObject.setString("timeZone", changeBlocker.window().zone().getId());
+ Cursor days = changeBlockerObject.setArray("days");
+ changeBlocker.window().days().stream().map(DayOfWeek::getValue).forEach(days::addLong);
+ Cursor hours = changeBlockerObject.setArray("hours");
+ changeBlocker.window().hours().forEach(hours::addLong);
+ }));
+ }
// Rotation
Cursor globalRotationsArray = object.setArray("globalRotations");
@@ -772,9 +776,10 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
}
// Deployments sorted according to deployment spec
- List<Deployment> deployments = controller.applications().deploymentTrigger()
- .steps(deploymentSpec)
- .sortedDeployments(instance.deployments().values());
+ List<Deployment> deployments = deploymentSpec.instance(instance.name())
+ .map(spec -> new DeploymentSteps(spec, controller::system))
+ .map(steps -> steps.sortedDeployments(instance.deployments().values()))
+ .orElse(List.copyOf(instance.deployments().values()));
Cursor deploymentsArray = object.setArray("deployments");
for (Deployment deployment : deployments) {
@@ -822,36 +827,38 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
toSlime(object.setObject("outstandingChange"), application.outstandingChange());
}
- // Jobs sorted according to deployment spec
- List<JobStatus> jobStatus = controller.applications().deploymentTrigger()
- .steps(application.deploymentSpec())
- .sortedJobs(instance.deploymentJobs().jobStatus().values());
-
- object.setBool("deployedInternally", application.internal());
- Cursor deploymentsArray = object.setArray("deploymentJobs");
- for (JobStatus job : jobStatus) {
- Cursor jobObject = deploymentsArray.addObject();
- jobObject.setString("type", job.type().jobName());
- jobObject.setBool("success", job.isSuccess());
-
- job.lastTriggered().ifPresent(jobRun -> toSlime(jobRun, jobObject.setObject("lastTriggered")));
- job.lastCompleted().ifPresent(jobRun -> toSlime(jobRun, jobObject.setObject("lastCompleted")));
- job.firstFailing().ifPresent(jobRun -> toSlime(jobRun, jobObject.setObject("firstFailing")));
- job.lastSuccess().ifPresent(jobRun -> toSlime(jobRun, jobObject.setObject("lastSuccess")));
- }
+ if (application.deploymentSpec().instance(instance.name()).isPresent()) {
+ // Jobs sorted according to deployment spec
+ List<JobStatus> jobStatus = controller.applications().deploymentTrigger()
+ .steps(application.deploymentSpec().requireInstance(instance.name()))
+ .sortedJobs(instance.deploymentJobs().jobStatus().values());
+
+ object.setBool("deployedInternally", application.internal());
+ Cursor deploymentsArray = object.setArray("deploymentJobs");
+ for (JobStatus job : jobStatus) {
+ Cursor jobObject = deploymentsArray.addObject();
+ jobObject.setString("type", job.type().jobName());
+ jobObject.setBool("success", job.isSuccess());
+
+ job.lastTriggered().ifPresent(jobRun -> toSlime(jobRun, jobObject.setObject("lastTriggered")));
+ job.lastCompleted().ifPresent(jobRun -> toSlime(jobRun, jobObject.setObject("lastCompleted")));
+ job.firstFailing().ifPresent(jobRun -> toSlime(jobRun, jobObject.setObject("firstFailing")));
+ job.lastSuccess().ifPresent(jobRun -> toSlime(jobRun, jobObject.setObject("lastSuccess")));
+ }
- // Change blockers
- Cursor changeBlockers = object.setArray("changeBlockers");
- application.deploymentSpec().instance(instance.name()).ifPresent(spec -> spec.changeBlocker().forEach(changeBlocker -> {
- Cursor changeBlockerObject = changeBlockers.addObject();
- changeBlockerObject.setBool("versions", changeBlocker.blocksVersions());
- changeBlockerObject.setBool("revisions", changeBlocker.blocksRevisions());
- changeBlockerObject.setString("timeZone", changeBlocker.window().zone().getId());
- Cursor days = changeBlockerObject.setArray("days");
- changeBlocker.window().days().stream().map(DayOfWeek::getValue).forEach(days::addLong);
- Cursor hours = changeBlockerObject.setArray("hours");
- changeBlocker.window().hours().forEach(hours::addLong);
- }));
+ // Change blockers
+ Cursor changeBlockers = object.setArray("changeBlockers");
+ application.deploymentSpec().instance(instance.name()).ifPresent(spec -> spec.changeBlocker().forEach(changeBlocker -> {
+ Cursor changeBlockerObject = changeBlockers.addObject();
+ changeBlockerObject.setBool("versions", changeBlocker.blocksVersions());
+ changeBlockerObject.setBool("revisions", changeBlocker.blocksRevisions());
+ changeBlockerObject.setString("timeZone", changeBlocker.window().zone().getId());
+ Cursor days = changeBlockerObject.setArray("days");
+ changeBlocker.window().days().stream().map(DayOfWeek::getValue).forEach(days::addLong);
+ Cursor hours = changeBlockerObject.setArray("hours");
+ changeBlocker.window().hours().forEach(hours::addLong);
+ }));
+ }
// Compile version. The version that should be used when building an application
object.setString("compileVersion", compileVersion(application.id()).toFullString());
@@ -883,9 +890,11 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
}
// Deployments sorted according to deployment spec
- List<Deployment> deployments = controller.applications().deploymentTrigger()
- .steps(application.deploymentSpec())
- .sortedDeployments(instance.deployments().values());
+ List<Deployment> deployments =
+ application.deploymentSpec().instance(instance.name())
+ .map(spec -> new DeploymentSteps(spec, controller::system))
+ .map(steps -> steps.sortedDeployments(instance.deployments().values()))
+ .orElse(List.copyOf(instance.deployments().values()));
Cursor instancesArray = object.setArray("instances");
for (Deployment deployment : deployments) {
Cursor deploymentObject = instancesArray.addObject();
@@ -1925,8 +1934,6 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
ApplicationPackage applicationPackage = new ApplicationPackage(dataParts.get(EnvironmentResource.APPLICATION_ZIP));
if (DeploymentSpec.empty.equals(applicationPackage.deploymentSpec()))
throw new IllegalArgumentException("Missing required file 'deployment.xml'");
- if (applicationPackage.deploymentSpec().instances().size() != 1)
- throw new IllegalArgumentException("Only single-instance deployment specs are currently supported");
controller.applications().verifyApplicationIdentityConfiguration(TenantName.from(tenant),
applicationPackage,
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java
index 0f1cd9495bf..9fb20ef4f81 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java
@@ -76,7 +76,7 @@ class JobControllerApiHandlerHelper {
Application application = controller.applications().requireApplication(TenantAndApplicationId.from(id));
Instance instance = application.require(id.instance());
Change change = application.change();
- DeploymentSteps steps = new DeploymentSteps(application.deploymentSpec(), controller::system);
+ DeploymentSteps steps = new DeploymentSteps(application.deploymentSpec().requireInstance(id.instance()), controller::system);
// The logic for pending runs imitates DeploymentTrigger logic; not good, but the trigger wiring must be re-written to reuse :S
Map<JobType, Versions> pendingProduction =
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandler.java
index 64a32bce3c0..6ffdea93a1c 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandler.java
@@ -11,6 +11,7 @@ import com.yahoo.restapi.SlimeJsonResponse;
import com.yahoo.slime.Cursor;
import com.yahoo.slime.Slime;
import com.yahoo.vespa.hosted.controller.Controller;
+import com.yahoo.vespa.hosted.controller.api.integration.ServiceRegistry;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry;
import com.yahoo.vespa.hosted.controller.auditlog.AuditLoggingRequestHandler;
import com.yahoo.vespa.hosted.controller.proxy.ConfigServerRestExecutor;
@@ -39,10 +40,10 @@ public class ConfigServerApiHandler extends AuditLoggingRequestHandler {
private final ZoneRegistry zoneRegistry;
private final ConfigServerRestExecutor proxy;
- public ConfigServerApiHandler(Context parentCtx, ZoneRegistry zoneRegistry,
+ public ConfigServerApiHandler(Context parentCtx, ServiceRegistry serviceRegistry,
ConfigServerRestExecutor proxy, Controller controller) {
super(parentCtx, controller.auditLogger());
- this.zoneRegistry = zoneRegistry;
+ this.zoneRegistry = serviceRegistry.zoneRegistry();
this.proxy = proxy;
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
index 3e96d2f6972..ea97e3e6c71 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
@@ -41,6 +41,10 @@ public class EndpointTest {
"https://cd--a1--t1.global.vespa.oath.cloud:4443/",
Endpoint.of(app1).named(endpointId).on(Port.tls(4443)).in(SystemName.cd),
+ // Main endpoint in CD
+ "https://cd--i2--a2--t2.global.vespa.oath.cloud:4443/",
+ Endpoint.of(app2).named(endpointId).on(Port.tls(4443)).in(SystemName.cd),
+
// Main endpoint with direct routing and default TLS port
"https://a1.t1.global.vespa.oath.cloud/",
Endpoint.of(app1).named(endpointId).on(Port.tls()).directRouting().in(SystemName.main),
@@ -50,11 +54,11 @@ public class EndpointTest {
Endpoint.of(app1).named(EndpointId.of("r1")).on(Port.tls()).directRouting().in(SystemName.main),
// Main endpoint for custom instance in default rotation
- "https://a2.t2.global.vespa.oath.cloud/",
+ "https://i2.a2.t2.global.vespa.oath.cloud/",
Endpoint.of(app2).named(endpointId).on(Port.tls()).directRouting().in(SystemName.main),
// Main endpoint for custom instance with custom rotation name
- "https://r2.a2.t2.global.vespa.oath.cloud/",
+ "https://r2.i2.a2.t2.global.vespa.oath.cloud/",
Endpoint.of(app2).named(EndpointId.of("r2")).on(Port.tls()).directRouting().in(SystemName.main),
// Main endpoint in public system
@@ -82,6 +86,10 @@ public class EndpointTest {
Endpoint.of(app1).named(endpointId).on(Port.tls(4443)).in(SystemName.main),
// Main endpoint in CD
+ "https://cd--i2--a2--t2.global.vespa.oath.cloud:4443/",
+ Endpoint.of(app2).named(endpointId).on(Port.tls(4443)).in(SystemName.cd),
+
+ // Main endpoint in CD
"https://cd--a1--t1.global.vespa.oath.cloud:4443/",
Endpoint.of(app1).named(endpointId).on(Port.tls(4443)).in(SystemName.cd),
@@ -94,11 +102,11 @@ public class EndpointTest {
Endpoint.of(app1).named(EndpointId.of("r1")).on(Port.tls()).directRouting().in(SystemName.main),
// Main endpoint for custom instance in default rotation
- "https://a2.t2.global.vespa.oath.cloud/",
+ "https://i2.a2.t2.global.vespa.oath.cloud/",
Endpoint.of(app2).named(endpointId).on(Port.tls()).directRouting().in(SystemName.main),
// Main endpoint for custom instance with custom rotation name
- "https://r2.a2.t2.global.vespa.oath.cloud/",
+ "https://r2.i2.a2.t2.global.vespa.oath.cloud/",
Endpoint.of(app2).named(EndpointId.of("r2")).on(Port.tls()).directRouting().in(SystemName.main),
// Main endpoint in public system
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
index b8d6689a9d9..d0eb86c3ba1 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
@@ -172,10 +172,11 @@ public class DeploymentContext {
.allMatch(deployments -> deployments.stream()
.allMatch(deployment -> deployment.version().equals(version))));
- for (JobType type : new DeploymentSteps(application().deploymentSpec(), tester.controller()::system).productionJobs())
- assertTrue(tester.configServer().nodeRepository()
- .list(type.zone(tester.controller().system()), applicationId.defaultInstance()).stream() // TODO jonmv: support more
- .allMatch(node -> node.currentVersion().equals(version)));
+ for (var spec : application().deploymentSpec().instances())
+ for (JobType type : new DeploymentSteps(spec, tester.controller()::system).productionJobs())
+ assertTrue(tester.configServer().nodeRepository()
+ .list(type.zone(tester.controller().system()), applicationId.defaultInstance()).stream() // TODO jonmv: support more
+ .allMatch(node -> node.currentVersion().equals(version)));
assertFalse(application().change().hasTargets());
return this;
@@ -267,7 +268,7 @@ public class DeploymentContext {
triggerJobs();
}
else
- throw new AssertionError("Job '" + run.id().type() + "' was run twice for '" + instanceId + "'");
+ throw new AssertionError("Job '" + run.id() + "' was run twice");
assertFalse("Change should have no targets, but was " + application().change(), application().change().hasTargets());
if (!deferDnsUpdates) {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java
index de5c04af80b..8fb766164c2 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java
@@ -862,15 +862,16 @@ public class DeploymentTriggerTest {
}
@Test
- @Ignore
public void testMultipleInstances() {
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
.instances("instance1,instance2")
.environment(Environment.prod)
.region("us-east-3")
.build();
- var app = tester.newDeploymentContext().submit(applicationPackage); // TODO jonmv: support instances in deployment context>
- app.deploy();
+ var app = tester.newDeploymentContext("tenant1", "application1", "instance1").submit(applicationPackage); // TODO jonmv: support instances in deployment context>
+ var otherInstance = tester.newDeploymentContext("tenant1", "application1", "instance2");
+ app.runJob(systemTest).runJob(stagingTest).runJob(productionUsEast3);
+ otherInstance.runJob(systemTest).runJob(stagingTest).runJob(productionUsEast3);
assertEquals(2, app.application().instances().size());
assertEquals(2, app.application().productionDeployments().values().stream()
.mapToInt(Collection::size)
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
index 976f2dead8f..83d9d4d1ad0 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
@@ -2,36 +2,21 @@
package com.yahoo.vespa.hosted.controller.integration;
import com.google.inject.Inject;
+import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.component.AbstractComponent;
+import com.yahoo.config.provision.SystemName;
import com.yahoo.test.ManualClock;
-import com.yahoo.vespa.hosted.controller.api.integration.BuildService;
-import com.yahoo.vespa.hosted.controller.api.integration.RunDataStore;
import com.yahoo.vespa.hosted.controller.api.integration.ServiceRegistry;
-import com.yahoo.vespa.hosted.controller.api.integration.aws.AwsEventFetcher;
import com.yahoo.vespa.hosted.controller.api.integration.aws.MockAwsEventFetcher;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.ApplicationCertificateMock;
-import com.yahoo.vespa.hosted.controller.api.integration.certificates.ApplicationCertificateProvider;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServer;
-import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationStore;
-import com.yahoo.vespa.hosted.controller.api.integration.deployment.ArtifactRepository;
import com.yahoo.vespa.hosted.controller.api.integration.dns.MemoryNameService;
-import com.yahoo.vespa.hosted.controller.api.integration.dns.NameService;
-import com.yahoo.vespa.hosted.controller.api.integration.entity.EntityService;
import com.yahoo.vespa.hosted.controller.api.integration.entity.MemoryEntityService;
-import com.yahoo.vespa.hosted.controller.api.integration.organization.Billing;
-import com.yahoo.vespa.hosted.controller.api.integration.organization.ContactRetriever;
-import com.yahoo.vespa.hosted.controller.api.integration.organization.DeploymentIssues;
-import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueHandler;
-import com.yahoo.vespa.hosted.controller.api.integration.organization.Mailer;
import com.yahoo.vespa.hosted.controller.api.integration.organization.MockBilling;
import com.yahoo.vespa.hosted.controller.api.integration.organization.MockContactRetriever;
import com.yahoo.vespa.hosted.controller.api.integration.organization.MockIssueHandler;
-import com.yahoo.vespa.hosted.controller.api.integration.organization.OwnershipIssues;
-import com.yahoo.vespa.hosted.controller.api.integration.resource.CostReportConsumer;
import com.yahoo.vespa.hosted.controller.api.integration.resource.CostReportConsumerMock;
-import com.yahoo.vespa.hosted.controller.api.integration.resource.MeteringClient;
import com.yahoo.vespa.hosted.controller.api.integration.resource.MockTenantCost;
-import com.yahoo.vespa.hosted.controller.api.integration.resource.TenantCost;
import com.yahoo.vespa.hosted.controller.api.integration.routing.GlobalRoutingService;
import com.yahoo.vespa.hosted.controller.api.integration.routing.MemoryGlobalRoutingService;
import com.yahoo.vespa.hosted.controller.api.integration.routing.RoutingGenerator;
@@ -43,9 +28,6 @@ import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockMailer;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockMeteringClient;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockRunDataStore;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockTesterCloud;
-import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry;
-
-import java.time.Clock;
/**
* A mock implementation of a {@link ServiceRegistry} for testing purposes.
@@ -78,14 +60,18 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
private final MockBuildService mockBuildService = new MockBuildService();
private final MockTenantCost mockTenantCost = new MockTenantCost();
+ public ServiceRegistryMock(SystemName system) {
+ this.zoneRegistryMock = new ZoneRegistryMock(system);
+ this.configServerMock = new ConfigServerMock(zoneRegistryMock);
+ }
+
@Inject
- public ServiceRegistryMock(ZoneRegistryMock zoneRegistry) {
- this.zoneRegistryMock = zoneRegistry;
- this.configServerMock = new ConfigServerMock(zoneRegistry);
+ public ServiceRegistryMock(ConfigserverConfig config) {
+ this(SystemName.from(config.system()));
}
public ServiceRegistryMock() {
- this(new ZoneRegistryMock());
+ this(SystemName.main);
}
@Override
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java
index f5158a1ffa2..07db06164c6 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ZoneRegistryMock.java
@@ -40,15 +40,6 @@ public class ZoneRegistryMock extends AbstractComponent implements ZoneRegistry
private UpgradePolicy upgradePolicy = null;
private Map<CloudName, UpgradePolicy> osUpgradePolicies = new HashMap<>();
- @Inject
- public ZoneRegistryMock(ConfigserverConfig config) {
- this(SystemName.from(config.system()));
- }
-
- public ZoneRegistryMock() {
- this(SystemName.main);
- }
-
/**
* This sets the default list of zones contained in this. If your test need a particular set of zones, use
* {@link #setZones(List)} instead of changing the default set.}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/metric/ConfigServerMetricsTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/metric/ConfigServerMetricsTest.java
index 23a6c6286c0..23e86320a78 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/metric/ConfigServerMetricsTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/metric/ConfigServerMetricsTest.java
@@ -1,6 +1,7 @@
package com.yahoo.vespa.hosted.controller.metric;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.ClusterMetrics;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
@@ -31,7 +32,7 @@ public class ConfigServerMetricsTest {
@Before
public void before() {
- configServer = new ConfigServerMock(new ZoneRegistryMock());
+ configServer = new ConfigServerMock(new ZoneRegistryMock(SystemName.main));
service = new ConfigServerMetrics(configServer);
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
index 186e982fd75..9ac73604da5 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
@@ -6,6 +6,7 @@ import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.config.application.api.ValidationOverrides;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.security.KeyUtils;
import com.yahoo.vespa.config.SlimeUtils;
@@ -74,8 +75,13 @@ public class ApplicationSerializerTest {
@Test
public void testSerialization() {
- DeploymentSpec deploymentSpec = DeploymentSpec.fromXml("<deployment version='1.0'>" +
- " <staging/>" +
+ DeploymentSpec deploymentSpec = DeploymentSpec.fromXml("<deployment version='1.0'>\n" +
+ " <staging/>\n" +
+ " <instance id=\"i1\">\n" +
+ " <prod global-service-id=\"default\">\n" +
+ " <region active=\"true\">us-west-1</region>\n" +
+ " </prod>\n" +
+ " </instance>\n" +
"</deployment>");
ValidationOverrides validationOverrides = ValidationOverrides.fromXml("<validation-overrides version='1.0'>" +
" <allow until='2017-06-15'>deployment-removal</allow>" +
@@ -100,9 +106,6 @@ public class ApplicationSerializerTest {
List<JobStatus> statusList = new ArrayList<>();
- JobStatus.JobRun componentJob = JobStatus.JobRun.triggering(Version.emptyVersion, applicationVersion1, empty(),
- empty(), "New commit", Instant.ofEpochMilli(400))
- .completion(100, Instant.ofEpochMilli(500));
statusList.add(JobStatus.initial(JobType.systemTest)
.withTriggering(Version.fromString("5.6.7"), ApplicationVersion.unknown, empty(), "Test", Instant.ofEpochMilli(7))
.withCompletion(30, empty(), Instant.ofEpochMilli(8))
@@ -127,7 +130,7 @@ public class ApplicationSerializerTest {
List<Instance> instances = List.of(new Instance(id1,
deployments,
deploymentJobs,
- List.of(AssignedRotation.fromStrings("foo", "default", "my-rotation", Set.of())),
+ List.of(AssignedRotation.fromStrings("foo", "default", "my-rotation", Set.of("us-west-1"))),
rotationStatus),
new Instance(id3,
List.of(),
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java
index 1a53920e8de..dc6abcb2616 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java
@@ -70,7 +70,6 @@ public class ControllerContainerTest {
" <component id='com.yahoo.vespa.curator.mock.MockCurator'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactoryMock'/>\n" +
- " <component id='com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.integration.ServiceRegistryMock'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.Controller'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.integration.ConfigServerProxyMock'/>\n" +
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
index a1c9aa872fd..5deedaa2fb1 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
@@ -661,17 +661,19 @@ public class ApplicationApiTest extends ControllerContainerTest {
.data(streamer),
"{\"message\":\"Application package version: 1.0.3-commit1, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}");
- // Sixth attempt has a multi-instance deployment spec, and fails.
+ // Sixth attempt has a multi-instance deployment spec, and is accepted.
ApplicationPackage multiInstanceSpec = new ApplicationPackageBuilder()
.instances("instance1,instance2")
.environment(Environment.prod)
.region("us-central-1")
.parallel("us-west-1", "us-east-3")
+ .endpoint("default", "foo", "us-central-1", "us-west-1", "us-east-3")
.build();
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/submit", POST)
.screwdriverIdentity(SCREWDRIVER_ID)
.data(createApplicationSubmissionData(multiInstanceSpec, 123)),
- "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Only single-instance deployment specs are currently supported\"}", 400);
+ "{\"message\":\"Application package version: 1.0.4-commit1, source revision of repository 'repository1', branch 'master' with commit 'commit1', by a@b, built against 6.1 at 1970-01-01T00:00:01Z\"}");
+
// GET deployment job overview, after triggering system and staging test jobs.
assertEquals(2, tester.controller().applications().deploymentTrigger().triggerReadyJobs());
@@ -730,6 +732,10 @@ public class ApplicationApiTest extends ControllerContainerTest {
.userIdentity(USER_ID)
.oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"message\":\"Deleted instance tenant1.application1.instance1\"}");
+ tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance2", DELETE)
+ .userIdentity(USER_ID)
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
+ "{\"message\":\"Deleted instance tenant1.application1.instance2\"}");
// DELETE a tenant
tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE).userIdentity(USER_ID)
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application2-with-patches.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application2-with-patches.json
index 267c8b01330..195219c691e 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application2-with-patches.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application2-with-patches.json
@@ -31,8 +31,6 @@
"instances": [
{
"instance": "default",
- "deploymentJobs": [],
- "changeBlockers": [],
"globalRotations": [],
"deployments": []
},
@@ -80,7 +78,7 @@
],
"changeBlockers": [],
"globalRotations": [
- "https://application2--tenant2.global.vespa.oath.cloud:4443/"
+ "https://instance1--application2--tenant2.global.vespa.oath.cloud:4443/"
],
"rotationId": "rotation-id-2",
"deployments": []
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application2.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application2.json
index 8a1ed245fae..c29944924dc 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application2.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application2.json
@@ -30,8 +30,6 @@
"instances": [
{
"instance": "default",
- "deploymentJobs": [],
- "changeBlockers": [],
"globalRotations": [],
"deployments": []
},
@@ -79,7 +77,7 @@
],
"changeBlockers": [],
"globalRotations": [
- "https://application2--tenant2.global.vespa.oath.cloud:4443/"
+ "https://instance1--application2--tenant2.global.vespa.oath.cloud:4443/"
],
"rotationId": "rotation-id-2",
"deployments": []
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance-with-routing-policy.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance-with-routing-policy.json
index d0abe24fa58..bb81125e54d 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance-with-routing-policy.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance-with-routing-policy.json
@@ -162,7 +162,7 @@
"changeBlockers": [],
"compileVersion": "(ignore)",
"globalRotations": [
- "https://c0.application1.tenant1.global.vespa.oath.cloud/"
+ "https://c0.instance1.application1.tenant1.global.vespa.oath.cloud/"
],
"instances": [
{
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance-without-change-multiple-deployments.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance-without-change-multiple-deployments.json
index c61932f15fe..75493a30155 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance-without-change-multiple-deployments.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance-without-change-multiple-deployments.json
@@ -211,7 +211,7 @@
"changeBlockers": [],
"compileVersion": "(ignore)",
"globalRotations": [
- "https://application1--tenant1.global.vespa.oath.cloud:4443/"
+ "https://instance1--application1--tenant1.global.vespa.oath.cloud:4443/"
],
"rotationId": "rotation-id-1",
"instances": [
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance.json
index b2ab9aa636d..061931336ed 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance.json
@@ -216,7 +216,7 @@
],
"compileVersion": "6.0.0",
"globalRotations": [
- "https://application1--tenant1.global.vespa.oath.cloud:4443/"
+ "https://instance1--application1--tenant1.global.vespa.oath.cloud:4443/"
],
"rotationId": "rotation-id-1",
"instances": [
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json
index da73c36b208..2dacacedf51 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json
@@ -216,7 +216,7 @@
],
"compileVersion": "(ignore)",
"globalRotations": [
- "https://application1--tenant1.global.vespa.oath.cloud:4443/"
+ "https://instance1--application1--tenant1.global.vespa.oath.cloud:4443/"
],
"rotationId": "rotation-id-1",
"instances": [
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs.json
index 8fe38db994d..b73c36c804b 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/jobs.json
@@ -3,12 +3,12 @@
"platform": {
"platform": "6.1",
"at": "(ignore)",
- "pending": "Waiting for application change to 1.0.3-commit1 to complete"
+ "pending": "Waiting for application change to 1.0.4-commit1 to complete"
},
"application": {
"application": {
- "hash": "1.0.3-commit1",
- "build": 3,
+ "hash": "1.0.4-commit1",
+ "build": 4,
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -21,8 +21,8 @@
},
"deploying": {
"application": {
- "hash": "1.0.3-commit1",
- "build": 3,
+ "hash": "1.0.4-commit1",
+ "build": 4,
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -43,8 +43,8 @@
"start": "(ignore)",
"wantedPlatform": "6.1",
"wantedApplication": {
- "hash": "1.0.3-commit1",
- "build": 3,
+ "hash": "1.0.4-commit1",
+ "build": 4,
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -111,8 +111,8 @@
"start": "(ignore)",
"wantedPlatform": "6.1",
"wantedApplication": {
- "hash": "1.0.3-commit1",
- "build": 3,
+ "hash": "1.0.4-commit1",
+ "build": 4,
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
@@ -181,8 +181,8 @@
"status": "pending",
"wantedPlatform": "6.1",
"wantedApplication": {
- "hash": "1.0.3-commit1",
- "build": 3,
+ "hash": "1.0.4-commit1",
+ "build": 4,
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-job.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-job.json
index df55185fde5..a572fa04781 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-job.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-job.json
@@ -39,8 +39,8 @@
"start": "(ignore)",
"wantedPlatform": "6.1",
"wantedApplication": {
- "hash": "1.0.3-commit1",
- "build": 3,
+ "hash": "1.0.4-commit1",
+ "build": 4,
"source": {
"gitRepository": "repository1",
"gitBranch": "master",
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandlerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandlerTest.java
index e723b5a2f30..8aea26f21e3 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandlerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/configserver/ConfigServerApiHandlerTest.java
@@ -38,12 +38,11 @@ public class ConfigServerApiHandlerTest extends ControllerContainerTest {
@Before
public void before() {
- ZoneRegistryMock zoneRegistry = (ZoneRegistryMock) container.components()
- .getComponent(ZoneRegistryMock.class.getName());
- zoneRegistry.setDefaultRegionForEnvironment(Environment.dev, RegionName.from("us-north-2"))
- .setZones(zones);
- this.tester = new ContainerTester(container, responseFiles);
- this.proxy = (ConfigServerProxyMock) container.components().getComponent(ConfigServerProxyMock.class.getName());
+ tester = new ContainerTester(container, responseFiles);
+ tester.serviceRegistry().zoneRegistry()
+ .setDefaultRegionForEnvironment(Environment.dev, RegionName.from("us-north-2"))
+ .setZones(zones);
+ proxy = (ConfigServerProxyMock) container.components().getComponent(ConfigServerProxyMock.class.getName());
}
@Test
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiTest.java
index 73c64b0ee5d..eaeba420bc9 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v1/ZoneApiTest.java
@@ -34,11 +34,10 @@ public class ZoneApiTest extends ControllerContainerCloudTest {
@Before
public void before() {
- ZoneRegistryMock zoneRegistry = (ZoneRegistryMock) container.components()
- .getComponent(ZoneRegistryMock.class.getName());
- zoneRegistry.setDefaultRegionForEnvironment(Environment.dev, RegionName.from("us-north-2"))
- .setZones(zones);
- this.tester = new ContainerTester(container, responseFiles);
+ tester = new ContainerTester(container, responseFiles);
+ tester.serviceRegistry().zoneRegistry()
+ .setDefaultRegionForEnvironment(Environment.dev, RegionName.from("us-north-2"))
+ .setZones(zones);
}
@Test
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiTest.java
index 50b1db50c19..f00363989e6 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/zone/v2/ZoneApiTest.java
@@ -39,12 +39,11 @@ public class ZoneApiTest extends ControllerContainerTest {
@Before
public void before() {
- ZoneRegistryMock zoneRegistry = (ZoneRegistryMock) container.components()
- .getComponent(ZoneRegistryMock.class.getName());
- zoneRegistry.setDefaultRegionForEnvironment(Environment.dev, RegionName.from("us-north-2"))
- .setZones(zones);
- this.tester = new ContainerTester(container, responseFiles);
- this.proxy = (ConfigServerProxyMock) container.components().getComponent(ConfigServerProxyMock.class.getName());
+ tester = new ContainerTester(container, responseFiles);
+ tester.serviceRegistry().zoneRegistry()
+ .setDefaultRegionForEnvironment(Environment.dev, RegionName.from("us-north-2"))
+ .setZones(zones);
+ proxy = (ConfigServerProxyMock) container.components().getComponent(ConfigServerProxyMock.class.getName());
}
@Test
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java
index c76046b3f67..674f084a8b7 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java
@@ -146,6 +146,44 @@ public class RotationRepositoryTest {
application2.instance().endpointsIn(SystemName.cd).main().get().url().toString());
}
+ @Test
+ public void multiple_instances_with_similar_global_service_id() {
+ ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
+ .instances("instance1,instance2")
+ .region("us-central-1")
+ .parallel("us-west-1", "us-east-3")
+ .globalServiceId("global")
+ .build();
+ var instance1 = tester.newDeploymentContext("tenant1", "application1", "instance1").submit(applicationPackage);
+ var instance2 = tester.newDeploymentContext("tenant1", "application1", "instance2");
+ assertEquals(List.of(new RotationId("foo-1")), rotationIds(instance1.instance().rotations()));
+ assertEquals(List.of(new RotationId("foo-2")), rotationIds(instance2.instance().rotations()));
+ assertEquals(URI.create("https://instance1--application1--tenant1.global.vespa.oath.cloud:4443/"),
+ instance1.instance().endpointsIn(SystemName.main).main().get().url());
+ assertEquals(URI.create("https://instance2--application1--tenant1.global.vespa.oath.cloud:4443/"),
+ instance2.instance().endpointsIn(SystemName.main).main().get().url());
+ }
+
+ @Test
+ public void multiple_instances_with_similar_endpoints() {
+ ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
+ .instances("instance1,instance2")
+ .region("us-central-1")
+ .parallel("us-west-1", "us-east-3")
+ .endpoint("default", "foo", "us-central-1", "us-west-1")
+ .build();
+ var instance1 = tester.newDeploymentContext("tenant1", "application1", "instance1").submit(applicationPackage);
+ var instance2 = tester.newDeploymentContext("tenant1", "application1", "instance2");
+
+ assertEquals(List.of(new RotationId("foo-1")), rotationIds(instance1.instance().rotations()));
+ assertEquals(List.of(new RotationId("foo-2")), rotationIds(instance2.instance().rotations()));
+
+ assertEquals(URI.create("https://instance1--application1--tenant1.global.vespa.oath.cloud:4443/"),
+ instance1.instance().endpointsIn(SystemName.main).main().get().url());
+ assertEquals(URI.create("https://instance2--application1--tenant1.global.vespa.oath.cloud:4443/"),
+ instance2.instance().endpointsIn(SystemName.main).main().get().url());
+ }
+
private void assertSingleRotation(Rotation expected, List<AssignedRotation> assignedRotations, RotationRepository repository) {
assertEquals(1, assignedRotations.size());
var rotationId = assignedRotations.get(0).rotationId();