summaryrefslogtreecommitdiffstats
path: root/config-model-api
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2022-05-11 14:35:53 +0200
committerMartin Polden <mpolden@mpolden.no>2022-05-11 15:18:16 +0200
commit47d57ef30bceff2832d14edfd844623a8a6ce1a2 (patch)
treeadf747b8600e20fd4f848517cbc65721e918f8e7 /config-model-api
parent45dedaa74cde1a9b29da723fe8fe7d2182a79fd1 (diff)
Support cloud account in deployment spec
Diffstat (limited to 'config-model-api')
-rw-r--r--config-model-api/abi-spec.json6
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentInstanceSpec.java36
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java22
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java68
-rw-r--r--config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java22
-rw-r--r--config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecWithoutInstanceTest.java28
6 files changed, 130 insertions, 52 deletions
diff --git a/config-model-api/abi-spec.json b/config-model-api/abi-spec.json
index 6e02b88e22e..847defb92db 100644
--- a/config-model-api/abi-spec.json
+++ b/config-model-api/abi-spec.json
@@ -195,7 +195,7 @@
"public"
],
"methods": [
- "public void <init>(com.yahoo.config.provision.InstanceName, java.util.List, com.yahoo.config.application.api.DeploymentSpec$UpgradePolicy, com.yahoo.config.application.api.DeploymentSpec$RevisionTarget, com.yahoo.config.application.api.DeploymentSpec$RevisionChange, com.yahoo.config.application.api.DeploymentSpec$UpgradeRollout, int, int, int, java.util.List, java.util.Optional, java.util.Optional, com.yahoo.config.application.api.Notifications, java.util.List, java.time.Instant)",
+ "public void <init>(com.yahoo.config.provision.InstanceName, java.util.List, com.yahoo.config.application.api.DeploymentSpec$UpgradePolicy, com.yahoo.config.application.api.DeploymentSpec$RevisionTarget, com.yahoo.config.application.api.DeploymentSpec$RevisionChange, com.yahoo.config.application.api.DeploymentSpec$UpgradeRollout, int, int, int, java.util.List, java.util.Optional, java.util.Optional, java.util.Optional, com.yahoo.config.application.api.Notifications, java.util.List, java.time.Instant)",
"public com.yahoo.config.provision.InstanceName name()",
"public com.yahoo.config.application.api.DeploymentSpec$UpgradePolicy upgradePolicy()",
"public com.yahoo.config.application.api.DeploymentSpec$RevisionTarget revisionTarget()",
@@ -209,6 +209,7 @@
"public boolean canUpgradeAt(java.time.Instant)",
"public boolean canChangeRevisionAt(java.time.Instant)",
"public java.util.Optional athenzService(com.yahoo.config.provision.Environment, com.yahoo.config.provision.RegionName)",
+ "public java.util.Optional cloudAccount(com.yahoo.config.provision.Environment, com.yahoo.config.provision.RegionName)",
"public com.yahoo.config.application.api.Notifications notifications()",
"public java.util.List endpoints()",
"public boolean deploysTo(com.yahoo.config.provision.Environment, com.yahoo.config.provision.RegionName)",
@@ -258,12 +259,13 @@
],
"methods": [
"public void <init>(com.yahoo.config.provision.Environment)",
- "public void <init>(com.yahoo.config.provision.Environment, java.util.Optional, boolean, java.util.Optional, java.util.Optional)",
+ "public void <init>(com.yahoo.config.provision.Environment, java.util.Optional, boolean, java.util.Optional, java.util.Optional, java.util.Optional)",
"public com.yahoo.config.provision.Environment environment()",
"public java.util.Optional region()",
"public boolean active()",
"public java.util.Optional testerFlavor()",
"public java.util.Optional athenzService()",
+ "public java.util.Optional cloudAccount()",
"public java.util.List zones()",
"public boolean concerns(com.yahoo.config.provision.Environment, java.util.Optional)",
"public boolean isTest()",
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentInstanceSpec.java b/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentInstanceSpec.java
index 9d90167a0ef..5f6d47fb586 100644
--- a/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentInstanceSpec.java
+++ b/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentInstanceSpec.java
@@ -1,9 +1,8 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.application.api;
-import ai.vespa.validation.Validation;
-import com.yahoo.config.application.api.DeploymentSpec.RevisionTarget;
import com.yahoo.config.provision.AthenzService;
+import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.RegionName;
@@ -14,11 +13,9 @@ import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
-import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -29,7 +26,6 @@ import static com.yahoo.config.application.api.DeploymentSpec.RevisionChange.whe
import static com.yahoo.config.application.api.DeploymentSpec.RevisionTarget.next;
import static com.yahoo.config.provision.Environment.prod;
import static java.util.stream.Collectors.toList;
-import static java.util.stream.Collectors.toSet;
/**
* The deployment spec for an application instance
@@ -54,6 +50,7 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Steps {
private final List<DeploymentSpec.ChangeBlocker> changeBlockers;
private final Optional<String> globalServiceId;
private final Optional<AthenzService> athenzService;
+ private final Optional<CloudAccount> cloudAccount;
private final Notifications notifications;
private final List<Endpoint> endpoints;
@@ -67,25 +64,29 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Steps {
List<DeploymentSpec.ChangeBlocker> changeBlockers,
Optional<String> globalServiceId,
Optional<AthenzService> athenzService,
+ Optional<CloudAccount> cloudAccount,
Notifications notifications,
List<Endpoint> endpoints,
Instant now) {
super(steps);
- this.name = name;
- this.upgradePolicy = upgradePolicy;
+ this.name = Objects.requireNonNull(name);
+ this.upgradePolicy = Objects.requireNonNull(upgradePolicy);
+ Objects.requireNonNull(revisionTarget);
+ Objects.requireNonNull(revisionChange);
this.revisionTarget = require(maxRisk == 0 || revisionTarget == next, revisionTarget,
"revision-target must be 'next' when max-risk is specified");
this.revisionChange = require(maxRisk == 0 || revisionChange == whenClear, revisionChange,
"revision-change must be 'when-clear' when max-risk is specified");
- this.upgradeRollout = upgradeRollout;
+ this.upgradeRollout = Objects.requireNonNull(upgradeRollout);
this.minRisk = requireAtLeast(minRisk, "minimum risk score", 0);
this.maxRisk = require(maxRisk >= minRisk, maxRisk, "maximum risk cannot be less than minimum risk score");
this.maxIdleHours = requireInRange(maxIdleHours, "maximum idle hours", 0, 168);
- this.changeBlockers = changeBlockers;
- this.globalServiceId = globalServiceId;
- this.athenzService = athenzService;
- this.notifications = notifications;
- this.endpoints = List.copyOf(endpoints);
+ this.changeBlockers = Objects.requireNonNull(changeBlockers);
+ this.globalServiceId = Objects.requireNonNull(globalServiceId);
+ this.athenzService = Objects.requireNonNull(athenzService);
+ this.cloudAccount = Objects.requireNonNull(cloudAccount);
+ this.notifications = Objects.requireNonNull(notifications);
+ this.endpoints = List.copyOf(Objects.requireNonNull(endpoints));
validateZones(new HashSet<>(), new HashSet<>(), this);
validateEndpoints(steps(), globalServiceId, this.endpoints);
validateChangeBlockers(changeBlockers, now);
@@ -224,6 +225,15 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Steps {
.or(() -> this.athenzService);
}
+ /** Returns the cloud account to use for given environment and region, if any */
+ public Optional<CloudAccount> cloudAccount(Environment environment, RegionName region) {
+ return zones().stream()
+ .filter(zone -> zone.concerns(environment, Optional.of(region)))
+ .findFirst()
+ .flatMap(DeploymentSpec.DeclaredZone::cloudAccount)
+ .or(() -> cloudAccount);
+ }
+
/** Returns the notification configuration of these instances */
public Notifications notifications() { return notifications; }
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 e5ea65b6d4e..22ffdeb7262 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
@@ -5,6 +5,7 @@ import com.yahoo.collections.Comparables;
import com.yahoo.config.application.api.xml.DeploymentSpecXmlReader;
import com.yahoo.config.provision.AthenzDomain;
import com.yahoo.config.provision.AthenzService;
+import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.RegionName;
@@ -362,22 +363,27 @@ public class DeploymentSpec {
private final boolean active;
private final Optional<AthenzService> athenzService;
private final Optional<String> testerFlavor;
+ private final Optional<CloudAccount> cloudAccount;
public DeclaredZone(Environment environment) {
- this(environment, Optional.empty(), false, Optional.empty(), Optional.empty());
+ this(environment, Optional.empty(), false, Optional.empty(), Optional.empty(), Optional.empty());
}
public DeclaredZone(Environment environment, Optional<RegionName> region, boolean active,
- Optional<AthenzService> athenzService, Optional<String> testerFlavor) {
+ Optional<AthenzService> athenzService, Optional<String> testerFlavor,
+ Optional<CloudAccount> cloudAccount) {
if (environment != Environment.prod && region.isPresent())
illegal("Non-prod environments cannot specify a region");
if (environment == Environment.prod && region.isEmpty())
illegal("Prod environments must be specified with a region");
- this.environment = environment;
- this.region = region;
+ if (environment != Environment.prod && cloudAccount.isPresent())
+ illegal("Non-prod environments cannot specify cloud account");
+ this.environment = Objects.requireNonNull(environment);
+ this.region = Objects.requireNonNull(region);
this.active = active;
- this.athenzService = athenzService;
- this.testerFlavor = testerFlavor;
+ this.athenzService = Objects.requireNonNull(athenzService);
+ this.testerFlavor = Objects.requireNonNull(testerFlavor);
+ this.cloudAccount = Objects.requireNonNull(cloudAccount);
}
public Environment environment() { return environment; }
@@ -392,6 +398,10 @@ public class DeploymentSpec {
public Optional<AthenzService> athenzService() { return athenzService; }
+ public Optional<CloudAccount> cloudAccount() {
+ return cloudAccount;
+ }
+
@Override
public List<DeclaredZone> zones() { return Collections.singletonList(this); }
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java b/config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java
index 96cc33d44b4..23c1adafd56 100644
--- a/config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java
+++ b/config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java
@@ -21,6 +21,7 @@ import com.yahoo.config.application.api.Notifications.When;
import com.yahoo.config.application.api.TimeWindow;
import com.yahoo.config.provision.AthenzDomain;
import com.yahoo.config.provision.AthenzService;
+import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.RegionName;
@@ -72,6 +73,8 @@ public class DeploymentSpecXmlReader {
private static final String athenzDomainAttribute = "athenz-domain";
private static final String testerFlavorAttribute = "tester-flavor";
private static final String majorVersionAttribute = "major-version";
+ private static final String globalServiceIdAttribute = "global-service-id";
+ private static final String cloudAccountAttribute = "cloud-account";
private final boolean validate;
private final Clock clock;
@@ -118,7 +121,7 @@ public class DeploymentSpecXmlReader {
List<Step> steps = new ArrayList<>();
List<Endpoint> applicationEndpoints = List.of();
if ( ! containsTag(instanceTag, root)) { // deployment spec skipping explicit instance -> "default" instance
- steps.addAll(readInstanceContent("default", root, new MutableOptional<>(), root));
+ steps.addAll(readInstanceContent("default", root, new HashMap<>(), root));
}
else {
if (XML.getChildren(root).stream().anyMatch(child -> child.getTagName().equals(prodTag)))
@@ -129,9 +132,9 @@ public class DeploymentSpecXmlReader {
for (Element child : XML.getChildren(root)) {
String tagName = child.getTagName();
if (tagName.equals(instanceTag)) {
- steps.addAll(readInstanceContent(child.getAttribute(idAttribute), child, new MutableOptional<>(), root));
+ steps.addAll(readInstanceContent(child.getAttribute(idAttribute), child, new HashMap<>(), root));
} else {
- steps.addAll(readNonInstanceSteps(child, new MutableOptional<>(), root)); // (No global service id here)
+ steps.addAll(readNonInstanceSteps(child, new HashMap<>(), root)); // (No global service id here)
}
}
applicationEndpoints = readEndpoints(root, Optional.empty(), steps);
@@ -156,7 +159,7 @@ public class DeploymentSpecXmlReader {
*/
private List<DeploymentInstanceSpec> readInstanceContent(String instanceNameString,
Element instanceTag,
- MutableOptional<String> globalServiceId,
+ Map<String, String> prodAttributes,
Element parentTag) {
if (instanceNameString.isBlank())
illegal("<instance> attribute 'id' must be specified, and not be blank");
@@ -183,7 +186,7 @@ public class DeploymentSpecXmlReader {
// Values where there is no default
List<Step> steps = new ArrayList<>();
for (Element instanceChild : XML.getChildren(instanceTag))
- steps.addAll(readNonInstanceSteps(instanceChild, globalServiceId, instanceChild));
+ steps.addAll(readNonInstanceSteps(instanceChild, prodAttributes, instanceChild));
List<Endpoint> endpoints = readEndpoints(instanceTag, Optional.of(instanceNameString), steps);
// Build and return instances with these values
@@ -198,32 +201,39 @@ public class DeploymentSpecXmlReader {
upgradeRollout,
minRisk, maxRisk, maxIdleHours,
changeBlockers,
- globalServiceId.asOptional(),
+ Optional.ofNullable(prodAttributes.get(globalServiceIdAttribute)),
athenzService,
+ Optional.ofNullable(prodAttributes.get(cloudAccountAttribute))
+ .map(CloudAccount::new),
notifications,
endpoints,
now))
.collect(Collectors.toList());
}
- private List<Step> readSteps(Element stepTag, MutableOptional<String> globalServiceId, Element parentTag) {
+ private List<Step> readSteps(Element stepTag, Map<String, String> prodAttributes, Element parentTag) {
if (stepTag.getTagName().equals(instanceTag))
- return new ArrayList<>(readInstanceContent(stepTag.getAttribute(idAttribute), stepTag, globalServiceId, parentTag));
+ return new ArrayList<>(readInstanceContent(stepTag.getAttribute(idAttribute), stepTag, prodAttributes, parentTag));
else
- return readNonInstanceSteps(stepTag, globalServiceId, parentTag);
+ return readNonInstanceSteps(stepTag, prodAttributes, parentTag);
}
+
+
// Consume the given tag as 0-N steps. 0 if it is not a step, >1 if it contains multiple nested steps that should be flattened
@SuppressWarnings("fallthrough")
- private List<Step> readNonInstanceSteps(Element stepTag, MutableOptional<String> globalServiceId, Element parentTag) {
+ private List<Step> readNonInstanceSteps(Element stepTag, Map<String, String> prodAttributes, Element parentTag) {
Optional<AthenzService> athenzService = mostSpecificAttribute(stepTag, athenzServiceAttribute).map(AthenzService::from);
Optional<String> testerFlavor = mostSpecificAttribute(stepTag, testerFlavorAttribute);
- if (prodTag.equals(stepTag.getTagName()))
- globalServiceId.set(readGlobalServiceId(stepTag));
- else if (readGlobalServiceId(stepTag).isPresent())
- illegal("Attribute 'global-service-id' is only valid on 'prod' tag.");
+ if (prodTag.equals(stepTag.getTagName())) {
+ readGlobalServiceId(stepTag).ifPresent(id -> prodAttributes.put(globalServiceIdAttribute, id));
+ readCloudAccount(stepTag).ifPresent(account -> prodAttributes.put(cloudAccountAttribute, account));
+ } else {
+ if (readGlobalServiceId(stepTag).isPresent()) illegal("Attribute '" + globalServiceIdAttribute + "' is only valid on 'prod' tag");
+ if (!regionTag.equals(stepTag.getTagName()) && readCloudAccount(stepTag).isPresent()) illegal("Attribute '" + cloudAccountAttribute + "' is only valid on 'prod' or 'region' tag");
+ }
switch (stepTag.getTagName()) {
case testTag:
@@ -231,10 +241,10 @@ public class DeploymentSpecXmlReader {
.anyMatch(node -> prodTag.equals(node.getNodeName())))
return List.of(new DeclaredTest(RegionName.from(XML.getValue(stepTag).trim())));
case stagingTag:
- return List.of(new DeclaredZone(Environment.from(stepTag.getTagName()), Optional.empty(), false, athenzService, testerFlavor));
+ return List.of(new DeclaredZone(Environment.from(stepTag.getTagName()), Optional.empty(), false, athenzService, testerFlavor, Optional.empty()));
case prodTag: // regions, delay and parallel may be nested within, but we can flatten them
return XML.getChildren(stepTag).stream()
- .flatMap(child -> readNonInstanceSteps(child, globalServiceId, stepTag).stream())
+ .flatMap(child -> readNonInstanceSteps(child, prodAttributes, stepTag).stream())
.collect(Collectors.toList());
case delayTag:
return List.of(new Delay(Duration.ofSeconds(longAttribute("hours", stepTag) * 60 * 60 +
@@ -242,11 +252,11 @@ public class DeploymentSpecXmlReader {
longAttribute("seconds", stepTag))));
case parallelTag: // regions and instances may be nested within
return List.of(new ParallelSteps(XML.getChildren(stepTag).stream()
- .flatMap(child -> readSteps(child, globalServiceId, parentTag).stream())
+ .flatMap(child -> readSteps(child, prodAttributes, parentTag).stream())
.collect(Collectors.toList())));
case stepsTag: // regions and instances may be nested within
return List.of(new Steps(XML.getChildren(stepTag).stream()
- .flatMap(child -> readSteps(child, globalServiceId, parentTag).stream())
+ .flatMap(child -> readSteps(child, prodAttributes, parentTag).stream())
.collect(Collectors.toList())));
case regionTag:
return List.of(readDeclaredZone(Environment.prod, athenzService, testerFlavor, stepTag));
@@ -425,13 +435,19 @@ public class DeploymentSpecXmlReader {
private DeclaredZone readDeclaredZone(Environment environment, Optional<AthenzService> athenzService,
Optional<String> testerFlavor, Element regionTag) {
return new DeclaredZone(environment, Optional.of(RegionName.from(XML.getValue(regionTag).trim())),
- readActive(regionTag), athenzService, testerFlavor);
+ readActive(regionTag), athenzService, testerFlavor,
+ readCloudAccount(regionTag).map(CloudAccount::new));
+ }
+
+ private Optional<String> readCloudAccount(Element tag) {
+ return Optional.of(tag.getAttribute(cloudAccountAttribute))
+ .filter(account -> !account.isEmpty());
}
private Optional<String> readGlobalServiceId(Element environmentTag) {
- String globalServiceId = environmentTag.getAttribute("global-service-id");
+ String globalServiceId = environmentTag.getAttribute(globalServiceIdAttribute);
if (globalServiceId.isEmpty()) return Optional.empty();
- deprecate(environmentTag, List.of("global-service-id"), "See https://cloud.vespa.ai/en/reference/routing#deprecated-syntax");
+ deprecate(environmentTag, List.of(globalServiceIdAttribute), "See https://cloud.vespa.ai/en/reference/routing#deprecated-syntax");
return Optional.of(globalServiceId);
}
@@ -546,14 +562,4 @@ public class DeploymentSpecXmlReader {
throw new IllegalArgumentException(message);
}
- private static class MutableOptional<T> {
-
- private Optional<T> value = Optional.empty();
-
- public void set(Optional<T> value) { this.value = value; }
-
- public Optional<T> asOptional() { return value; }
-
- }
-
}
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 5073c6b9fb2..dfe8b324d1c 100644
--- a/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java
+++ b/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java
@@ -3,6 +3,7 @@ package com.yahoo.config.application.api;
import com.google.common.collect.ImmutableSet;
import com.yahoo.config.application.api.xml.DeploymentSpecXmlReader;
+import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.RegionName;
@@ -1509,6 +1510,27 @@ public class DeploymentSpecTest {
"</deployment>").deployableHashCode());
}
+ @Test
+ public void cloudAccount() {
+ StringReader r = new StringReader(
+ "<deployment version='1.0'>" +
+ " <instance id='beta'>" +
+ " <prod cloud-account='219876543210'>" +
+ " <region>us-west-1</region>" +
+ " </prod>" +
+ " </instance>" +
+ " <instance id='main'>" +
+ " <prod cloud-account='012345678912'>" +
+ " <region>us-east-1</region>" +
+ " </prod>" +
+ " </instance>" +
+ "</deployment>"
+ );
+ DeploymentSpec spec = DeploymentSpec.fromXml(r);
+ assertEquals(Optional.of(new CloudAccount("219876543210")), spec.requireInstance("beta").cloudAccount(Environment.prod, RegionName.from("us-east-1")));
+ assertEquals(Optional.of(new CloudAccount("012345678912")), spec.requireInstance("main").cloudAccount(Environment.prod, RegionName.from("us-west-1")));
+ }
+
private static void assertInvalid(String deploymentSpec, String errorMessagePart) {
assertInvalid(deploymentSpec, errorMessagePart, new ManualClock());
}
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 efed8ecc06c..8baeeb79441 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
@@ -2,6 +2,7 @@
package com.yahoo.config.application.api;
import com.google.common.collect.ImmutableSet;
+import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.RegionName;
import org.junit.Test;
@@ -702,6 +703,33 @@ public class DeploymentSpecWithoutInstanceTest {
assertEquals(Set.of("us-east", "us-west"), endpointRegions("default", spec));
}
+ @Test
+ public void productionSpecWithCloudAccount() {
+ StringReader r = new StringReader(
+ "<deployment version='1.0'>" +
+ " <prod cloud-account='012345678912'>" +
+ " <region cloud-account='219876543210'>us-east-1</region>" +
+ " <region>us-west-1</region>" +
+ " </prod>" +
+ "</deployment>"
+ );
+ DeploymentSpec spec = DeploymentSpec.fromXml(r);
+ assertEquals(Optional.of(new CloudAccount("219876543210")), spec.requireInstance("default").cloudAccount(Environment.prod, RegionName.from("us-east-1")));
+ assertEquals(Optional.of(new CloudAccount("012345678912")), spec.requireInstance("default").cloudAccount(Environment.prod, RegionName.from("us-west-1")));
+
+ r = new StringReader(
+ "<deployment version='1.0'>" +
+ " <prod>" +
+ " <region cloud-account='219876543210'>us-east-1</region>" +
+ " <region>us-west-1</region>" +
+ " </prod>" +
+ "</deployment>"
+ );
+ spec = DeploymentSpec.fromXml(r);
+ assertEquals(Optional.of(new CloudAccount("219876543210")), spec.requireInstance("default").cloudAccount(Environment.prod, RegionName.from("us-east-1")));
+ assertEquals(Optional.empty(), spec.requireInstance("default").cloudAccount(Environment.prod, RegionName.from("us-west-1")));
+ }
+
private static Set<String> endpointRegions(String endpointId, DeploymentSpec spec) {
return spec.requireInstance("default").endpoints().stream()
.filter(endpoint -> endpoint.endpointId().equals(endpointId))