From 0875cb2e7f31396a42a2543b3c54ba07cde0ef6a Mon Sep 17 00:00:00 2001 From: Øyvind Grønnesby Date: Wed, 29 May 2019 15:47:09 +0200 Subject: Add endpoints to deployment specification Endpoints are now part of the DeploymentSpec and understood by the DeploymentSpecXmlReader classes. --- .../config/application/api/DeploymentSpec.java | 21 ++++++--- .../com/yahoo/config/application/api/Endpoint.java | 51 ++++++++++++++++++++++ .../api/xml/DeploymentSpecXmlReader.java | 36 ++++++++++++++- .../config/application/api/DeploymentSpecTest.java | 42 +++++++++++++++++- 4 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 config-model-api/src/main/java/com/yahoo/config/application/api/Endpoint.java (limited to 'config-model-api') 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 66111b91aa0..05f5f667287 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 @@ -46,7 +46,8 @@ public class DeploymentSpec { "", Optional.empty(), Optional.empty(), - Notifications.none()); + Notifications.none(), + Collections.emptyList()); private final Optional globalServiceId; private final UpgradePolicy upgradePolicy; @@ -57,10 +58,12 @@ public class DeploymentSpec { private final Optional athenzDomain; private final Optional athenzService; private final Notifications notifications; + private final List endpoints; public DeploymentSpec(Optional globalServiceId, UpgradePolicy upgradePolicy, Optional majorVersion, List changeBlockers, List steps, String xmlForm, - Optional athenzDomain, Optional athenzService, Notifications notifications) { + Optional athenzDomain, Optional athenzService, Notifications notifications, + List endpoints) { validateTotalDelay(steps); this.globalServiceId = globalServiceId; this.upgradePolicy = upgradePolicy; @@ -71,6 +74,7 @@ public class DeploymentSpec { this.athenzDomain = athenzDomain; this.athenzService = athenzService; this.notifications = notifications; + this.endpoints = Objects.requireNonNull(endpoints, "Missing endpoints parameter"); validateZones(this.steps); validateAthenz(); } @@ -101,16 +105,16 @@ public class DeploymentSpec { */ private void validateAthenz() { // If athenz domain is not set, athenz service cannot be set on any level - if (! athenzDomain.isPresent()) { + if (athenzDomain.isEmpty()) { for (DeclaredZone zone : zones()) { if(zone.athenzService().isPresent()) { throw new IllegalArgumentException("Athenz service configured for zone: " + zone + ", but Athenz domain is not configured"); } } // if athenz domain is not set, athenz service must be set implicitly or directly on all zones. - } else if(! athenzService.isPresent()) { + } else if (athenzService.isEmpty()) { for (DeclaredZone zone : zones()) { - if(! zone.athenzService().isPresent()) { + if (zone.athenzService().isEmpty()) { throw new IllegalArgumentException("Athenz domain is configured, but Athenz service not configured for zone: " + zone); } } @@ -199,6 +203,9 @@ public class DeploymentSpec { /** Returns the notification configuration */ public Notifications notifications() { return notifications; } + /** Returns the rotations configuration */ + public List endpoints() { return Collections.unmodifiableList(endpoints); } + /** Returns the XML form of this spec, or null if it was not created by fromXml, nor is empty */ public String xmlForm() { return xmlForm; } @@ -369,7 +376,7 @@ public class DeploymentSpec { Optional athenzService, Optional testerFlavor) { if (environment != Environment.prod && region.isPresent()) throw new IllegalArgumentException("Non-prod environments cannot specify a region"); - if (environment == Environment.prod && ! region.isPresent()) + if (environment == Environment.prod && region.isEmpty()) throw new IllegalArgumentException("Prod environments must be specified with a region"); this.environment = environment; this.region = region; @@ -417,7 +424,7 @@ public class DeploymentSpec { @Override public String toString() { - return environment + ( region.isPresent() ? "." + region.get() : ""); + return environment + (region.map(regionName -> "." + regionName).orElse("")); } } diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/Endpoint.java b/config-model-api/src/main/java/com/yahoo/config/application/api/Endpoint.java new file mode 100644 index 00000000000..b505bf72faf --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/Endpoint.java @@ -0,0 +1,51 @@ +package com.yahoo.config.application.api; + +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +/** + * Represents a (global) endpoint in 'deployments.xml'. It contains the name of the + * endpoint (endpointId) and the name of the container cluster that the endpoint + * should point to. + * + * If the endpointId is not set, it will default to the same as the containerId. + */ +public class Endpoint { + private final Optional endpointId; + private final String containerId; + private final Set regions; + + public Endpoint(Optional endpointId, String containerId, Set regions) { + this.endpointId = endpointId; + this.containerId = containerId; + this.regions = Set.copyOf(regions); + } + + public String endpointId() { + return endpointId.orElse(containerId); + } + + public String containerId() { + return containerId; + } + + public Set regions() { + return regions; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Endpoint endpoint = (Endpoint) o; + return Objects.equals(endpointId, endpoint.endpointId) && + Objects.equals(containerId, endpoint.containerId) && + Objects.equals(regions, endpoint.regions); + } + + @Override + public int hashCode() { + return Objects.hash(endpointId, containerId, regions); + } +} 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 8e899b28a06..1078317e0d2 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 @@ -6,6 +6,7 @@ import com.yahoo.config.application.api.DeploymentSpec.DeclaredZone; import com.yahoo.config.application.api.DeploymentSpec.Delay; import com.yahoo.config.application.api.DeploymentSpec.ParallelZones; import com.yahoo.config.application.api.DeploymentSpec.Step; +import com.yahoo.config.application.api.Endpoint; import com.yahoo.config.application.api.Notifications; import com.yahoo.config.application.api.Notifications.Role; import com.yahoo.config.application.api.Notifications.When; @@ -25,6 +26,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -40,6 +42,8 @@ public class DeploymentSpecXmlReader { private static final String stagingTag = "staging"; private static final String blockChangeTag = "block-change"; private static final String prodTag = "prod"; + private static final String endpointsTag = "endpoints"; + private static final String endpointTag = "endpoint"; private final boolean validate; @@ -123,7 +127,8 @@ public class DeploymentSpecXmlReader { xmlForm, athenzDomain, athenzService, - readNotifications(root)); + readNotifications(root), + readEndpoints(root)); } private Notifications readNotifications(Element root) { @@ -152,6 +157,35 @@ public class DeploymentSpecXmlReader { return Notifications.of(emailAddresses, emailRoles); } + private List readEndpoints(Element root) { + final var endpointsElement = XML.getChild(root, endpointsTag); + if (endpointsElement == null) { return Collections.emptyList(); } + + final var endpoints = new ArrayList(); + + for (var endpointElement : XML.getChildren(endpointsElement, endpointTag)) { + final Optional rotationId = stringAttribute("id", endpointElement); + final Optional containerId = stringAttribute("container-id", endpointElement); + final var regions = new HashSet(); + + if (containerId.isEmpty()) { + throw new IllegalArgumentException("Missing 'container-id' from 'endpoint' tag."); + } + + for (var regionElement : XML.getChildren(endpointElement, "region")) { + var region = regionElement.getTextContent(); + if (region == null || region.isEmpty() || region.isBlank()) { + throw new IllegalArgumentException("Empty 'region' element in 'endpoint' tag."); + } + regions.add(regionElement.getTextContent()); + } + + endpoints.add(new Endpoint(rotationId, containerId.get(), regions)); + } + + return endpoints; + } + /** * Imposes some constraints on tag order which are not expressible in the schema */ 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 46e6dc457e9..e783ee89729 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 @@ -1,9 +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.config.application.api; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.yahoo.config.provision.Deployment; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.RegionName; import org.junit.Test; @@ -11,7 +9,11 @@ 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; @@ -452,4 +454,40 @@ public class DeploymentSpecTest { assertEquals(Optional.of("d-2-8-50"), spec.steps().get(2).zones().get(0).testerFlavor()); } + @Test + public void noEndpoints() { + assertEquals(Collections.emptyList(), DeploymentSpec.fromXml("").endpoints()); + } + + @Test + public void emptyEndpoints() { + final var spec = DeploymentSpec.fromXml(""); + assertEquals(Collections.emptyList(), spec.endpoints()); + } + + @Test + public void someEndpoints() { + final var spec = DeploymentSpec.fromXml("" + + "" + + " " + + " " + + " us-east" + + " " + + " " + + " " + + " " + + ""); + + assertEquals( + List.of("foo", "nalle", "quux"), + spec.endpoints().stream().map(Endpoint::endpointId).collect(Collectors.toList()) + ); + + assertEquals( + List.of("bar", "frosk", "quux"), + spec.endpoints().stream().map(Endpoint::containerId).collect(Collectors.toList()) + ); + + assertEquals(Set.of("us-east"), spec.endpoints().get(0).regions()); + } } -- cgit v1.2.3