aboutsummaryrefslogtreecommitdiffstats
path: root/node-repository/src/test
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2018-11-27 14:14:21 +0100
committerMartin Polden <mpolden@mpolden.no>2018-11-27 14:40:00 +0100
commitb084bc71e5c921698a27157333cf9ac0c7aa09be (patch)
tree0e421e6a2b634737e32d2f3c15777897df14e3eb /node-repository/src/test
parente12e2d54042b2aeca632ee630f0d67695dfb2f1b (diff)
Support feature flags in node repository
This implements feature flags for the node repository. A feature flag can be toggled on/off for the following dimensions: 1) The node repository (entire zone) 2) A specific node 3) A specific application Flags must be declared in the `FlagId` enum, this is typically done when implementing the feature that should be guarded by a flag. Flag status is stored in ZooKeeper. Inspecting and toggling flag status is done through a REST API, see `RestApiTest#test_flags()`.
Diffstat (limited to 'node-repository/src/test')
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/flag/FlagsTest.java64
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/FlagSerializerTest.java33
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java38
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/flags1.json10
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/flags2.json14
5 files changed, 159 insertions, 0 deletions
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/flag/FlagsTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/flag/FlagsTest.java
new file mode 100644
index 00000000000..5018b18c491
--- /dev/null
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/flag/FlagsTest.java
@@ -0,0 +1,64 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.provision.flag;
+
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.HostName;
+import com.yahoo.vespa.curator.mock.MockCurator;
+import com.yahoo.vespa.hosted.provision.NodeRepository;
+import com.yahoo.vespa.hosted.provision.testutils.MockNodeFlavors;
+import com.yahoo.vespa.hosted.provision.testutils.MockNodeRepository;
+import org.junit.Test;
+
+import java.util.function.Supplier;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author mpolden
+ */
+public class FlagsTest {
+
+ @Test
+ public void test_flag_toggling() {
+ NodeRepository nodeRepository = new MockNodeRepository(new MockCurator(), new MockNodeFlavors());
+ Flags flags = nodeRepository.flags();
+ Supplier<Flag> flag = () -> flags.get(FlagId.exclusiveLoadBalancer);
+
+ // Flag is disabled by default
+ assertFalse(flag.get().isEnabled());
+
+ // Toggle flag for a node
+ {
+ HostName node1 = HostName.from("host1");
+ flags.setEnabled(FlagId.exclusiveLoadBalancer, node1, true);
+ assertTrue(flag.get().isEnabled(node1));
+ assertFalse(flag.get().isEnabled());
+ flags.setEnabled(FlagId.exclusiveLoadBalancer, node1, false);
+ assertFalse(flag.get().isEnabled(node1));
+ }
+
+ // Toggle flag for an application
+ {
+ ApplicationId app1 = ApplicationId.from("tenant1", "application1", "default");
+ flags.setEnabled(FlagId.exclusiveLoadBalancer, app1, true);
+ assertTrue(flag.get().isEnabled(app1));
+ assertFalse(flag.get().isEnabled());
+ flags.setEnabled(FlagId.exclusiveLoadBalancer, app1, false);
+ assertFalse(flag.get().isEnabled(app1));
+ }
+
+ // Toggle flag globally
+ {
+ flags.setEnabled(FlagId.exclusiveLoadBalancer, true);
+ assertTrue(flag.get().isEnabled());
+ // Flag is implicitly enabled for all dimensions
+ assertTrue(flag.get().isEnabled(HostName.from("host1")));
+ assertTrue(flag.get().isEnabled(ApplicationId.from("tenant1", "application1", "default")));
+ flags.setEnabled(FlagId.exclusiveLoadBalancer, false);
+ assertFalse(flag.get().isEnabled());
+ }
+ }
+
+
+}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/FlagSerializerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/FlagSerializerTest.java
new file mode 100644
index 00000000000..15f2289d340
--- /dev/null
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/FlagSerializerTest.java
@@ -0,0 +1,33 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.provision.persistence;
+
+import com.google.common.collect.ImmutableSet;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.vespa.hosted.provision.flag.Flag;
+import com.yahoo.vespa.hosted.provision.flag.FlagId;
+import org.junit.Test;
+
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author mpolden
+ */
+public class FlagSerializerTest {
+
+ @Test
+ public void test_serialization() {
+ Flag flag = new Flag(FlagId.exclusiveLoadBalancer, true,
+ ImmutableSet.of("host1", "host2"),
+ Collections.singleton(
+ ApplicationId.from("tenant1", "application1", "default")
+ ));
+ Flag serialized = FlagSerializer.fromJson(FlagSerializer.toJson(flag));
+ assertEquals(flag.id(), serialized.id());
+ assertEquals(flag.isEnabled(), serialized.isEnabled());
+ assertEquals(flag.hostnames(), serialized.hostnames());
+ assertEquals(flag.applications(), serialized.applications());
+ }
+
+}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java
index 2ff1e403e35..ae7f3f14975 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java
@@ -659,6 +659,44 @@ public class RestApiTest {
"]}");
}
+ @Test
+ public void test_flags() throws Exception {
+ assertFile(new Request("http://localhost:8080/nodes/v2/flags/"), "flags1.json");
+
+ // Enable flag for application
+ assertResponse(new Request("http://localhost:8080/nodes/v2/flags/exclusive-load-balancer/application/foo:bar:default",
+ new byte[0], Request.Method.POST),
+ "{\"message\":\"Enabled feature exclusiveLoadBalancer for application 'foo:bar:default'\"}");
+
+ // Enable flag for node
+ assertResponse(new Request("http://localhost:8080/nodes/v2/flags/exclusive-load-balancer/node/host1",
+ new byte[0], Request.Method.POST),
+ "{\"message\":\"Enabled feature exclusiveLoadBalancer for node 'host1'\"}");
+
+ assertFile(new Request("http://localhost:8080/nodes/v2/flags/"), "flags2.json");
+
+ // Enable flag for entire repository
+ assertResponse(new Request("http://localhost:8080/nodes/v2/flags/exclusive-load-balancer",
+ new byte[0], Request.Method.POST),
+ "{\"message\":\"Enabled feature exclusiveLoadBalancer\"}");
+
+ // Disable flag for application
+ assertResponse(new Request("http://localhost:8080/nodes/v2/flags/exclusive-load-balancer/application/foo:bar:default",
+ new byte[0], Request.Method.DELETE),
+ "{\"message\":\"Disabled feature exclusiveLoadBalancer for application 'foo:bar:default'\"}");
+
+ // Disable flag for node
+ assertResponse(new Request("http://localhost:8080/nodes/v2/flags/exclusive-load-balancer/node/host1",
+ new byte[0], Request.Method.DELETE),
+ "{\"message\":\"Disabled feature exclusiveLoadBalancer for node 'host1'\"}");
+
+ // Disable flag for entire repository
+ assertResponse(new Request("http://localhost:8080/nodes/v2/flags/exclusive-load-balancer",
+ new byte[0], Request.Method.DELETE),
+ "{\"message\":\"Disabled feature exclusiveLoadBalancer\"}");
+
+ }
+
/** Tests the rendering of each node separately to make it easier to find errors */
@Test
public void test_single_node_rendering() throws Exception {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/flags1.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/flags1.json
new file mode 100644
index 00000000000..8fd09b4a274
--- /dev/null
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/flags1.json
@@ -0,0 +1,10 @@
+{
+ "flags": [
+ {
+ "id": "exclusive-load-balancer",
+ "enabled": false,
+ "enabledHostnames": [],
+ "enabledApplications": []
+ }
+ ]
+}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/flags2.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/flags2.json
new file mode 100644
index 00000000000..78de52e4e85
--- /dev/null
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/flags2.json
@@ -0,0 +1,14 @@
+{
+ "flags": [
+ {
+ "id": "exclusive-load-balancer",
+ "enabled": false,
+ "enabledHostnames": [
+ "host1"
+ ],
+ "enabledApplications": [
+ "foo:bar:default"
+ ]
+ }
+ ]
+}