diff options
author | Jon Bratseth <bratseth@verizonmedia.com> | 2019-10-03 15:27:26 +0200 |
---|---|---|
committer | Jon Bratseth <bratseth@verizonmedia.com> | 2019-10-03 15:27:26 +0200 |
commit | def6fe944d8717802b2ab67246b1cbb78b5afab9 (patch) | |
tree | 13f0bd97210e56dcc52626d34c23acd8b91d8b0a | |
parent | 27411b5dc5fa85ba259948b205397097042febe7 (diff) |
Basic support for explicit instance
4 files changed, 231 insertions, 161 deletions
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentInstancesSpec.java b/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentInstancesSpec.java index 13e7ba91049..344f3438804 100644 --- a/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentInstancesSpec.java +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentInstancesSpec.java @@ -25,7 +25,7 @@ import java.util.stream.Collectors; public class DeploymentInstancesSpec extends DeploymentSpec.Step { /** The instances deployed in this step */ - private final List<InstanceName> names; + private final List<InstanceName> ids; private final List<DeploymentSpec.Step> steps; private final DeploymentSpec.UpgradePolicy upgradePolicy; @@ -33,30 +33,33 @@ public class DeploymentInstancesSpec extends DeploymentSpec.Step { private final Optional<String> globalServiceId; private final Optional<AthenzDomain> athenzDomain; private final Optional<AthenzService> athenzService; + private final Notifications notifications; private final List<Endpoint> endpoints; - public DeploymentInstancesSpec(List<InstanceName> names, + public DeploymentInstancesSpec(List<InstanceName> ids, List<DeploymentSpec.Step> steps, DeploymentSpec.UpgradePolicy upgradePolicy, List<DeploymentSpec.ChangeBlocker> changeBlockers, Optional<String> globalServiceId, Optional<AthenzDomain> athenzDomain, Optional<AthenzService> athenzService, + Notifications notifications, List<Endpoint> endpoints) { - this.names = List.copyOf(names); + this.ids = List.copyOf(ids); this.steps = List.copyOf(completeSteps(new ArrayList<>(steps))); this.upgradePolicy = upgradePolicy; this.changeBlockers = changeBlockers; this.globalServiceId = globalServiceId; this.athenzDomain = athenzDomain; this.athenzService = athenzService; + this.notifications = notifications; this.endpoints = List.copyOf(validateEndpoints(endpoints, this.steps)); validateZones(this.steps); validateEndpoints(this.steps, globalServiceId, this.endpoints); validateAthenz(); } - public List<InstanceName> names() { return names; } + public List<InstanceName> names() { return ids; } /** Adds missing required steps and reorders steps to a permissible order */ private static List<DeploymentSpec.Step> completeSteps(List<DeploymentSpec.Step> steps) { @@ -222,9 +225,6 @@ public class DeploymentInstancesSpec extends DeploymentSpec.Step { return false; } - /** Returns the rotations configuration of these instances */ - public List<Endpoint> endpoints() { return endpoints; } - /** Returns the athenz domain if configured */ public Optional<AthenzDomain> athenzDomain() { return athenzDomain; } @@ -237,6 +237,13 @@ public class DeploymentInstancesSpec extends DeploymentSpec.Step { .orElse(this.athenzService.orElse(null)); return Optional.ofNullable(athenzService); } + + /** Returns the notification configuration of these instances */ + public Notifications notifications() { return notifications; } + + /** Returns the rotations configuration of these instances */ + public List<Endpoint> endpoints() { return endpoints; } + /** Returns whether this instances deployment specifies the given zone, either implicitly or explicitly */ public boolean includes(Environment environment, Optional<RegionName> region) { for (DeploymentSpec.Step step : steps) @@ -254,12 +261,19 @@ public class DeploymentInstancesSpec extends DeploymentSpec.Step { changeBlockers.equals(other.changeBlockers) && steps.equals(other.steps) && athenzDomain.equals(other.athenzDomain) && - athenzService.equals(other.athenzService); + athenzService.equals(other.athenzService) && + notifications.equals(other.notifications) && + endpoints.equals(other.endpoints); } @Override public int hashCode() { - return Objects.hash(globalServiceId, upgradePolicy, changeBlockers, steps, athenzDomain, athenzService); + return Objects.hash(globalServiceId, upgradePolicy, changeBlockers, steps, athenzDomain, athenzService, notifications, endpoints); + } + + @Override + public String toString() { + return "instance" + ( ids.size() < 1 ? " " : "s " ) + ids; } } 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 7a554cbb749..4079fbf2fbb 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 @@ -49,16 +49,13 @@ public class DeploymentSpec { List.of()); private final List<Step> steps; - private final Notifications notifications; private final Optional<Integer> majorVersion; private final String xmlForm; public DeploymentSpec(List<Step> steps, - Notifications notifications, Optional<Integer> majorVersion, String xmlForm) { this.steps = steps; - this.notifications = notifications; this.majorVersion = majorVersion; this.xmlForm = xmlForm; validateTotalDelay(steps); @@ -77,8 +74,8 @@ public class DeploymentSpec { globalServiceId, athenzDomain, athenzService, + notifications, endpoints)), - notifications, majorVersion, xmlForm); } @@ -95,7 +92,8 @@ public class DeploymentSpec { private DeploymentInstancesSpec defaultInstance() { if (hasDefaultInstanceStepOnly()) return (DeploymentInstancesSpec)steps.get(0); throw new IllegalArgumentException("This deployment spec does not support the legacy API " + - "as it does not consist only of a default instance"); + "as it does not consist only of a default instance. Content: " + + steps.stream().map(Step::toString).collect(Collectors.joining(","))); } // TODO: Remove after October 2019 @@ -136,31 +134,28 @@ public class DeploymentSpec { .collect(Collectors.toList()); } - /** Returns the notification configuration */ - public Notifications notifications() { return notifications; } - - /** Returns the rotations configuration */ - // TODO: Remove after October 2019 - public List<Endpoint> endpoints() { return defaultInstance().endpoints(); } - - /** Returns the athenz domain if configured */ // TODO: Remove after October 2019 public Optional<AthenzDomain> athenzDomain() { return defaultInstance().athenzDomain(); } - /** Returns the athenz service for environment/region if configured */ // TODO: Remove after October 2019 public Optional<AthenzService> athenzService(Environment environment, RegionName region) { return defaultInstance().athenzService(environment, region); } // TODO: Remove after October 2019 - public boolean includes(Environment environment, Optional<RegionName> region) { - return defaultInstance().deploysTo(environment, region); - } + public Notifications notifications() { return defaultInstance().notifications(); } + + // TODO: Remove after October 2019 + public List<Endpoint> endpoints() { return defaultInstance().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; } + // TODO: Remove after October 2019 + public boolean includes(Environment environment, Optional<RegionName> region) { + return defaultInstance().deploysTo(environment, region); + } + /** * Creates a deployment spec from XML. * @@ -212,13 +207,12 @@ public class DeploymentSpec { DeploymentSpec other = (DeploymentSpec) o; return majorVersion.equals(other.majorVersion) && steps.equals(other.steps) && - xmlForm.equals(other.xmlForm) && - notifications.equals(other.notifications); + xmlForm.equals(other.xmlForm); } @Override public int hashCode() { - return Objects.hash(majorVersion, steps, xmlForm, notifications); + return Objects.hash(majorVersion, steps, xmlForm); } /** This may be invoked by a continuous build */ @@ -361,21 +355,30 @@ public class DeploymentSpec { } - /** A deployment step which is to run deployment to multiple zones in parallel */ + /** A deployment step which is to run multiple steps (zones or instances) in parallel */ public static class ParallelZones extends Step { - private final List<DeclaredZone> zones; + private final List<Step> steps; - public ParallelZones(List<DeclaredZone> zones) { - this.zones = List.copyOf(zones); + public ParallelZones(List<Step> steps) { + this.steps = List.copyOf(steps); } + /** Returns the steps inside this which are zones */ @Override - public List<DeclaredZone> zones() { return this.zones; } + public List<DeclaredZone> zones() { + return this.steps.stream() + .filter(step -> step instanceof DeclaredZone) + .map(DeclaredZone.class::cast) + .collect(Collectors.toList()); + } + + /** Returns all the steps nested in this */ + public List<Step> steps() { return steps; } @Override public boolean deploysTo(Environment environment, Optional<RegionName> region) { - return zones.stream().anyMatch(zone -> zone.deploysTo(environment, region)); + return zones().stream().anyMatch(zone -> zone.deploysTo(environment, region)); } @Override @@ -383,13 +386,14 @@ public class DeploymentSpec { if (this == o) return true; if (!(o instanceof ParallelZones)) return false; ParallelZones that = (ParallelZones) o; - return Objects.equals(zones, that.zones); + return Objects.equals(steps, that.steps); } @Override public int hashCode() { - return Objects.hash(zones); + return Objects.hash(steps); } + } /** Controls when this application will be upgraded to new Vespa versions */ 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 72a806bb7be..acecb320b83 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 @@ -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.config.application.api.xml; +import com.yahoo.config.application.api.DeploymentInstancesSpec; import com.yahoo.config.application.api.DeploymentSpec; import com.yahoo.config.application.api.DeploymentSpec.DeclaredZone; import com.yahoo.config.application.api.DeploymentSpec.Delay; @@ -14,6 +15,7 @@ import com.yahoo.config.application.api.TimeWindow; import com.yahoo.config.provision.AthenzDomain; import com.yahoo.config.provision.AthenzService; import com.yahoo.config.provision.Environment; +import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.RegionName; import com.yahoo.io.IOUtils; import com.yahoo.text.XML; @@ -38,27 +40,32 @@ import java.util.stream.Collectors; */ public class DeploymentSpecXmlReader { + private static final String instanceTag = "instance"; private static final String majorVersionTag = "major-version"; private static final String testTag = "test"; private static final String stagingTag = "staging"; private static final String blockChangeTag = "block-change"; private static final String prodTag = "prod"; + private static final String regionTag = "region"; + private static final String delayTag = "delay"; + private static final String parallelTag = "parallel"; private static final String endpointsTag = "endpoints"; private static final String endpointTag = "endpoint"; + private static final String athenzServiceAttribute = "athenz-service"; + private static final String testerFlavorAttribute = "tester-flavor"; + private final boolean validate; - /** - * Creates a validating reader - */ + /** Creates a validating reader */ public DeploymentSpecXmlReader() { this(true); } /** - * Creates a reader + * Creates a deployment spec reader * - * @param validate true to validate the input, false to accept any input which can be unabiguously parsed + * @param validate true to validate the input, false to accept any input which can be unambiguously parsed */ public DeploymentSpecXmlReader(boolean validate) { this.validate = validate; @@ -73,63 +80,102 @@ public class DeploymentSpecXmlReader { } } - /** - * Reads a deployment spec from XML - */ + /** Reads a deployment spec from XML */ public DeploymentSpec read(String xmlForm) { - List<Step> steps = new ArrayList<>(); - Optional<String> globalServiceId = Optional.empty(); Element root = XML.getDocument(xmlForm).getDocumentElement(); - if (validate) - validateTagOrder(root); - for (Element environmentTag : XML.getChildren(root)) { - if (!isEnvironmentName(environmentTag.getTagName())) continue; - - Environment environment = Environment.from(environmentTag.getTagName()); - Optional<AthenzService> athenzService = stringAttribute("athenz-service", environmentTag).map(AthenzService::from); - Optional<String> testerFlavor = stringAttribute("tester-flavor", environmentTag); - - if (environment == Environment.prod) { - for (Element stepTag : XML.getChildren(environmentTag)) { - if (stepTag.getTagName().equals("delay")) { - steps.add(new Delay(Duration.ofSeconds(longAttribute("hours", stepTag) * 60 * 60 + - longAttribute("minutes", stepTag) * 60 + - longAttribute("seconds", stepTag)))); - } - else if (stepTag.getTagName().equals("parallel")) { - List<DeclaredZone> zones = new ArrayList<>(); - for (Element regionTag : XML.getChildren(stepTag)) { - zones.add(readDeclaredZone(environment, athenzService, testerFlavor, regionTag)); - } - steps.add(new ParallelZones(zones)); - } - else { // a region: deploy step - steps.add(readDeclaredZone(environment, athenzService, testerFlavor, stepTag)); - } - } - } - else { - steps.add(new DeclaredZone(environment, Optional.empty(), false, athenzService, testerFlavor)); + + List<Step> steps = new ArrayList<>(); + if ( ! hasChildTag(instanceTag, root)) { // deployment spec skipping explicit instance -> "default" instance + steps.add(readInstanceContent("default", root)); + } + else { + if (hasChildTag(prodTag, root)) + throw new IllegalArgumentException("A deployment spec cannot have both a <prod> tag and an " + + "<instance> tag under the root: " + + "Wrap the prod tags inside the appropriate instance"); + + for (Element topLevelTag : XML.getChildren(root)) { + if (topLevelTag.getTagName().equals(instanceTag)) + steps.add(readInstanceContent(topLevelTag.getAttribute("id"), topLevelTag)); + else + steps.addAll(readNonInstanceSteps(topLevelTag, new MutableOptional<>(), topLevelTag)); // (No global service id here) } + } + + return new DeploymentSpec(steps, + optionalIntegerAttribute(majorVersionTag, root), + xmlForm); + } + + /** + * Reads the content of an (implicit or explicit) instance tag producing an instances step + * + * @param instanceIdString a comma-separated list of the ids of the instance id(s) this is for + * @param instanceElement the element having the content of this instance + */ + private DeploymentInstancesSpec readInstanceContent(String instanceIdString, Element instanceElement) { + if (validate) + validateTagOrder(instanceElement); - if (environment == Environment.prod) - globalServiceId = readGlobalServiceId(environmentTag); - else if (readGlobalServiceId(environmentTag).isPresent()) - throw new IllegalArgumentException("Attribute 'global-service-id' is only valid on 'prod' tag."); + List<InstanceName> instanceNames = Arrays.stream(instanceIdString.split("'")) + .map(InstanceName::from) + .collect(Collectors.toList()); + MutableOptional<String> globalServiceId = new MutableOptional<>(); // Deprecated: Set of prod, but belongs to instance + Optional<AthenzDomain> athenzDomain = stringAttribute("athenz-domain", instanceElement).map(AthenzDomain::from); + Optional<AthenzService> athenzService = stringAttribute("athenz-service", instanceElement).map(AthenzService::from); + List<Step> steps = new ArrayList<>(); + for (Element instanceChild : XML.getChildren(instanceElement)) + steps.addAll(readNonInstanceSteps(instanceChild, globalServiceId, instanceChild)); + + return new DeploymentInstancesSpec(instanceNames, + steps, + readUpgradePolicy(instanceElement), + readChangeBlockers(instanceElement), + globalServiceId.asOptional(), + athenzDomain, + athenzService, + readNotifications(instanceElement), + readEndpoints(instanceElement)); + } + + // Consume the give tag as 0-N steps. 0 if it is not a step, >1 if it contains multiple nested steps that should be flattened + private List<Step> readNonInstanceSteps(Element stepTag, MutableOptional<String> globalServiceId, Element parentTag) { + Optional<AthenzService> athenzService = stringAttribute(athenzServiceAttribute, stepTag) + .or(() -> stringAttribute(athenzServiceAttribute, parentTag)) + .map(AthenzService::from); + Optional<String> testerFlavor = stringAttribute(testerFlavorAttribute, stepTag) + .or(() -> stringAttribute(testerFlavorAttribute, parentTag)); + + if (prodTag.equals(stepTag.getTagName())) + globalServiceId.set(readGlobalServiceId(stepTag)); + else if (readGlobalServiceId(stepTag).isPresent()) + throw new IllegalArgumentException("Attribute 'global-service-id' is only valid on 'prod' tag."); + + switch (stepTag.getTagName()) { + case testTag: case stagingTag: + return List.of(new DeclaredZone(Environment.from(stepTag.getTagName()), Optional.empty(), false, athenzService, testerFlavor)); + 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()) + .collect(Collectors.toList()); + case delayTag: + return List.of(new Delay(Duration.ofSeconds(longAttribute("hours", stepTag) * 60 * 60 + + longAttribute("minutes", stepTag) * 60 + + longAttribute("seconds", stepTag)))); + case parallelTag: // regions and instances may be nested within + return List.of(new ParallelZones(XML.getChildren(stepTag).stream() + .flatMap(child -> readNonInstanceSteps(child, globalServiceId, stepTag).stream()) + .collect(Collectors.toList()))); + case regionTag: + return List.of(readDeclaredZone(Environment.prod, athenzService, testerFlavor, stepTag)); + default: + return List.of(); } - Optional<AthenzDomain> athenzDomain = stringAttribute("athenz-domain", root).map(AthenzDomain::from); - Optional<AthenzService> athenzService = stringAttribute("athenz-service", root).map(AthenzService::from); - return new DeploymentSpec(globalServiceId, - readUpgradePolicy(root), - optionalIntegerAttribute(majorVersionTag, root), - readChangeBlockers(root), - steps, - xmlForm, - athenzDomain, - athenzService, - readNotifications(root), - readEndpoints(root)); + } + + private boolean hasChildTag(String childTagName, Element parent) { + return XML.getChildren(parent).stream().anyMatch(child -> child.getTagName().equals(childTagName)); } private Notifications readNotifications(Element root) { @@ -159,15 +205,15 @@ public class DeploymentSpecXmlReader { } private List<Endpoint> readEndpoints(Element root) { - final var endpointsElement = XML.getChild(root, endpointsTag); + var endpointsElement = XML.getChild(root, endpointsTag); if (endpointsElement == null) { return Collections.emptyList(); } - final var endpoints = new LinkedHashMap<String, Endpoint>(); + var endpoints = new LinkedHashMap<String, Endpoint>(); for (var endpointElement : XML.getChildren(endpointsElement, endpointTag)) { - final Optional<String> rotationId = stringAttribute("id", endpointElement); - final Optional<String> containerId = stringAttribute("container-id", endpointElement); - final var regions = new HashSet<String>(); + Optional<String> rotationId = stringAttribute("id", endpointElement); + Optional<String> containerId = stringAttribute("container-id", endpointElement); + var regions = new HashSet<String>(); if (containerId.isEmpty()) { throw new IllegalArgumentException("Missing 'container-id' from 'endpoint' tag."); @@ -255,10 +301,6 @@ public class DeploymentSpecXmlReader { return Optional.ofNullable(value).filter(s -> !s.equals("")); } - private boolean isEnvironmentName(String tagName) { - return tagName.equals(testTag) || tagName.equals(stagingTag) || tagName.equals(prodTag); - } - 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())), @@ -324,4 +366,14 @@ public class DeploymentSpecXmlReader { "to control whether the region should receive production traffic"); } + 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 694917e1bff..c99a3be7303 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 @@ -32,9 +32,9 @@ public class DeploymentSpecTest { @Test public void testSpec() { String specXml = "<deployment version='1.0'>" + - " <instances name='default'>" + + " <instance id='default'>" + " <test/>" + - " </instances>" + + " </instance>" + "</deployment>"; StringReader r = new StringReader(specXml); @@ -53,9 +53,9 @@ public class DeploymentSpecTest { @Test public void testSpecPinningMajorVersion() { String specXml = "<deployment version='1.0' major-version='6'>" + - " <instances name='default'>" + + " <instance id='default'>" + " <test/>" + - " </instances>" + + " </instance>" + "</deployment>"; StringReader r = new StringReader(specXml); @@ -70,9 +70,9 @@ public class DeploymentSpecTest { public void stagingSpec() { StringReader r = new StringReader( "<deployment version='1.0'>" + - " <instances name='default'>" + + " <instance id='default'>" + " <staging/>" + - " </instances>" + + " </instance>" + "</deployment>" ); @@ -91,12 +91,12 @@ public class DeploymentSpecTest { public void minimalProductionSpec() { StringReader r = new StringReader( "<deployment version='1.0'>" + - " <instances name='default'>" + + " <instance id='default'>" + " <prod>" + " <region active='false'>us-east1</region>" + " <region active='true'>us-west1</region>" + " </prod>" + - " </instances>" + + " </instance>" + "</deployment>" ); @@ -128,7 +128,7 @@ public class DeploymentSpecTest { public void maximalProductionSpec() { StringReader r = new StringReader( "<deployment version='1.0'>" + - " <instances name='default'>" + + " <instance id='default'>" + " <test/>" + " <staging/>" + " <prod>" + @@ -136,7 +136,7 @@ public class DeploymentSpecTest { " <delay hours='3' minutes='30'/>" + " <region active='true'>us-west1</region>" + " </prod>" + - " </instances>" + + " </instance>" + "</deployment>" ); @@ -170,12 +170,12 @@ public class DeploymentSpecTest { public void productionSpecWithGlobalServiceId() { StringReader r = new StringReader( "<deployment version='1.0'>" + - " <instances name='default'>" + + " <instance id='default'>" + " <prod global-service-id='query'>" + " <region active='true'>us-east-1</region>" + " <region active='true'>us-west-1</region>" + " </prod>" + - " </instances>" + + " </instance>" + "</deployment>" ); @@ -187,9 +187,9 @@ public class DeploymentSpecTest { public void globalServiceIdInTest() { StringReader r = new StringReader( "<deployment version='1.0'>" + - " <instances name='default'>" + + " <instance id='default'>" + " <test global-service-id='query' />" + - " </instances>" + + " </instance>" + "</deployment>" ); DeploymentSpec spec = DeploymentSpec.fromXml(r); @@ -199,9 +199,9 @@ public class DeploymentSpecTest { public void globalServiceIdInStaging() { StringReader r = new StringReader( "<deployment version='1.0'>" + - " <instances name='default'>" + + " <instance id='default'>" + " <staging global-service-id='query' />" + - " </instances>" + + " </instance>" + "</deployment>" ); DeploymentSpec spec = DeploymentSpec.fromXml(r); @@ -211,7 +211,7 @@ public class DeploymentSpecTest { public void productionSpecWithGlobalServiceIdBeforeStaging() { StringReader r = new StringReader( "<deployment>" + - " <instances name='default'>" + + " <instance id='default'>" + " <test/>" + " <prod global-service-id='qrs'>" + " <region active='true'>us-west-1</region>" + @@ -219,7 +219,7 @@ public class DeploymentSpecTest { " <region active='true'>us-east-3</region>" + " </prod>" + " <staging/>" + - " </instances>" + + " </instance>" + "</deployment>" ); @@ -231,14 +231,14 @@ public class DeploymentSpecTest { public void productionSpecWithUpgradePolicy() { StringReader r = new StringReader( "<deployment>" + - " <instances name='default'>" + + " <instance id='default'>" + " <upgrade policy='canary'/>" + " <prod>" + " <region active='true'>us-west-1</region>" + " <region active='true'>us-central-1</region>" + " <region active='true'>us-east-3</region>" + " </prod>" + - " </instances>" + + " </instance>" + "</deployment>" ); @@ -251,7 +251,7 @@ public class DeploymentSpecTest { try { StringReader r = new StringReader( "<deployment>" + - " <instances name='default'>" + + " <instance id='default'>" + " <upgrade policy='canary'/>" + " <prod>" + " <region active='true'>us-west-1</region>" + @@ -260,7 +260,7 @@ public class DeploymentSpecTest { " <delay minutes='59' seconds='61'/>" + " <region active='true'>us-east-3</region>" + " </prod>" + - " </instances>" + + " </instance>" + "</deployment>" ); DeploymentSpec.fromXml(r); @@ -284,7 +284,7 @@ public class DeploymentSpecTest { public void productionSpecWithParallelDeployments() { StringReader r = new StringReader( "<deployment>" + - " <instances name='default'>" + + " <instance id='default'>" + " <prod>" + " <region active='true'>us-west-1</region>" + " <parallel>" + @@ -292,7 +292,7 @@ public class DeploymentSpecTest { " <region active='true'>us-east-3</region>" + " </parallel>" + " </prod>" + - " </instances>" + + " </instance>" + "</deployment>" ); DeploymentSpec spec = DeploymentSpec.fromXml(r); @@ -306,7 +306,7 @@ public class DeploymentSpecTest { public void productionSpecWithDuplicateRegions() { StringReader r = new StringReader( "<deployment>" + - " <instances name='default'>" + + " <instance id='default'>" + " <prod>" + " <region active='true'>us-west-1</region>" + " <parallel>" + @@ -315,7 +315,7 @@ public class DeploymentSpecTest { " <region active='true'>us-east-3</region>" + " </parallel>" + " </prod>" + - " </instances>" + + " </instance>" + "</deployment>" ); try { @@ -330,13 +330,13 @@ public class DeploymentSpecTest { public void deploymentSpecWithIllegallyOrderedDeploymentSpec1() { StringReader r = new StringReader( "<deployment>" + - " <instances name='default'>" + + " <instance id='default'>" + " <block-change days='sat' hours='10' time-zone='CET'/>" + " <prod>" + " <region active='true'>us-west-1</region>" + " </prod>" + " <block-change days='mon,tue' hours='15-16'/>" + - " </instances>" + + " </instance>" + "</deployment>" ); DeploymentSpec spec = DeploymentSpec.fromXml(r); @@ -346,13 +346,13 @@ public class DeploymentSpecTest { public void deploymentSpecWithIllegallyOrderedDeploymentSpec2() { StringReader r = new StringReader( "<deployment>\n" + - " <instances name='default'>" + + " <instance id='default'>" + " <block-change days='sat' hours='10' time-zone='CET'/>" + " <test/>" + " <prod>" + " <region active='true'>us-west-1</region>" + " </prod>" + - " </instances>" + + " </instance>" + "</deployment>" ); DeploymentSpec spec = DeploymentSpec.fromXml(r); @@ -362,13 +362,13 @@ public class DeploymentSpecTest { public void deploymentSpecWithChangeBlocker() { StringReader r = new StringReader( "<deployment>" + - " <instances name='default'>" + + " <instance id='default'>" + " <block-change revision='false' days='mon,tue' hours='15-16'/>" + " <block-change days='sat' hours='10' time-zone='CET'/>" + " <prod>" + " <region active='true'>us-west-1</region>" + " </prod>" + - " </instances>" + + " </instance>" + "</deployment>" ); DeploymentSpec spec = DeploymentSpec.fromXml(r); @@ -395,11 +395,11 @@ public class DeploymentSpecTest { public void athenz_config_is_disallowed_on_deployment_if_instances() { StringReader r = new StringReader( "<deployment athenz-domain='domain' athenz-service='service''>" + - " <instances name='default'>" + + " <instance id='default'>" + " <prod>" + " <region active='true'>us-west-1</region>" + " </prod>" + - " </instances>" + + " </instance>" + "</deployment>" ); DeploymentSpec spec = DeploymentSpec.fromXml(r); @@ -408,12 +408,12 @@ public class DeploymentSpecTest { @Test public void athenz_config_is_read_from_instance() { StringReader r = new StringReader( - "<deployment'>" + - " <instances name='default' athenz-domain='domain' athenz-service='service'>" + + "<deployment>" + + " <instance id='default' athenz-domain='domain' athenz-service='service'>" + " <prod>" + " <region active='true'>us-west-1</region>" + " </prod>" + - " </instances>" + + " </instance>" + "</deployment>" ); DeploymentSpec spec = DeploymentSpec.fromXml(r); @@ -425,12 +425,12 @@ public class DeploymentSpecTest { public void athenz_service_is_overridden_from_environment() { StringReader r = new StringReader( "<deployment athenz-domain='domain' athenz-service='service'>" + - " <instances name='default' athenz-domain='domain' athenz-service='service'>" + + " <instance id='default' athenz-domain='domain' athenz-service='service'>" + " <test/>" + " <prod athenz-service='prod-service'>" + " <region active='true'>us-west-1</region>" + " </prod>" + - " </instances>" + + " </instance>" + "</deployment>" ); DeploymentSpec spec = DeploymentSpec.fromXml(r); @@ -442,11 +442,11 @@ public class DeploymentSpecTest { public void it_fails_when_athenz_service_is_not_defined() { StringReader r = new StringReader( "<deployment>" + - " <instances name='default' athenz-domain='domain'>" + + " <instance id='default' athenz-domain='domain'>" + " <prod>" + " <region active='true'>us-west-1</region>" + " </prod>" + - " </instances>" + + " </instance>" + "</deployment>" ); DeploymentSpec spec = DeploymentSpec.fromXml(r); @@ -456,11 +456,11 @@ public class DeploymentSpecTest { public void it_fails_when_athenz_service_is_configured_but_not_athenz_domain() { StringReader r = new StringReader( "<deployment>" + - " <instances name='default'>" + + " <instance id='default'>" + " <prod athenz-service='service'>" + " <region active='true'>us-west-1</region>" + " </prod>" + - " </instances>" + + " </instance>" + "</deployment>" ); DeploymentSpec spec = DeploymentSpec.fromXml(r); @@ -470,16 +470,16 @@ public class DeploymentSpecTest { public void noNotifications() { assertEquals(Notifications.none(), DeploymentSpec.fromXml("<deployment>" + - " <instances name='default'/>" + + " <instance id='default'/>" + "</deployment>").notifications()); } @Test public void emptyNotifications() { DeploymentSpec spec = DeploymentSpec.fromXml("<deployment>" + - " <instances name='default'>" + + " <instance id='default'>" + " <notifications/>" + - " </instances>" + + " </instance>" + "</deployment>"); assertEquals(Notifications.none(), spec.notifications()); @@ -488,13 +488,13 @@ public class DeploymentSpecTest { @Test public void someNotifications() { DeploymentSpec spec = DeploymentSpec.fromXml("<deployment>\n" + - " <instances name='default'>" + + " <instance id='default'>" + " <notifications when=\"failing\">" + " <email role=\"author\"/>" + " <email address=\"john@dev\" when=\"failing-commit\"/>" + " <email address=\"jane@dev\"/>" + " </notifications>" + - " </instances>" + + " </instance>" + "</deployment>"); assertEquals(ImmutableSet.of(author), spec.notifications().emailRolesFor(failing)); assertEquals(ImmutableSet.of(author), spec.notifications().emailRolesFor(failingCommit)); @@ -505,12 +505,12 @@ public class DeploymentSpecTest { @Test public void customTesterFlavor() { DeploymentSpec spec = DeploymentSpec.fromXml("<deployment>" + - " <instances name='default'>" + + " <instance id='default'>" + " <test tester-flavor=\"d-1-4-20\" />" + " <prod tester-flavor=\"d-2-8-50\">" + " <region active=\"false\">us-north-7</region>" + " </prod>" + - " </instances>" + + " </instance>" + "</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()); @@ -521,16 +521,16 @@ public class DeploymentSpecTest { public void noEndpoints() { assertEquals(Collections.emptyList(), DeploymentSpec.fromXml("<deployment>" + - " <instances name='default'/>" + + " <instance id='default'/>" + "</deployment>").endpoints()); } @Test public void emptyEndpoints() { var spec = DeploymentSpec.fromXml("<deployment>" + - " <instances name='default'>" + + " <instance id='default'>" + " <endpoints/>" + - " </instances>" + + " </instance>" + "</deployment>"); assertEquals(Collections.emptyList(), spec.endpoints()); } @@ -539,7 +539,7 @@ public class DeploymentSpecTest { public void someEndpoints() { var spec = DeploymentSpec.fromXml("" + "<deployment>" + - " <instances name='default'>" + + " <instance id='default'>" + " <prod>" + " <region active=\"true\">us-east</region>" + " </prod>" + @@ -550,7 +550,7 @@ public class DeploymentSpecTest { " <endpoint id=\"nalle\" container-id=\"frosk\" />" + " <endpoint container-id=\"quux\" />" + " </endpoints>" + - " </instances>" + + " </instance>" + "</deployment>"); assertEquals( @@ -593,7 +593,7 @@ public class DeploymentSpecTest { public void endpointDefaultRegions() { var spec = DeploymentSpec.fromXml("" + "<deployment>" + - " <instances name='default'>" + + " <instance id='default'>" + " <prod>" + " <region active=\"true\">us-east</region>" + " <region active=\"true\">us-west</region>" + @@ -605,7 +605,7 @@ public class DeploymentSpecTest { " <endpoint id=\"nalle\" container-id=\"frosk\" />" + " <endpoint container-id=\"quux\" />" + " </endpoints>" + - " </instances>" + + " </instance>" + "</deployment>"); assertEquals(Set.of("us-east"), endpointRegions("foo", spec)); @@ -630,14 +630,14 @@ public class DeploymentSpecTest { private static List<String> endpointIds(String endpointTag) { var xml = "<deployment>" + - " <instances name='default'>" + + " <instance id='default'>" + " <prod>" + " <region active=\"true\">us-east</region>" + " </prod>" + " <endpoints>" + endpointTag + " </endpoints>" + - " </instances>" + + " </instance>" + "</deployment>"; return DeploymentSpec.fromXml(xml).endpoints().stream() |