diff options
5 files changed, 74 insertions, 15 deletions
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java b/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java index 4308e0c2a0e..fdaa7d57074 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 @@ -435,15 +435,16 @@ public class DeploymentSpec { private final Optional<RegionName> region; private final Optional<AthenzService> athenzService; private final Optional<String> testerFlavor; + private final Optional<String> testerNodes; private final Map<CloudName, CloudAccount> cloudAccounts; private final Optional<Duration> hostTTL; public DeclaredZone(Environment environment) { - this(environment, Optional.empty(), Optional.empty(), Optional.empty(), Map.of(), Optional.empty()); + this(environment, Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Map.of(), Optional.empty()); } - public DeclaredZone(Environment environment, Optional<RegionName> region, - Optional<AthenzService> athenzService, Optional<String> testerFlavor, + public DeclaredZone(Environment environment, Optional<RegionName> region, Optional<AthenzService> athenzService, + Optional<String> testerFlavor, Optional<String> testerNodes, Map<CloudName, CloudAccount> cloudAccounts, Optional<Duration> hostTTL) { if (environment != Environment.prod && region.isPresent()) illegal("Non-prod environments cannot specify a region"); @@ -454,6 +455,7 @@ public class DeploymentSpec { this.region = Objects.requireNonNull(region); this.athenzService = Objects.requireNonNull(athenzService); this.testerFlavor = Objects.requireNonNull(testerFlavor); + this.testerNodes = Objects.requireNonNull(testerNodes); this.cloudAccounts = Map.copyOf(cloudAccounts); this.hostTTL = Objects.requireNonNull(hostTTL); } @@ -463,11 +465,12 @@ public class DeploymentSpec { /** The region name, or empty if not declared */ public Optional<RegionName> region() { return region; } - // TODO(mpolden): Remove after Vespa < 8.203 is no longer in use - public boolean active() { return true; } - + // TODO jonmv: remove after 8.350. public Optional<String> testerFlavor() { return testerFlavor; } + /** The XML <nodes> tag of the tester application for this zone, if specified. */ + public Optional<String> testerNodes() { return testerNodes; } + Optional<AthenzService> athenzService() { return athenzService; } Map<CloudName, CloudAccount> cloudAccounts() { return cloudAccounts; } 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 23471264960..1f5fa228d8f 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 @@ -92,6 +92,8 @@ public class DeploymentSpecXmlReader { private static final String athenzServiceAttribute = "athenz-service"; private static final String athenzDomainAttribute = "athenz-domain"; private static final String testerFlavorAttribute = "tester-flavor"; + private static final String testerTag = "tester"; + private static final String nodesTag = "nodes"; private static final String majorVersionAttribute = "major-version"; private static final String cloudAccountAttribute = "cloud-account"; private static final String hostTTLAttribute = "empty-host-ttl"; @@ -265,6 +267,7 @@ public class DeploymentSpecXmlReader { private List<Step> readNonInstanceSteps(Element stepTag, Map<String, String> prodAttributes, Element parentTag, Bcp defaultBcp) { Optional<AthenzService> athenzService = mostSpecificAttribute(stepTag, athenzServiceAttribute).map(AthenzService::from); Optional<String> testerFlavor = mostSpecificAttribute(stepTag, testerFlavorAttribute); + Optional<String> testerNodes = mostSpecificSibling(stepTag, testerTag).map(tester -> XML.getChild(tester, nodesTag)).map(XML::toString); switch (stepTag.getTagName()) { case testTag: @@ -273,7 +276,7 @@ public class DeploymentSpecXmlReader { return List.of(new DeclaredTest(RegionName.from(XML.getValue(stepTag).trim()), readHostTTL(stepTag))); // A production test } case devTag, perfTag, stagingTag: // Intentional fallthrough from test tag. - return List.of(new DeclaredZone(Environment.from(stepTag.getTagName()), Optional.empty(), athenzService, testerFlavor, readCloudAccounts(stepTag), readHostTTL(stepTag))); + return List.of(new DeclaredZone(Environment.from(stepTag.getTagName()), Optional.empty(), athenzService, testerFlavor, testerNodes, readCloudAccounts(stepTag), readHostTTL(stepTag))); case prodTag: // regions, delay and parallel may be nested within, but we can flatten them return XML.getChildren(stepTag).stream() .flatMap(child -> readNonInstanceSteps(child, prodAttributes, stepTag, defaultBcp).stream()) @@ -291,7 +294,7 @@ public class DeploymentSpecXmlReader { .flatMap(child -> readSteps(child, prodAttributes, parentTag, defaultBcp).stream()) .toList())); case regionTag: - return List.of(readDeclaredZone(Environment.prod, athenzService, testerFlavor, stepTag)); + return List.of(readDeclaredZone(Environment.prod, athenzService, testerFlavor, testerNodes, stepTag)); default: return List.of(); } @@ -680,9 +683,9 @@ public class DeploymentSpecXmlReader { } private DeclaredZone readDeclaredZone(Environment environment, Optional<AthenzService> athenzService, - Optional<String> testerFlavor, Element regionTag) { + Optional<String> testerFlavor, Optional<String> testerNodes, Element regionTag) { return new DeclaredZone(environment, Optional.of(RegionName.from(XML.getValue(regionTag).trim())), - athenzService, testerFlavor, + athenzService, testerFlavor, testerNodes, readCloudAccounts(regionTag), readHostTTL(regionTag)); } @@ -808,6 +811,16 @@ public class DeploymentSpecXmlReader { return mostSpecificAttribute(tag, attributeName, true); } + /** Returns the first encountered sibling with the given name, or sibling of parent, or sibling of grandparent, etc.. */ + private static Optional<Element> mostSpecificSibling(Element tag, String siblingName) { + return Stream.iterate(tag, Objects::nonNull, Node::getParentNode) + .filter(Element.class::isInstance) + .map(Element.class::cast) + .map(element -> XML.getChild(element, siblingName)) + .filter(Objects::nonNull) + .findFirst(); + } + /** * Returns a string consisting of a number followed by "m", "h" or "d" to a duration given in that unit, * or zero duration if null or blank. 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 05807ae6cc1..a3df216eea7 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 @@ -1128,7 +1128,7 @@ public class DeploymentSpecTest { } @Test - public void customTesterFlavor() { + public void customLegacyTesterFlavor() { DeploymentSpec spec = DeploymentSpec.fromXml(""" <deployment> <instance id='default'> @@ -1145,6 +1145,42 @@ public class DeploymentSpecTest { } @Test + public void customTesterFlavor() { + DeploymentSpec spec = DeploymentSpec.fromXml(""" + <deployment> + <instance id='default'> + <test> + <tester> + <nodes docker-image="foo"> + <resources vcpu="1" memory="3.5Gb" disk="30Gb" architecture="arm64" /> + </nodes> + </tester> + </test> + <staging /> + <prod> + <tester> + <nodes> + <resources vcpu="2" memory="7Gb" disk="30Gb" /> + </nodes> + </tester> + <region>us-north-7</region> + </prod> + </instance> + </deployment>"""); + assertEquals(Optional.of(""" + <nodes docker-image="foo"> + <resources architecture="arm64" disk="30Gb" memory="3.5Gb" vcpu="1"/> + </nodes>"""), + spec.requireInstance("default").steps().get(0).zones().get(0).testerNodes()); + assertEquals(Optional.empty(), spec.requireInstance("default").steps().get(1).zones().get(0).testerNodes()); + assertEquals(Optional.of(""" + <nodes> + <resources disk="30Gb" memory="7Gb" vcpu="2"/> + </nodes>"""), + spec.requireInstance("default").steps().get(2).zones().get(0).testerNodes()); + } + + @Test public void noEndpoints() { DeploymentSpec spec = DeploymentSpec.fromXml(""" <deployment> diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java index d877600db13..11f4c9794aa 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java @@ -137,7 +137,7 @@ public class NodesSpecification { int defaultMinGroups = nodes.from().orElse(1) / groupSize.to().orElse(nodes.from().orElse(1)); int defaultMaxGroups = groupSize.isEmpty() ? 1 : nodes.to().orElse(1) / groupSize.from().orElse(1); - var min = new ClusterResources(nodes.from().orElse(1), groups.from().orElse(defaultMinGroups), nodeResources(nodesElement).getFirst()); + var min = new ClusterResources(nodes.from().orElse(1), groups.from().orElse(defaultMinGroups), nodeResources(nodesElement).getFirst()); var max = new ClusterResources(nodes.to().orElse(1), groups.to().orElse(defaultMaxGroups), nodeResources(nodesElement).getSecond()); return new ResourceConstraints(min, max, groupSize); } diff --git a/config-model/src/main/resources/schema/deployment.rnc b/config-model/src/main/resources/schema/deployment.rnc index 3491d868f20..f79fc614a53 100644 --- a/config-model/src/main/resources/schema/deployment.rnc +++ b/config-model/src/main/resources/schema/deployment.rnc @@ -2,6 +2,8 @@ # RELAX NG Compact Syntax # Vespa Deployment file +include "common.rnc" + start = element deployment { attribute version { "1.0" } & attribute major-version { text }? & @@ -100,7 +102,7 @@ Test = element test { attribute tester-flavor { xsd:string }? & attribute cloud-account { xsd:string }? & attribute empty-host-ttl { xsd:string }? & - text + Tester? } Staging = element staging { @@ -108,7 +110,7 @@ Staging = element staging { attribute tester-flavor { xsd:string }? & attribute cloud-account { xsd:string }? & attribute empty-host-ttl { xsd:string }? & - text + Tester? } Dev = element dev { @@ -129,7 +131,8 @@ Prod = element prod { Region* & Delay* & ProdTest* & - ParallelSteps* + ParallelSteps* & + Tester? } ProdTest = element test { @@ -197,3 +200,7 @@ MemberRegion = element region { attribute fraction { xsd:double }? & text } + +Tester = element tester { + Nodes? +}
\ No newline at end of file |