From 3f1a4bbdc43c97fb93ead93725bc2619862f2e13 Mon Sep 17 00:00:00 2001 From: Martin Polden Date: Tue, 7 Feb 2017 14:29:17 +0100 Subject: Add retired field to Flavor config --- .../java/com/yahoo/config/provision/Flavor.java | 14 +++- .../com/yahoo/config/provision/NodeFlavors.java | 22 ++++-- .../main/resources/configdefinitions/flavors.def | 3 + .../yahoo/config/provision/NodeFlavorsTest.java | 80 ++++++++++++++++++++++ 4 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 config-provisioning/src/test/java/com/yahoo/config/provision/NodeFlavorsTest.java (limited to 'config-provisioning') diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java b/config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java index 835325bc0f8..fff1d53b50e 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/Flavor.java @@ -23,6 +23,7 @@ public class Flavor { private final double minMainMemoryAvailableGb; private final double minDiskAvailableGb; private final String description; + private final boolean retired; private List replacesFlavors; /** @@ -39,6 +40,7 @@ public class Flavor { this.minMainMemoryAvailableGb = flavorConfig.minMainMemoryAvailableGb(); this.minDiskAvailableGb = flavorConfig.minDiskAvailableGb(); this.description = flavorConfig.description(); + this.retired = flavorConfig.retired(); } /** Returns the unique identity of this flavor */ @@ -62,6 +64,11 @@ public class Flavor { public String getDescription() { return description; } + /** Returns whether the flavor is retired */ + public boolean isRetired() { + return retired; + } + public Type getType() { return type; } /** @@ -97,7 +104,12 @@ public class Flavor { * (by being the same), or by directly or indirectly replacing it */ public boolean satisfies(Flavor flavor) { - if (this.equals(flavor)) return true; + if (this.equals(flavor)) { + return true; + } + if (this.retired) { + return false; + } for (Flavor replaces : replacesFlavors) if (replaces.satisfies(flavor)) return true; diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java index 9451e47cbc3..ae2e5710d2e 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/NodeFlavors.java @@ -3,6 +3,7 @@ package com.yahoo.config.provision; import com.google.common.collect.ImmutableMap; import com.google.inject.Inject; +import com.yahoo.config.provisioning.FlavorsConfig; import java.util.Collection; import java.util.HashMap; @@ -11,8 +12,6 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; -import com.yahoo.config.provisioning.FlavorsConfig; - /** * All the available node flavors. * @@ -38,9 +37,8 @@ public class NodeFlavors { /** Returns the flavor with the given name or throws an IllegalArgumentException if it does not exist */ public Flavor getFlavorOrThrow(String flavorName) { - Optional flavor = getFlavor(flavorName); - if ( flavor.isPresent()) return flavor.get(); - throw new IllegalArgumentException("Unknown flavor '" + flavorName + "'. Flavors are " + canonicalFlavorNames()); + return getFlavor(flavorName).orElseThrow(() -> new IllegalArgumentException("Unknown flavor '" + flavorName + + "'. Flavors are " + canonicalFlavorNames())); } private List canonicalFlavorNames() { @@ -65,7 +63,21 @@ public class NodeFlavors { } flavor.freeze(); } + // Third pass, ensure that retired flavors have a replacement + for (Flavor flavor : flavors.values()) { + if (flavor.isRetired() && !hasReplacement(flavors.values(), flavor)) { + throw new IllegalStateException( + String.format("Flavor '%s' is retired, but has no replacement", flavor.name()) + ); + } + } return flavors.values(); } + private static boolean hasReplacement(Collection flavors, Flavor flavor) { + return flavors.stream() + .filter(f -> !f.equals(flavor)) + .anyMatch(f -> f.satisfies(flavor)); + } + } diff --git a/config-provisioning/src/main/resources/configdefinitions/flavors.def b/config-provisioning/src/main/resources/configdefinitions/flavors.def index 26006c25df8..edcd957c0b2 100644 --- a/config-provisioning/src/main/resources/configdefinitions/flavors.def +++ b/config-provisioning/src/main/resources/configdefinitions/flavors.def @@ -39,3 +39,6 @@ flavor[].minDiskAvailableGb double default=0.0 # Human readable free text for description of node. flavor[].description string default="" + +# The flavor is retired and should no longer be used. +flavor[].retired bool default=false diff --git a/config-provisioning/src/test/java/com/yahoo/config/provision/NodeFlavorsTest.java b/config-provisioning/src/test/java/com/yahoo/config/provision/NodeFlavorsTest.java new file mode 100644 index 00000000000..3b55c05c7c6 --- /dev/null +++ b/config-provisioning/src/test/java/com/yahoo/config/provision/NodeFlavorsTest.java @@ -0,0 +1,80 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.provision; + +import com.yahoo.config.provisioning.FlavorsConfig; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + + +public class NodeFlavorsTest { + + @Rule + public final ExpectedException exception = ExpectedException.none(); + + @Test + public void testReplacesWithBadValue() { + FlavorsConfig.Builder builder = new FlavorsConfig.Builder(); + List flavorBuilderList = new ArrayList<>(); + FlavorsConfig.Flavor.Builder flavorBuilder = new FlavorsConfig.Flavor.Builder(); + FlavorsConfig.Flavor.Replaces.Builder flavorReplacesBuilder = new FlavorsConfig.Flavor.Replaces.Builder(); + flavorReplacesBuilder.name("non-existing-config"); + flavorBuilder.name("strawberry").cost(2).replaces.add(flavorReplacesBuilder); + flavorBuilderList.add(flavorBuilder); + builder.flavor(flavorBuilderList); + FlavorsConfig config = new FlavorsConfig(builder); + exception.expect(IllegalStateException.class); + exception.expectMessage("Replaces for strawberry pointing to a non existing flavor: non-existing-config"); + new NodeFlavors(config); + } + + @Test + public void testConfigParsing() { + FlavorsConfig.Builder builder = new FlavorsConfig.Builder(); + List flavorBuilderList = new ArrayList<>(); + { + FlavorsConfig.Flavor.Builder flavorBuilder = new FlavorsConfig.Flavor.Builder(); + FlavorsConfig.Flavor.Replaces.Builder flavorReplacesBuilder = new FlavorsConfig.Flavor.Replaces.Builder(); + flavorReplacesBuilder.name("banana"); + flavorBuilder.name("strawberry").cost(2).replaces.add(flavorReplacesBuilder); + flavorBuilderList.add(flavorBuilder); + } + { + FlavorsConfig.Flavor.Builder flavorBuilder = new FlavorsConfig.Flavor.Builder(); + flavorBuilder.name("banana").cost(3); + flavorBuilderList.add(flavorBuilder); + } + builder.flavor(flavorBuilderList); + FlavorsConfig config = new FlavorsConfig(builder); + NodeFlavors nodeFlavors = new NodeFlavors(config); + assertThat(nodeFlavors.getFlavor("banana").get().cost(), is(3)); + } + + @Test + public void testRetiredFlavorWithoutReplacement() { + FlavorsConfig.Builder builder = new FlavorsConfig.Builder(); + List flavorBuilderList = new ArrayList<>(); + { + FlavorsConfig.Flavor.Builder flavorBuilder = new FlavorsConfig.Flavor.Builder(); + flavorBuilder.name("retired").retired(true); + flavorBuilderList.add(flavorBuilder); + } + { + FlavorsConfig.Flavor.Builder flavorBuilder = new FlavorsConfig.Flavor.Builder(); + flavorBuilder.name("chocolate"); + flavorBuilderList.add(flavorBuilder); + } + builder.flavor(flavorBuilderList); + FlavorsConfig config = new FlavorsConfig(builder); + exception.expect(IllegalStateException.class); + exception.expectMessage("Flavor 'retired' is retired, but has no replacement"); + new NodeFlavors(config); + } + +} -- cgit v1.2.3