From 9585c6091fbdc7cef1a95e3d05b42e155ab1c2c3 Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Mon, 9 Oct 2017 12:56:46 +0200 Subject: block-upgrade -> block-change to support blocking revisions --- .../config/application/api/DeploymentSpec.java | 72 ++++++++++++++++------ .../config/application/api/DeploymentSpecTest.java | 42 ++++++++++++- .../src/main/resources/schema/deployment.rnc | 9 ++- .../src/test/schema-test-files/deployment.xml | 4 +- 4 files changed, 102 insertions(+), 25 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 e75ba5871f7..b2a908077e5 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 @@ -48,28 +48,28 @@ public class DeploymentSpec { private final Optional globalServiceId; private final UpgradePolicy upgradePolicy; - private final List blockUpgrades; + private final List changeBlockers; private final List steps; private final String xmlForm; public DeploymentSpec(Optional globalServiceId, UpgradePolicy upgradePolicy, - List blockUpgrades, List steps) { - this(globalServiceId, upgradePolicy, blockUpgrades, steps, null); + List changeBlockers, List steps) { + this(globalServiceId, upgradePolicy, changeBlockers, steps, null); } private DeploymentSpec(Optional globalServiceId, UpgradePolicy upgradePolicy, - List blockUpgrades, List steps, String xmlForm) { + List changeBlockers, List steps, String xmlForm) { validateTotalDelay(steps); this.globalServiceId = globalServiceId; this.upgradePolicy = upgradePolicy; - this.blockUpgrades = blockUpgrades; + this.changeBlockers = changeBlockers; this.steps = ImmutableList.copyOf(completeSteps(new ArrayList<>(steps))); this.xmlForm = xmlForm; validateZones(this.steps); } /** Throw an IllegalArgumentException if the total delay exceeds 24 hours */ - private static void validateTotalDelay(List steps) { + private void validateTotalDelay(List steps) { long totalDelaySeconds = steps.stream().filter(step -> step instanceof Delay) .mapToLong(delay -> ((Delay)delay).duration().getSeconds()) .sum(); @@ -123,7 +123,6 @@ public class DeploymentSpec { /** * Removes the first occurrence of a deployment step to the given environment and returns it. * - * @param environment * @return the removed step, or null if it is not present */ private static DeclaredZone remove(Environment environment, List steps) { @@ -144,11 +143,18 @@ public class DeploymentSpec { /** Returns whether upgrade can occur at the given instant */ public boolean canUpgradeAt(Instant instant) { - return blockUpgrades.stream().noneMatch(timeWindow -> timeWindow.includes(instant)); + return changeBlockers.stream().filter(block -> block.blocksVersions()) + .noneMatch(block -> block.window().includes(instant)); } - /** Returns time windows where upgrade is disallowed */ - public List blockUpgrades() { return blockUpgrades; } + /** Returns whether an application revision change can occur at the given instant */ + public boolean canChangeRevisionAt(Instant instant) { + return changeBlockers.stream().filter(block -> block.blocksRevisions()) + .noneMatch(block -> block.window().includes(instant)); + } + + /** Returns time windows where upgrades are disallowed */ + public List changeBlocker() { return changeBlockers; } /** Returns the deployment steps of this in the order they will be performed */ public List steps() { return steps; } @@ -223,8 +229,7 @@ public class DeploymentSpec { else if (readGlobalServiceId(environmentTag).isPresent()) throw new IllegalArgumentException("Attribute 'global-service-id' is only valid on 'prod' tag."); } - return new DeploymentSpec(globalServiceId, readUpgradePolicy(root), readBlockUpgradeWindows(root), steps, - xmlForm); + return new DeploymentSpec(globalServiceId, readUpgradePolicy(root), readChangeBlockers(root), steps, xmlForm); } /** Returns the given attribute as an integer, or 0 if it is not present */ @@ -259,21 +264,31 @@ public class DeploymentSpec { } } - private static List readBlockUpgradeWindows(Element root) { - List timeWindows = new ArrayList<>(); + private static List readChangeBlockers(Element root) { + List changeBlockers = new ArrayList<>(); for (Element tag : XML.getChildren(root)) { - if (!"block-upgrade".equals(tag.getTagName())) { - continue; - } + // TODO: Remove block-upgrade on Vespa 7 + if ( ! "block-change".equals(tag.getTagName()) && !"block-upgrade".equals(tag.getTagName())) continue; + + boolean blockVersions = trueOrMissing(tag.getAttribute("versions")); + boolean blockRevisions = trueOrMissing(tag.getAttribute("revisions")) + && !tag.getTagName().equals("block-upgrade"); // TODO: Remove condition on Vespa 7 + String daySpec = tag.getAttribute("days"); String hourSpec = tag.getAttribute("hours"); String zoneSpec = tag.getAttribute("time-zone"); if (zoneSpec.isEmpty()) { // Default to UTC time zone zoneSpec = "UTC"; } - timeWindows.add(TimeWindow.from(daySpec, hourSpec, zoneSpec)); + changeBlockers.add(new ChangeBlocker(blockRevisions, blockVersions, + TimeWindow.from(daySpec, hourSpec, zoneSpec))); } - return Collections.unmodifiableList(timeWindows); + return Collections.unmodifiableList(changeBlockers); + } + + /** Returns true if the given value is "true", or if it is missing */ + private static boolean trueOrMissing(String value) { + return value == null || value.isEmpty() || value.equals("true"); } private static UpgradePolicy readUpgradePolicy(Element root) { @@ -474,4 +489,23 @@ public class DeploymentSpec { conservative } + /** A blocking of changes in a given time window */ + public static class ChangeBlocker { + + private final boolean revision; + private final boolean version; + private final TimeWindow window; + + private ChangeBlocker(boolean revision, boolean version, TimeWindow window) { + this.revision = revision; + this.version = version; + this.window = window; + } + + public boolean blocksRevisions() { return revision; } + public boolean blocksVersions() { return version; } + public TimeWindow window() { return window; } + + } + } 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 3540a5d785f..c2b68b54655 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 @@ -290,9 +290,45 @@ public class DeploymentSpecTest { "" ); DeploymentSpec spec = DeploymentSpec.fromXml(r); - assertEquals(2, spec.blockUpgrades().size()); - assertEquals(ZoneId.of("UTC"), spec.blockUpgrades().get(0).zone()); - assertEquals(ZoneId.of("CET"), spec.blockUpgrades().get(1).zone()); + assertEquals(2, spec.changeBlocker().size()); + assertTrue(spec.changeBlocker().get(0).blocksVersions()); + assertFalse(spec.changeBlocker().get(0).blocksRevisions()); + assertEquals(ZoneId.of("UTC"), spec.changeBlocker().get(0).window().zone()); + + assertTrue(spec.changeBlocker().get(1).blocksVersions()); + assertFalse(spec.changeBlocker().get(1).blocksRevisions()); + assertEquals(ZoneId.of("CET"), spec.changeBlocker().get(1).window().zone()); + + assertTrue(spec.canUpgradeAt(Instant.parse("2017-09-18T14:15:30.00Z"))); + assertFalse(spec.canUpgradeAt(Instant.parse("2017-09-18T15:15:30.00Z"))); + assertFalse(spec.canUpgradeAt(Instant.parse("2017-09-18T16:15:30.00Z"))); + assertTrue(spec.canUpgradeAt(Instant.parse("2017-09-18T17:15:30.00Z"))); + + assertTrue(spec.canUpgradeAt(Instant.parse("2017-09-23T09:15:30.00Z"))); + assertFalse(spec.canUpgradeAt(Instant.parse("2017-09-23T08:15:30.00Z"))); // 10 in CET + assertTrue(spec.canUpgradeAt(Instant.parse("2017-09-23T10:15:30.00Z"))); + } + + @Test + public void deploymentSpecWithChangeBlocker() { + StringReader r = new StringReader( + "\n" + + " \n" + + " \n" + + " \n" + + " us-west-1\n" + + " \n" + + "" + ); + DeploymentSpec spec = DeploymentSpec.fromXml(r); + assertEquals(2, spec.changeBlocker().size()); + assertTrue(spec.changeBlocker().get(0).blocksVersions()); + assertFalse(spec.changeBlocker().get(0).blocksRevisions()); + assertEquals(ZoneId.of("UTC"), spec.changeBlocker().get(0).window().zone()); + + assertTrue(spec.changeBlocker().get(1).blocksVersions()); + assertTrue(spec.changeBlocker().get(1).blocksRevisions()); + assertEquals(ZoneId.of("CET"), spec.changeBlocker().get(1).window().zone()); assertTrue(spec.canUpgradeAt(Instant.parse("2017-09-18T14:15:30.00Z"))); assertFalse(spec.canUpgradeAt(Instant.parse("2017-09-18T15:15:30.00Z"))); diff --git a/config-model/src/main/resources/schema/deployment.rnc b/config-model/src/main/resources/schema/deployment.rnc index 31212b7f69d..16cc3acfafa 100644 --- a/config-model/src/main/resources/schema/deployment.rnc +++ b/config-model/src/main/resources/schema/deployment.rnc @@ -5,6 +5,7 @@ start = element deployment { attribute version { "1.0" } & Upgrade? & + BlockChange* & BlockUpgrade* & Test? & Staging? & @@ -15,12 +16,18 @@ Upgrade = element upgrade { attribute policy { xsd:string } } -BlockUpgrade = element block-upgrade { +BlockChange = element block-change { + attribute revisions { xsd:boolean }? & + attribute versions { xsd:boolean }? & attribute days { xsd:string } & attribute hours { xsd:string } & attribute time-zone { xsd:string }? } +BlockUpgrade = element block-upgrade { # Legacy name - remove on Vespa 7 + BlockChange +} + Test = element test { text } diff --git a/config-model/src/test/schema-test-files/deployment.xml b/config-model/src/test/schema-test-files/deployment.xml index 6f16817e627..2fd6d7c3ec8 100644 --- a/config-model/src/test/schema-test-files/deployment.xml +++ b/config-model/src/test/schema-test-files/deployment.xml @@ -1,8 +1,8 @@ - - + + -- cgit v1.2.3