summaryrefslogtreecommitdiffstats
path: root/configserver
diff options
context:
space:
mode:
authorØyvind Grønnesby <oyving@verizonmedia.com>2019-06-05 17:10:07 +0200
committerØyvind Grønnesby <oyving@verizonmedia.com>2019-06-05 17:10:07 +0200
commit793209c73c3f118dac7b2cb5a6ef72c555439fe8 (patch)
tree10d1f5e920419ff835843a28891f5138494d8a65 /configserver
parenta5d2470ac60e9bcb8c67174a203b28faea11bef2 (diff)
Create new rotations cache that keeps info on endpoints and clusters
Diffstat (limited to 'configserver')
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/RotationAssignment.java79
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/RotationsCache.java77
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/tenant/RotationsCacheTest.java117
3 files changed, 273 insertions, 0 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/RotationAssignment.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/RotationAssignment.java
new file mode 100644
index 00000000000..e72570b63a7
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/RotationAssignment.java
@@ -0,0 +1,79 @@
+// Copyright 2019 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.config.server.tenant;
+
+import com.yahoo.config.provision.Rotation;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Inspector;
+import com.yahoo.vespa.applicationmodel.ClusterId;
+
+import java.util.Objects;
+
+public class RotationAssignment {
+ private final String endpointId;
+ private final Rotation rotation;
+ private final ClusterId containerId;
+
+ RotationAssignment(String endpointId, Rotation rotationName, ClusterId clusterId) {
+ this.endpointId = Objects.requireNonNull(endpointId);
+ this.rotation = Objects.requireNonNull(rotationName);
+ this.containerId = Objects.requireNonNull(clusterId);
+ }
+
+ public String getEndpointId() {
+ return endpointId;
+ }
+
+ public Rotation getRotation() {
+ return rotation;
+ }
+
+ public ClusterId getContainerId() {
+ return containerId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ RotationAssignment that = (RotationAssignment) o;
+ return Objects.equals(endpointId, that.endpointId) &&
+ Objects.equals(rotation, that.rotation) &&
+ Objects.equals(containerId, that.containerId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(endpointId, rotation, containerId);
+ }
+
+ static RotationAssignment fromSlime(Inspector inspector) {
+ final var endpointId = inspector.field("endpointId").asString();
+ final var rotationId = inspector.field("rotationId").asString();
+ final var containerId = inspector.field("containerId").asString();
+
+
+ if (endpointId.equals("")) {
+ throw new IllegalStateException("Missing 'endpointId' in RotationsCache");
+ }
+
+ if (rotationId.equals("")) {
+ throw new IllegalStateException("Missing 'rotationId' in RotationsCache");
+ }
+
+ if (containerId.equals("")) {
+ throw new IllegalStateException("Missing 'containerId' in RotationsCache");
+ }
+
+ return new RotationAssignment(
+ endpointId,
+ new Rotation(rotationId),
+ new ClusterId(containerId)
+ );
+ }
+
+ void toSlime(Cursor cursor) {
+ cursor.setString("endpointId", endpointId);
+ cursor.setString("rotationId", rotation.getId());
+ cursor.setString("containerId", containerId.toString());
+ }
+}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/RotationsCache.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/RotationsCache.java
new file mode 100644
index 00000000000..d1c9dc329ed
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/RotationsCache.java
@@ -0,0 +1,77 @@
+// Copyright 2019 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.config.server.tenant;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.path.Path;
+import com.yahoo.slime.ObjectTraverser;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.applicationmodel.ClusterId;
+import com.yahoo.vespa.config.SlimeUtils;
+import com.yahoo.vespa.curator.Curator;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * @author ogronnesby
+ */
+public class RotationsCache {
+ private final Path cachePath;
+ private final Curator curator;
+
+ public RotationsCache(Path tenantPath, Curator curator) {
+ this.cachePath = tenantPath.append("rotationsCache-v2/");
+ this.curator = curator;
+ }
+
+ public Map<ClusterId, RotationAssignment> getRotationAssignment(ApplicationId applicationId) {
+ final var optionalData = curator.getData(applicationPath(applicationId));
+ return optionalData
+ .map(SlimeUtils::jsonToSlime)
+ .map(RotationsCache::entryFromSlime)
+ .orElse(Collections.emptyMap());
+ }
+
+ public void putRotationAssignment(ApplicationId applicationId, Map<ClusterId, RotationAssignment> assignments) {
+ if (assignments.isEmpty()) return;
+ try {
+ curator.set(
+ applicationPath(applicationId),
+ SlimeUtils.toJsonBytes(entryToSlime(assignments))
+ );
+ } catch (IOException e) {
+ throw new RuntimeException("Error writing rotations of: " + applicationId, e);
+ }
+ }
+
+ static Map<ClusterId, RotationAssignment> entryFromSlime(Slime slime) {
+ final var assignmentMap = new HashMap<ClusterId, RotationAssignment>();
+
+ slime.get().traverse((ObjectTraverser) (name, inspector) -> {
+ final var containerId = new ClusterId(name);
+ final var assignment = RotationAssignment.fromSlime(inspector);
+ assignmentMap.put(containerId, assignment);
+ });
+
+ return Map.copyOf(assignmentMap);
+ }
+
+ static Slime entryToSlime(Map<ClusterId, RotationAssignment> assignments) {
+ final var slime = new Slime();
+ final var cursor = slime.setObject();
+
+ assignments.forEach((clusterId, assignment) -> {
+ final var assignmentCursor = cursor.setObject(clusterId.toString());
+ assignment.toSlime(assignmentCursor);
+ });
+
+ return slime;
+ }
+
+ private Path applicationPath(ApplicationId applicationId) {
+ return cachePath.append(applicationId.serializedForm());
+ }
+}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/RotationsCacheTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/RotationsCacheTest.java
new file mode 100644
index 00000000000..b0108d028d2
--- /dev/null
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/RotationsCacheTest.java
@@ -0,0 +1,117 @@
+// Copyright 2019 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.config.server.tenant;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.Rotation;
+import com.yahoo.path.Path;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.applicationmodel.ClusterId;
+import com.yahoo.vespa.curator.mock.MockCurator;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+
+public class RotationsCacheTest {
+ @Test
+ public void assignmentDeserialization() {
+ final var slime = new Slime();
+ final var cursor = slime.setObject();
+
+ cursor.setString("endpointId", "nallefisk");
+ cursor.setString("containerId", "froskelosk");
+ cursor.setString("rotationId", "rotterott");
+
+ final var assignment = RotationAssignment.fromSlime(cursor);
+ assertEquals("nallefisk", assignment.getEndpointId());
+ assertEquals("froskelosk", assignment.getContainerId().toString());
+ assertEquals("rotterott", assignment.getRotation().getId());
+ }
+
+ @Test
+ public void assignmentSerialization() {
+ final var assignment = new RotationAssignment(
+ "sluttpeker",
+ new Rotation("rotasjon"),
+ new ClusterId("klynge")
+ );
+
+ final var serialized = new Slime();
+ assignment.toSlime(serialized.setObject());
+
+ assertEquals(assignment, RotationAssignment.fromSlime(serialized.get()));
+ }
+
+ @Test
+ public void mapDeserialization() {
+ final var slime = new Slime();
+ final var cursor = slime.setObject();
+
+ final var clusterId = new ClusterId("nalle");
+
+ final var entry = cursor.setObject("nalle");
+ entry.setString("endpointId", "nallefisk");
+ entry.setString("containerId", clusterId.toString());
+ entry.setString("rotationId", "rotterott");
+
+ final var assignments = RotationsCache.entryFromSlime(slime);
+ assertEquals(Set.of(clusterId), assignments.keySet());
+
+ // check the entry
+ final var assignment = assignments.get(clusterId);
+ assertEquals(clusterId, assignment.getContainerId());
+ assertEquals("nallefisk", assignment.getEndpointId());
+ assertEquals(new Rotation("rotterott"), assignment.getRotation());
+ }
+
+ @Test
+ public void mapSerialization() {
+ final var assignments = new HashMap<ClusterId, RotationAssignment>();
+
+ assignments.put(
+ new ClusterId("the-nallefisk-1"),
+ new RotationAssignment(
+ "the-endpoint-1",
+ new Rotation("the-rotation-1"),
+ new ClusterId("the-cluster-1")
+ )
+ );
+
+ assignments.put(
+ new ClusterId("the-nallefisk-2"),
+ new RotationAssignment(
+ "the-endpoint-2",
+ new Rotation("the-rotation-2"),
+ new ClusterId("the-cluster-2")
+ )
+ );
+
+ final var serialized = RotationsCache.entryToSlime(assignments);
+ final var deserialized = RotationsCache.entryFromSlime(serialized);
+
+ assertEquals(assignments, deserialized);
+ }
+
+ @Test
+ public void cacheStoreAndLoad() {
+ final var rotations = new RotationsCache(Path.createRoot().append("foo"), new MockCurator());
+ final var assignments = new HashMap<ClusterId, RotationAssignment>();
+
+ assignments.put(
+ new ClusterId("the-nallefisk-1"),
+ new RotationAssignment(
+ "the-endpoint-1",
+ new Rotation("the-rotation-1"),
+ new ClusterId("the-cluster-1")
+ )
+ );
+
+ rotations.putRotationAssignment(ApplicationId.defaultId(), assignments);
+ final var fetched = rotations.getRotationAssignment(ApplicationId.defaultId());
+
+ assertEquals(assignments, fetched);
+ }
+
+}