summaryrefslogtreecommitdiffstats
path: root/config-provisioning
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2020-11-27 11:43:33 +0100
committerMartin Polden <mpolden@mpolden.no>2020-11-27 14:06:43 +0100
commit3cf13b61b9ddc7b9e623415db006b4cbfc2ce8cd (patch)
treed17d6126d30a01abcc1abef4b8c3d340e1ced6c1 /config-provisioning
parente1cd9737955d4521b2b90f5fd341af2fe608b184 (diff)
Read stateful tag in ClusterMembership
Diffstat (limited to 'config-provisioning')
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java44
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java51
-rw-r--r--config-provisioning/src/test/java/com/yahoo/config/provision/ClusterMembershipTest.java24
3 files changed, 83 insertions, 36 deletions
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 45465fa91c4..9fb1689782f 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,11 +3,12 @@ package com.yahoo.config.provision;
import com.yahoo.component.Version;
+import java.util.Objects;
import java.util.Optional;
/**
* A node's membership in a cluster. This is a value object.
- * The format is "clusterType/clusterId/groupId/index[/exclusive][/retired][/combinedId]"
+ * The format is "clusterType/clusterId/groupId/index[/exclusive][/retired][/stateful][/combinedId]"
*
* @author bratseth
*/
@@ -24,9 +25,10 @@ public class ClusterMembership {
String[] components = stringValue.split("/");
if (components.length < 4)
throw new RuntimeException("Could not parse '" + stringValue + "' to a cluster membership. " +
- "Expected 'clusterType/clusterId/groupId/index[/retired][/exclusive][/combinedId]'");
+ "Expected 'clusterType/clusterId/groupId/index[/retired][/exclusive][/stateful][/combinedId]'");
boolean exclusive = false;
+ boolean stateful = false;
var combinedId = Optional.<String>empty();
if (components.length > 4) {
for (int i = 4; i < components.length; i++) {
@@ -34,18 +36,24 @@ public class ClusterMembership {
switch (component) {
case "exclusive": exclusive = true; break;
case "retired": retired = true; break;
+ case "stateful": stateful = true; break;
default: combinedId = Optional.of(component); break;
}
}
}
- this.cluster = ClusterSpec.specification(ClusterSpec.Type.valueOf(components[0]), ClusterSpec.Id.from(components[1]))
- .group(ClusterSpec.Group.from(Integer.parseInt(components[2])))
- .vespaVersion(vespaVersion)
- .exclusive(exclusive)
- .combinedId(combinedId.map(ClusterSpec.Id::from))
- .dockerImageRepository(dockerImageRepo)
- .build();
+ ClusterSpec.Type type = ClusterSpec.Type.valueOf(components[0]);
+ if (type.isContent()) {
+ stateful = true; // TODO(mpolden): Serialization compatibility. Remove after December 2020
+ }
+ this.cluster = ClusterSpec.specification(type, ClusterSpec.Id.from(components[1]))
+ .group(ClusterSpec.Group.from(Integer.parseInt(components[2])))
+ .vespaVersion(vespaVersion)
+ .exclusive(exclusive)
+ .combinedId(combinedId.map(ClusterSpec.Id::from))
+ .dockerImageRepository(dockerImageRepo)
+ .stateful(stateful)
+ .build();
this.index = Integer.parseInt(components[3]);
this.stringValue = toStringValue();
}
@@ -64,6 +72,8 @@ public class ClusterMembership {
"/" + index +
( cluster.isExclusive() ? "/exclusive" : "") +
( retired ? "/retired" : "") +
+ // TODO(mpolden): Write stateful tag once all nodes can read it
+ // ( cluster.isStateful() ? "/stateful" : "") +
( cluster.combinedId().isPresent() ? "/" + cluster.combinedId().get().value() : "");
}
@@ -98,13 +108,19 @@ public class ClusterMembership {
public String stringValue() { return stringValue; }
@Override
- public int hashCode() { return stringValue().hashCode(); }
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ClusterMembership that = (ClusterMembership) o;
+ return index == that.index &&
+ retired == that.retired &&
+ cluster.equals(that.cluster) &&
+ stringValue.equals(that.stringValue);
+ }
@Override
- public boolean equals(Object other) {
- if (other == this) return true;
- if ( ! (other instanceof ClusterMembership)) return false;
- return ((ClusterMembership)other).stringValue().equals(stringValue());
+ public int hashCode() {
+ return Objects.hash(cluster, index, retired, stringValue);
}
@Override
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 7d2a96ce991..e455c9d2ed4 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
@@ -23,9 +23,10 @@ public final class ClusterSpec {
private final boolean exclusive;
private final Optional<Id> combinedId;
private final Optional<DockerImage> dockerImageRepo;
+ private final boolean stateful;
private ClusterSpec(Type type, Id id, Optional<Group> groupId, Version vespaVersion, boolean exclusive,
- Optional<Id> combinedId, Optional<DockerImage> dockerImageRepo) {
+ Optional<Id> combinedId, Optional<DockerImage> dockerImageRepo, boolean stateful) {
this.type = type;
this.id = id;
this.groupId = groupId;
@@ -40,6 +41,10 @@ public final class ClusterSpec {
if (dockerImageRepo.isPresent() && dockerImageRepo.get().tag().isPresent())
throw new IllegalArgumentException("dockerImageRepo is not allowed to have a tag");
this.dockerImageRepo = dockerImageRepo;
+ if (type.isContent() && !stateful) {
+ throw new IllegalArgumentException("Cluster of type " + type + " must be stateful");
+ }
+ this.stateful = stateful;
}
/** Returns the cluster type */
@@ -71,12 +76,17 @@ public final class ClusterSpec {
*/
public boolean isExclusive() { return exclusive; }
+ /** Whether this cluster has state */
+ public boolean isStateful() {
+ return stateful;
+ }
+
public ClusterSpec with(Optional<Group> newGroup) {
- return new ClusterSpec(type, id, newGroup, vespaVersion, exclusive, combinedId, dockerImageRepo);
+ return new ClusterSpec(type, id, newGroup, vespaVersion, exclusive, combinedId, dockerImageRepo, stateful);
}
public ClusterSpec exclusive(boolean exclusive) {
- return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, combinedId, dockerImageRepo);
+ return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, combinedId, dockerImageRepo, stateful);
}
/** Creates a ClusterSpec when requesting a cluster */
@@ -94,6 +104,7 @@ public final class ClusterSpec {
private final Type type;
private final Id id;
private final boolean specification;
+ private boolean stateful;
private Optional<Group> groupId = Optional.empty();
private Optional<DockerImage> dockerImageRepo = Optional.empty();
@@ -105,6 +116,7 @@ public final class ClusterSpec {
this.type = type;
this.id = id;
this.specification = specification;
+ this.stateful = type.isContent(); // Default to true for content clusters
}
public ClusterSpec build() {
@@ -113,7 +125,7 @@ public final class ClusterSpec {
if (vespaVersion == null) throw new IllegalArgumentException("vespaVersion is required to be set when creating a ClusterSpec with specification()");
} else
if (groupId.isPresent()) throw new IllegalArgumentException("groupId is not allowed to be set when creating a ClusterSpec with request()");
- return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, combinedId, dockerImageRepo);
+ return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, combinedId, dockerImageRepo, stateful);
}
public Builder group(Group groupId) {
@@ -146,6 +158,11 @@ public final class ClusterSpec {
return this;
}
+ public Builder stateful(boolean stateful) {
+ this.stateful = stateful;
+ return this;
+ }
+
}
@Override
@@ -154,19 +171,23 @@ public final class ClusterSpec {
}
@Override
- public int hashCode() { return type.hashCode() + 17 * id.hashCode() + 31 * groupId.hashCode(); }
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ClusterSpec that = (ClusterSpec) o;
+ return exclusive == that.exclusive &&
+ stateful == that.stateful &&
+ type == that.type &&
+ id.equals(that.id) &&
+ groupId.equals(that.groupId) &&
+ vespaVersion.equals(that.vespaVersion) &&
+ combinedId.equals(that.combinedId) &&
+ dockerImageRepo.equals(that.dockerImageRepo);
+ }
@Override
- public boolean equals(Object o) {
- if (o == this) return true;
- if ( ! (o instanceof ClusterSpec)) return false;
- ClusterSpec other = (ClusterSpec)o;
- if ( ! other.type.equals(this.type)) return false;
- if ( ! other.id.equals(this.id)) return false;
- if ( ! other.groupId.equals(this.groupId)) return false;
- if ( ! other.vespaVersion.equals(this.vespaVersion)) return false;
- if ( ! other.dockerImageRepo.equals(this.dockerImageRepo)) return false;
- return true;
+ public int hashCode() {
+ return Objects.hash(type, id, groupId, vespaVersion, exclusive, combinedId, dockerImageRepo, stateful);
}
/**
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 71e039f6e8e..1f170cca9a0 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
@@ -26,31 +26,41 @@ public class ClusterMembershipTest {
{
ClusterMembership instance = ClusterMembership.from("container/id1/4/37/exclusive/retired", Vtag.currentVersion, Optional.empty());
ClusterMembership serialized = ClusterMembership.from(instance.stringValue(), Vtag.currentVersion, Optional.empty());
+ assertFalse(serialized.cluster().isStateful());
assertEquals(instance, serialized);
assertTrue(instance.retired());
assertTrue(instance.cluster().isExclusive());
- assertFalse(instance.cluster().combinedId().isPresent());
- assertTrue(instance.cluster().dockerImageRepo().isEmpty());
}
{
ClusterMembership instance = ClusterMembership.from("container/id1/4/37/exclusive", Vtag.currentVersion, Optional.empty());
ClusterMembership serialized = ClusterMembership.from(instance.stringValue(), Vtag.currentVersion, Optional.empty());
+ assertFalse(serialized.cluster().isStateful());
assertEquals(instance, serialized);
- assertFalse(instance.retired());
assertTrue(instance.cluster().isExclusive());
- assertFalse(instance.cluster().combinedId().isPresent());
- assertTrue(instance.cluster().dockerImageRepo().isEmpty());
}
{
Optional<DockerImage> dockerImageRepo = Optional.of(DockerImage.fromString("docker.foo.com:4443/vespa/bar"));
ClusterMembership instance = ClusterMembership.from("combined/id1/4/37/exclusive/containerId1", Vtag.currentVersion, dockerImageRepo);
ClusterMembership serialized = ClusterMembership.from(instance.stringValue(), Vtag.currentVersion, dockerImageRepo);
+ assertTrue(serialized.cluster().isStateful());
assertEquals(instance, serialized);
- assertFalse(instance.retired());
- assertTrue(instance.cluster().isExclusive());
assertEquals(ClusterSpec.Id.from("containerId1"), instance.cluster().combinedId().get());
assertEquals(dockerImageRepo.get(), instance.cluster().dockerImageRepo().get());
}
+ {
+ ClusterMembership instance = ClusterMembership.from("container/id1/4/37", Vtag.currentVersion, Optional.empty());
+ ClusterMembership serialized = ClusterMembership.from(instance.stringValue(), Vtag.currentVersion, Optional.empty());
+ assertEquals(instance, serialized);
+ assertFalse("Skips serialization of stateful property", instance.cluster().isStateful());
+ }
+ }
+
+ @Test
+ public void testLegacyFormat() { // TODO(mpolden): Remove after December 2020
+ ClusterMembership instance = ClusterMembership.from("content/id1/4/37/exclusive", Vtag.currentVersion, Optional.empty());
+ ClusterMembership serialized = ClusterMembership.from(instance.stringValue(), Vtag.currentVersion, Optional.empty());
+ assertEquals(instance, serialized);
+ assertTrue(serialized.cluster().isStateful());
}
@Test