diff options
author | Martin Polden <mpolden@mpolden.no> | 2019-02-04 09:31:18 +0100 |
---|---|---|
committer | Martin Polden <mpolden@mpolden.no> | 2019-02-04 09:33:19 +0100 |
commit | 4cabf81b4078dc5974d37270f1170a92e5ed37b8 (patch) | |
tree | aacb1e26fa47c3b9d64daa3535d2e194fbbe5214 /config-provisioning | |
parent | 143348b1f6fe210ec8a3a0d22eb07cac56a07825 (diff) |
Add rotations to cluster spec
Diffstat (limited to 'config-provisioning')
4 files changed, 129 insertions, 21 deletions
diff --git a/config-provisioning/abi-spec.json b/config-provisioning/abi-spec.json index 3e55f74d3b2..3c5dd0fa834 100644 --- a/config-provisioning/abi-spec.json +++ b/config-provisioning/abi-spec.json @@ -235,10 +235,13 @@ "public com.yahoo.component.Version vespaVersion()", "public java.util.Optional group()", "public boolean isExclusive()", + "public java.util.Set rotations()", "public com.yahoo.config.provision.ClusterSpec with(java.util.Optional)", "public com.yahoo.config.provision.ClusterSpec exclusive(boolean)", "public static com.yahoo.config.provision.ClusterSpec request(com.yahoo.config.provision.ClusterSpec$Type, com.yahoo.config.provision.ClusterSpec$Id, com.yahoo.component.Version, boolean)", + "public static com.yahoo.config.provision.ClusterSpec request(com.yahoo.config.provision.ClusterSpec$Type, com.yahoo.config.provision.ClusterSpec$Id, com.yahoo.component.Version, boolean, java.util.Set)", "public static com.yahoo.config.provision.ClusterSpec from(com.yahoo.config.provision.ClusterSpec$Type, com.yahoo.config.provision.ClusterSpec$Id, com.yahoo.config.provision.ClusterSpec$Group, com.yahoo.component.Version, boolean)", + "public static com.yahoo.config.provision.ClusterSpec from(com.yahoo.config.provision.ClusterSpec$Type, com.yahoo.config.provision.ClusterSpec$Id, com.yahoo.config.provision.ClusterSpec$Group, com.yahoo.component.Version, boolean, java.util.Set)", "public java.lang.String toString()", "public int hashCode()", "public boolean equals(java.lang.Object)", @@ -598,6 +601,25 @@ ], "fields": [] }, + "com.yahoo.config.provision.RotationName": { + "superClass": "java.lang.Object", + "interfaces": [ + "java.lang.Comparable" + ], + "attributes": [ + "public" + ], + "methods": [ + "public java.lang.String value()", + "public boolean equals(java.lang.Object)", + "public int hashCode()", + "public int compareTo(com.yahoo.config.provision.RotationName)", + "public java.lang.String toString()", + "public static com.yahoo.config.provision.RotationName from(java.lang.String)", + "public bridge synthetic int compareTo(java.lang.Object)" + ], + "fields": [] + }, "com.yahoo.config.provision.SystemName": { "superClass": "java.lang.Enum", "interfaces": [], diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java index 1253496cdd2..c0099878b45 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java @@ -3,9 +3,14 @@ package com.yahoo.config.provision; import com.yahoo.component.Version; +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; + /** * A node's membership in a cluster. This is a value object. - * The format is "clusterType/clusterId/groupId/index[/exclusive][/retired] + * The format is "clusterType/clusterId/groupId/index[/exclusive][/retired][/rotationId,...]" * * @author bratseth */ @@ -20,18 +25,26 @@ public class ClusterMembership { private ClusterMembership(String stringValue, Version vespaVersion) { String[] components = stringValue.split("/"); - if (components.length < 4 || components.length > 6) + if (components.length < 4 || components.length > 7) throw new RuntimeException("Could not parse '" + stringValue + "' to a cluster membership. " + - "Expected 'clusterType/clusterId/groupId/index[/retired][/exclusive]'"); + "Expected 'clusterType/clusterId/groupId/index[/retired][/exclusive][/rotationId,...]'"); boolean exclusive = false; + Set<RotationName> rotations = Collections.emptySet(); if (components.length > 4) { - exclusive = components[4].equals("exclusive"); - retired = components[components.length-1].equals("retired"); + for (int i = 4; i < components.length; i++) { + String component = components[i]; + switch (component) { + case "exclusive": exclusive = true; break; + case "retired": retired = true; break; + default: rotations = rotationsFrom(component); break; + } + } } this.cluster = ClusterSpec.from(ClusterSpec.Type.valueOf(components[0]), ClusterSpec.Id.from(components[1]), - ClusterSpec.Group.from(Integer.valueOf(components[2])), vespaVersion, exclusive); + ClusterSpec.Group.from(Integer.valueOf(components[2])), vespaVersion, exclusive, + rotations); this.index = Integer.parseInt(components[3]); this.stringValue = toStringValue(); } @@ -49,7 +62,8 @@ public class ClusterMembership { (cluster.group().isPresent() ? "/" + cluster.group().get().index() : "") + "/" + index + ( cluster.isExclusive() ? "/exclusive" : "") + - ( retired ? "/retired" : ""); + ( retired ? "/retired" : "") + + ( !cluster.rotations().isEmpty() ? "/" + rotationsAsString(cluster.rotations()) : ""); } @@ -107,4 +121,12 @@ public class ClusterMembership { return new ClusterMembership(cluster, index, true); } + private static Set<RotationName> rotationsFrom(String s) { + return Arrays.stream(s.split(",")).map(RotationName::from).collect(Collectors.toUnmodifiableSet()); + } + + private static String rotationsAsString(Set<RotationName> rotations) { + return rotations.stream().map(RotationName::value).collect(Collectors.joining(",")); + } + } diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java index 5c2d41f11be..bd03949191e 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java @@ -1,10 +1,13 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.config.provision; +import com.google.common.collect.ImmutableSortedSet; import com.yahoo.component.Version; +import java.util.Collections; import java.util.Objects; import java.util.Optional; +import java.util.Set; /** * A specification of a cluster - or group in a grouped cluster - to be run on a set of hosts. @@ -19,17 +22,21 @@ public final class ClusterSpec { /** The group id of these hosts, or empty if this is represents a request for hosts */ private final Optional<Group> groupId; - private final Version vespaVersion; - private boolean exclusive; + private final Set<RotationName> rotations; - private ClusterSpec(Type type, Id id, Optional<Group> groupId, Version vespaVersion, boolean exclusive) { + private ClusterSpec(Type type, Id id, Optional<Group> groupId, Version vespaVersion, boolean exclusive, + Set<RotationName> rotations) { + if (type != Type.container && !rotations.isEmpty()) { + throw new IllegalArgumentException("Rotations can only be declared for clusters of type " + Type.container); + } this.type = type; this.id = id; this.groupId = groupId; this.vespaVersion = vespaVersion; this.exclusive = exclusive; + this.rotations = ImmutableSortedSet.copyOf(rotations); } /** Returns the cluster type */ @@ -51,20 +58,35 @@ public final class ClusterSpec { */ public boolean isExclusive() { return exclusive; } + /** Returns the rotations of which this cluster should be a member */ + public Set<RotationName> rotations() { + return rotations; + } + public ClusterSpec with(Optional<Group> newGroup) { - return new ClusterSpec(type, id, newGroup, vespaVersion, exclusive); + return new ClusterSpec(type, id, newGroup, vespaVersion, exclusive, rotations); } public ClusterSpec exclusive(boolean exclusive) { - return new ClusterSpec(type, id, groupId, vespaVersion, exclusive); + return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, rotations); } + // TODO: Remove when versions <= 7.6 are gone public static ClusterSpec request(Type type, Id id, Version vespaVersion, boolean exclusive) { - return new ClusterSpec(type, id, Optional.empty(), vespaVersion, exclusive); + return new ClusterSpec(type, id, Optional.empty(), vespaVersion, exclusive, Collections.emptySet()); } + public static ClusterSpec request(Type type, Id id, Version vespaVersion, boolean exclusive, Set<RotationName> rotations) { + return new ClusterSpec(type, id, Optional.empty(), vespaVersion, exclusive, rotations); + } + + // TODO: Remove when versions <= 7.6 are gone public static ClusterSpec from(Type type, Id id, Group groupId, Version vespaVersion, boolean exclusive) { - return new ClusterSpec(type, id, Optional.of(groupId), vespaVersion, exclusive); + return from(type, id, groupId, vespaVersion, exclusive, Collections.emptySet()); + } + + public static ClusterSpec from(Type type, Id id, Group groupId, Version vespaVersion, boolean exclusive, Set<RotationName> rotations) { + return new ClusterSpec(type, id, Optional.of(groupId), vespaVersion, exclusive, rotations); } @Override diff --git a/config-provisioning/src/test/java/com/yahoo/config/provision/ClusterMembershipTest.java b/config-provisioning/src/test/java/com/yahoo/config/provision/ClusterMembershipTest.java index f1970326137..9bd0680b691 100644 --- a/config-provisioning/src/test/java/com/yahoo/config/provision/ClusterMembershipTest.java +++ b/config-provisioning/src/test/java/com/yahoo/config/provision/ClusterMembershipTest.java @@ -1,11 +1,12 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.config.provision; -import com.yahoo.component.*; import com.yahoo.component.Version; +import com.yahoo.component.Vtag; import org.junit.Test; -import java.util.Optional; +import java.util.Collections; +import java.util.Set; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -18,20 +19,56 @@ public class ClusterMembershipTest { @Test public void testContainerServiceInstance() { - ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("id1"), Version.fromString("6.42"), false); + ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("id1"), Version.fromString("6.42"), false, Collections.emptySet()); assertContainerService(ClusterMembership.from(cluster, 3)); } @Test + public void testContainerInstanceWithOptionalParts() { + { + ClusterMembership instance = ClusterMembership.from("container/id1/4/37/exclusive/retired", Vtag.currentVersion); + assertTrue(instance.retired()); + assertTrue(instance.cluster().isExclusive()); + } + + { + ClusterMembership instance = ClusterMembership.from("container/id1/4/37/exclusive", Vtag.currentVersion); + assertFalse(instance.retired()); + assertTrue(instance.cluster().isExclusive()); + } + + { + ClusterMembership instance = ClusterMembership.from("container/id1/4/37/rotation1,rotation2", Vtag.currentVersion); + assertFalse(instance.retired()); + assertFalse(instance.cluster().isExclusive()); + assertEquals(Set.of(RotationName.from("rotation1"), RotationName.from("rotation2")), instance.cluster().rotations()); + } + + { + ClusterMembership instance = ClusterMembership.from("container/id1/4/37/exclusive/rotation1,rotation2", Vtag.currentVersion); + assertFalse(instance.retired()); + assertTrue(instance.cluster().isExclusive()); + assertEquals(Set.of(RotationName.from("rotation1"), RotationName.from("rotation2")), instance.cluster().rotations()); + } + + { + ClusterMembership instance = ClusterMembership.from("container/id1/4/37/exclusive/retired/rotation1,rotation2", Vtag.currentVersion); + assertTrue(instance.retired()); + assertTrue(instance.cluster().isExclusive()); + assertEquals(Set.of(RotationName.from("rotation1"), RotationName.from("rotation2")), instance.cluster().rotations()); + } + } + + @Test public void testServiceInstance() { - ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("id1"), Version.fromString("6.42"), false); + ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("id1"), Version.fromString("6.42"), false, Collections.emptySet()); assertContentService(ClusterMembership.from(cluster, 37)); } @Test public void testServiceInstanceWithGroup() { ClusterSpec cluster = ClusterSpec.from(ClusterSpec.Type.content, ClusterSpec.Id.from("id1"), - ClusterSpec.Group.from(4), Version.fromString("6.42"), false); + ClusterSpec.Group.from(4), Version.fromString("6.42"), false, Collections.emptySet()); assertContentServiceWithGroup(ClusterMembership.from(cluster, 37)); } @@ -42,14 +79,14 @@ public class ClusterMembershipTest { @Test public void testServiceInstanceWithRetire() { - ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("id1"), Version.fromString("6.42"), false); + ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("id1"), Version.fromString("6.42"), false, Collections.emptySet()); assertContentServiceWithRetire(ClusterMembership.retiredFrom(cluster, 37)); } @Test public void testServiceInstanceWithGroupAndRetire() { ClusterSpec cluster = ClusterSpec.from(ClusterSpec.Type.content, ClusterSpec.Id.from("id1"), - ClusterSpec.Group.from(4), Version.fromString("6.42"), false); + ClusterSpec.Group.from(4), Version.fromString("6.42"), false, Collections.emptySet()); assertContentServiceWithGroupAndRetire(ClusterMembership.retiredFrom(cluster, 37)); } @@ -64,6 +101,7 @@ public class ClusterMembershipTest { assertFalse(instance.cluster().group().isPresent()); assertEquals(3, instance.index()); assertEquals("container/id1/3", instance.stringValue()); + assertTrue(instance.cluster().rotations().isEmpty()); } private void assertContentService(ClusterMembership instance) { @@ -73,6 +111,7 @@ public class ClusterMembershipTest { assertEquals(37, instance.index()); assertFalse(instance.retired()); assertEquals("content/id1/37", instance.stringValue()); + assertTrue(instance.cluster().rotations().isEmpty()); } private void assertContentServiceWithGroup(ClusterMembership instance) { @@ -82,6 +121,7 @@ public class ClusterMembershipTest { assertEquals(37, instance.index()); assertFalse(instance.retired()); assertEquals("content/id1/4/37", instance.stringValue()); + assertTrue(instance.cluster().rotations().isEmpty()); } /** Serializing a spec without a group assigned works, but not deserialization */ @@ -91,6 +131,7 @@ public class ClusterMembershipTest { assertEquals(37, instance.index()); assertTrue(instance.retired()); assertEquals("content/id1/37/retired", instance.stringValue()); + assertTrue(instance.cluster().rotations().isEmpty()); } private void assertContentServiceWithGroupAndRetire(ClusterMembership instance) { @@ -100,6 +141,7 @@ public class ClusterMembershipTest { assertEquals(37, instance.index()); assertTrue(instance.retired()); assertEquals("content/id1/4/37/retired", instance.stringValue()); + assertTrue(instance.cluster().rotations().isEmpty()); } } |