From 2921d5bc1358b094db2e131970c969dcad481502 Mon Sep 17 00:00:00 2001 From: Martin Polden Date: Thu, 27 Jul 2023 12:21:14 +0200 Subject: Revert "Remove global-service-id" --- .../application/api/DeploymentInstanceSpec.java | 21 +++++++++++++--- .../config/application/api/DeploymentSpec.java | 10 ++++++-- .../api/xml/DeploymentSpecXmlReader.java | 29 ++++++++++++++++++++-- 3 files changed, 52 insertions(+), 8 deletions(-) (limited to 'config-model-api/src/main/java/com/yahoo/config/application') 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 4ca96453ad1..b6f934c8824 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,6 +1,7 @@ // 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.provision.AthenzService; import com.yahoo.config.provision.CloudAccount; import com.yahoo.config.provision.CloudName; @@ -57,6 +58,7 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Steps { private final int maxRisk; private final int maxIdleHours; private final List changeBlockers; + private final Optional globalServiceId; private final Optional athenzService; private final Map cloudAccounts; private final Optional hostTTL; @@ -74,6 +76,7 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Steps { DeploymentSpec.UpgradeRollout upgradeRollout, int minRisk, int maxRisk, int maxIdleHours, List changeBlockers, + Optional globalServiceId, Optional athenzService, Map cloudAccounts, Optional hostTTL, @@ -97,6 +100,7 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Steps { 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 = Objects.requireNonNull(changeBlockers); + this.globalServiceId = Objects.requireNonNull(globalServiceId); this.athenzService = Objects.requireNonNull(athenzService); this.cloudAccounts = Map.copyOf(cloudAccounts); this.hostTTL = Objects.requireNonNull(hostTTL); @@ -107,7 +111,7 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Steps { this.zoneEndpoints = Collections.unmodifiableMap(zoneEndpointsCopy); this.bcp = Objects.requireNonNull(bcp); validateZones(new HashSet<>(), new HashSet<>(), this); - validateEndpoints(this.endpoints); + validateEndpoints(globalServiceId, this.endpoints); validateChangeBlockers(changeBlockers, now); validateBcp(bcp); hostTTL.filter(Duration::isNegative).ifPresent(ttl -> illegal("Host TTL cannot be negative")); @@ -151,7 +155,11 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Steps { } /** Throw an IllegalArgumentException if an endpoint refers to a region that is not declared in 'prod' */ - private void validateEndpoints(List endpoints) { + private void validateEndpoints(Optional globalServiceId, List endpoints) { + if (globalServiceId.isPresent() && ! endpoints.isEmpty()) { + throw new IllegalArgumentException("Providing both 'endpoints' and 'global-service-id'. Use only 'endpoints'."); + } + var regions = prodRegions(); for (var endpoint : endpoints){ for (var endpointRegion : endpoint.regions()) { @@ -235,6 +243,9 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Steps { /** Returns time windows where upgrades are disallowed for these instances */ public List changeBlocker() { return changeBlockers; } + /** Returns the ID of the service to expose through global routing, if present */ + public Optional globalServiceId() { return globalServiceId; } + /** Returns whether the instances in this step can upgrade at the given instant */ public boolean canUpgradeAt(Instant instant) { return changeBlockers.stream().filter(block -> block.blocksVersions()) @@ -312,7 +323,8 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Steps { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DeploymentInstanceSpec other = (DeploymentInstanceSpec) o; - return upgradePolicy == other.upgradePolicy && + return globalServiceId.equals(other.globalServiceId) && + upgradePolicy == other.upgradePolicy && revisionTarget == other.revisionTarget && upgradeRollout == other.upgradeRollout && changeBlockers.equals(other.changeBlockers) && @@ -327,7 +339,7 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Steps { @Override public int hashCode() { - return Objects.hash(upgradePolicy, revisionTarget, upgradeRollout, changeBlockers, steps(), athenzService, notifications, endpoints, zoneEndpoints, bcp, tags); + return Objects.hash(globalServiceId, upgradePolicy, revisionTarget, upgradeRollout, changeBlockers, steps(), athenzService, notifications, endpoints, zoneEndpoints, bcp, tags); } int deployableHashCode() { @@ -337,6 +349,7 @@ public class DeploymentInstanceSpec extends DeploymentSpec.Steps { toHash[i++] = name; toHash[i++] = endpoints; toHash[i++] = zoneEndpoints; + toHash[i++] = globalServiceId; toHash[i++] = tags; toHash[i++] = bcp; toHash[i++] = cloudAccounts; 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 358744d40f7..797be652ebc 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 @@ -1,6 +1,7 @@ // 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.collections.Comparables; import com.yahoo.config.application.api.xml.DeploymentSpecXmlReader; import com.yahoo.config.provision.AthenzDomain; @@ -433,16 +434,17 @@ public class DeploymentSpec { private final Environment environment; private final Optional region; + private final boolean active; private final Optional athenzService; private final Optional testerFlavor; private final Map cloudAccounts; private final Optional hostTTL; public DeclaredZone(Environment environment) { - this(environment, Optional.empty(), Optional.empty(), Optional.empty(), Map.of(), Optional.empty()); + this(environment, Optional.empty(), false, Optional.empty(), Optional.empty(), Map.of(), Optional.empty()); } - public DeclaredZone(Environment environment, Optional region, + public DeclaredZone(Environment environment, Optional region, boolean active, Optional athenzService, Optional testerFlavor, Map cloudAccounts, Optional hostTTL) { if (environment != Environment.prod && region.isPresent()) @@ -452,6 +454,7 @@ public class DeploymentSpec { hostTTL.filter(Duration::isNegative).ifPresent(ttl -> illegal("Host TTL cannot be negative")); this.environment = Objects.requireNonNull(environment); this.region = Objects.requireNonNull(region); + this.active = active; this.athenzService = Objects.requireNonNull(athenzService); this.testerFlavor = Objects.requireNonNull(testerFlavor); this.cloudAccounts = Map.copyOf(cloudAccounts); @@ -463,6 +466,9 @@ public class DeploymentSpec { /** The region name, or empty if not declared */ public Optional region() { return region; } + /** Returns whether this zone should receive production traffic */ + public boolean active() { return active; } + public Optional testerFlavor() { return testerFlavor; } Optional athenzService() { return athenzService; } 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 4bfecabaf69..38562eefb03 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 @@ -93,6 +93,7 @@ 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 static final String hostTTLAttribute = "empty-host-ttl"; @@ -233,6 +234,7 @@ public class DeploymentSpecXmlReader { upgradeRollout, minRisk, maxRisk, maxIdleHours, changeBlockers, + Optional.ofNullable(prodAttributes.get(globalServiceIdAttribute)), athenzService, cloudAccounts, hostTTL, @@ -266,6 +268,12 @@ public class DeploymentSpecXmlReader { Optional athenzService = mostSpecificAttribute(stepTag, athenzServiceAttribute).map(AthenzService::from); Optional testerFlavor = mostSpecificAttribute(stepTag, testerFlavorAttribute); + if (prodTag.equals(stepTag.getTagName())) { + readGlobalServiceId(stepTag).ifPresent(id -> prodAttributes.put(globalServiceIdAttribute, id)); + } else { + if (readGlobalServiceId(stepTag).isPresent()) illegal("Attribute '" + globalServiceIdAttribute + "' is only valid on 'prod' tag"); + } + switch (stepTag.getTagName()) { case testTag: if (Stream.iterate(stepTag, Objects::nonNull, Node::getParentNode) @@ -273,7 +281,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(), false, athenzService, testerFlavor, 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()) @@ -682,7 +690,7 @@ public class DeploymentSpecXmlReader { private DeclaredZone readDeclaredZone(Environment environment, Optional athenzService, Optional testerFlavor, Element regionTag) { return new DeclaredZone(environment, Optional.of(RegionName.from(XML.getValue(regionTag).trim())), - athenzService, testerFlavor, + readActive(regionTag), athenzService, testerFlavor, readCloudAccounts(regionTag), readHostTTL(regionTag)); } @@ -706,6 +714,13 @@ public class DeploymentSpecXmlReader { return mostSpecificAttribute(tag, hostTTLAttribute).map(s -> toDuration(s, "empty host TTL")); } + private Optional readGlobalServiceId(Element environmentTag) { + String globalServiceId = environmentTag.getAttribute(globalServiceIdAttribute); + if (globalServiceId.isEmpty()) return Optional.empty(); + deprecate(environmentTag, List.of(globalServiceIdAttribute), 7, "See https://cloud.vespa.ai/en/reference/routing#deprecated-syntax"); + return Optional.of(globalServiceId); + } + private List readChangeBlockers(Element parent, Element globalBlockersParent) { List changeBlockers = new ArrayList<>(); if (globalBlockersParent != parent) { @@ -784,6 +799,16 @@ public class DeploymentSpecXmlReader { }; } + private boolean readActive(Element regionTag) { + String activeValue = regionTag.getAttribute("active"); + if ("".equals(activeValue)) return true; // Default to active + deprecate(regionTag, List.of("active"), 7, "See https://cloud.vespa.ai/en/reference/routing#deprecated-syntax"); + if ("true".equals(activeValue)) return true; + if ("false".equals(activeValue)) return false; + throw new IllegalArgumentException("Value of 'active' attribute in region tag must be 'true' or 'false' " + + "to control whether this region should receive traffic from the global endpoint of this application"); + } + private void deprecate(Element element, List attributes, int majorVersion, String message) { deprecatedElements.add(new DeprecatedElement(majorVersion, element.getTagName(), attributes, message)); } -- cgit v1.2.3