summaryrefslogtreecommitdiffstats
path: root/config-model
diff options
context:
space:
mode:
authorJon Marius Venstad <venstad@gmail.com>2021-02-19 17:38:26 +0100
committerJon Marius Venstad <venstad@gmail.com>2021-02-19 17:41:20 +0100
commita219da1a7379606860c874a64f94aafb8973d205 (patch)
tree9d3bfe7b78fbc8ed14451150a3d4adc8d9103199 /config-model
parent19bc1b558b233a924de13f2a02796d450f1469be (diff)
Test models with dedicated cluster controllers
Diffstat (limited to 'config-model')
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java5
-rw-r--r--config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java84
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerTest.java26
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java151
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/test/ModelAmendingTestCase.java52
5 files changed, 283 insertions, 35 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
index 4c5ec1cbbc8..498461fa3f8 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java
@@ -20,6 +20,7 @@ import org.w3c.dom.Node;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -195,7 +196,9 @@ public class NodesSpecification {
List<NodesSpecification> allContent = findParentByTag("services", element.getXml()).map(services -> XML.getChildren(services, "content"))
.orElse(List.of())
.stream()
- .map(content -> from(new ModelElement(content), context))
+ .map(content -> new ModelElement(content).child("nodes"))
+ .filter(nodes -> nodes != null && nodes.integerAttribute("count") != null)
+ .map(nodes -> from(nodes, context))
.collect(toList());
return new NodesSpecification(new ClusterResources(count, 1, resources),
new ClusterResources(count, 1, resources),
diff --git a/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java b/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java
index 67203b544fa..1b4e26cb4ba 100644
--- a/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java
+++ b/config-model/src/test/java/com/yahoo/config/model/provision/ModelProvisioningTest.java
@@ -809,6 +809,46 @@ public class ModelProvisioningTest {
}
@Test
+ public void testDedicatedClusterControllers() {
+ String services =
+ "<?xml version='1.0' encoding='utf-8' ?>\n" +
+ "<services>" +
+ " <content version='1.0' id='foo'>" +
+ " <redundancy>2</redundancy>" +
+ " <documents>" +
+ " <document type='type1' mode='index'/>" +
+ " </documents>" +
+ " <nodes count='2' />" +
+ " </content>" +
+ " <content version='1.0' id='bar'>" +
+ " <redundancy>2</redundancy>" +
+ " <documents>" +
+ " <document type='type1' mode='index'/>" +
+ " </documents>" +
+ " <nodes count='2' />" +
+ " </content>" +
+ "</services>";
+
+ int numberOfHosts = 7;
+ VespaModelTester tester = new VespaModelTester();
+ tester.addHosts(numberOfHosts);
+ tester.dedicatedClusterControllerCluster(true);
+ VespaModel model = tester.createModel(services);
+ assertEquals(7, model.getRoot().hostSystem().getHosts().size());
+
+ // Check cluster controllers
+ assertNull(model.getContentClusters().get("foo").getClusterControllers());
+ assertNull(model.getContentClusters().get("bar").getClusterControllers());
+ ClusterControllerContainerCluster clusterControllers = model.getAdmin().getClusterControllers();
+ assertEquals(3, clusterControllers.getContainers().size());
+ assertEquals("cluster-controllers", clusterControllers.getName());
+ clusterControllers.getContainers().stream().map(ClusterControllerContainer::getHost).forEach(host -> {
+ assertTrue(host.spec().membership().get().cluster().isStateful());
+ assertEquals(ClusterSpec.Type.admin, host.spec().membership().get().cluster().type());
+ });
+ }
+
+ @Test
public void testExplicitDedicatedClusterControllers() {
String services =
"<?xml version='1.0' encoding='utf-8' ?>\n" +
@@ -1034,6 +1074,50 @@ public class ModelProvisioningTest {
tester.createModel(services, false);
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testRequiredNodesAndDedicatedClusterControllers() {
+ String services =
+ "<?xml version='1.0' encoding='utf-8' ?>\n" +
+ "<services>" +
+ " <content version='1.0' id='foo'>" +
+ " <redundancy>1</redundancy>" +
+ " <documents>" +
+ " <document type='type1' mode='index'/>" +
+ " </documents>" +
+ " <nodes count='2' required='true'/>" +
+ " </content>" +
+ "</services>";
+
+ int numberOfHosts = 4; // needs 2 for foo and 3 for cluster controllers.
+ VespaModelTester tester = new VespaModelTester();
+ tester.addHosts(numberOfHosts);
+ tester.dedicatedClusterControllerCluster(true);
+ tester.createModel(services, false);
+ }
+
+ @Test
+ public void testExclusiveDedicatedClusterControllers() {
+ String services =
+ "<?xml version='1.0' encoding='utf-8' ?>\n" +
+ "<services>" +
+ " <content version='1.0' id='foo'>" +
+ " <redundancy>1</redundancy>" +
+ " <documents>" +
+ " <document type='type1' mode='index'/>" +
+ " </documents>" +
+ " <nodes count='2' exclusive='true'/>" +
+ " </content>" +
+ "</services>";
+
+ int numberOfHosts = 5;
+ VespaModelTester tester = new VespaModelTester();
+ tester.addHosts(numberOfHosts);
+ tester.dedicatedClusterControllerCluster(true);
+ VespaModel model = tester.createModel(services);
+ assertEquals(5, model.hostSystem().getHosts().size());
+ model.hostSystem().getHosts().forEach(host -> assertTrue(host.spec().membership().get().cluster().isExclusive()));
+ }
+
@Test
public void testExclusiveNodes() {
String services =
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerTest.java b/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerTest.java
index a18211f7f47..87db274d4f5 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerTest.java
@@ -43,9 +43,29 @@ public class MetricsProxyContainerTest {
for (var host : model.hostSystem().getHosts()) {
assertThat(host.getService(METRICS_PROXY_CONTAINER.serviceName), notNullValue());
- long metricsProxies = host.getServices().stream()
- .filter(s -> s.getClass().equals(MetricsProxyContainer.class))
- .count();
+ long metricsProxies = host.getServices().stream()
+ .filter(s -> s.getClass().equals(MetricsProxyContainer.class))
+ .count();
+ assertThat(metricsProxies, is(1L));
+ }
+ }
+
+ @Test
+ public void one_metrics_proxy_container_is_added_to_every_node_also_when_dedicated_CCC() {
+ var numberOfHosts = 7;
+ var tester = new VespaModelTester();
+ tester.addHosts(numberOfHosts);
+ tester.dedicatedClusterControllerCluster(true);
+
+ VespaModel model = tester.createModel(servicesWithManyNodes(), true);
+ assertThat(model.getRoot().hostSystem().getHosts().size(), is(numberOfHosts));
+
+ for (var host : model.hostSystem().getHosts()) {
+ assertThat(host.getService(METRICS_PROXY_CONTAINER.serviceName), notNullValue());
+
+ long metricsProxies = host.getServices().stream()
+ .filter(s -> s.getClass().equals(MetricsProxyContainer.class))
+ .count();
assertThat(metricsProxies, is(1L));
}
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java
index 37d14074737..04fe2c94796 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/content/ContentClusterTest.java
@@ -25,7 +25,9 @@ import com.yahoo.vespa.config.search.DispatchConfig;
import com.yahoo.vespa.config.search.core.ProtonConfig;
import com.yahoo.vespa.model.VespaModel;
import com.yahoo.vespa.model.admin.clustercontroller.ClusterControllerContainer;
+import com.yahoo.vespa.model.admin.clustercontroller.ClusterControllerContainerCluster;
import com.yahoo.vespa.model.container.ContainerCluster;
+import com.yahoo.vespa.model.container.component.Component;
import com.yahoo.vespa.model.content.cluster.ContentCluster;
import com.yahoo.vespa.model.content.engines.ProtonEngine;
import com.yahoo.vespa.model.content.utils.ContentClusterBuilder;
@@ -42,12 +44,18 @@ import org.junit.rules.ExpectedException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -254,40 +262,44 @@ public class ContentClusterTest extends ContentBaseTest {
VespaModel createEnd2EndOneNode(ModelContext.Properties properties) {
String services =
"<?xml version='1.0' encoding='UTF-8' ?>" +
- "<services version='1.0'>" +
- " <admin version='2.0'>" +
- " <adminserver hostalias='node1'/>" +
- " </admin>" +
- " <container id='default' version='1.0'>" +
- " <search/>" +
- " <nodes>" +
- " <node hostalias='node1'/>" +
- " </nodes>" +
- " </container>" +
- " <content id='storage' version='1.0'>" +
- " <redundancy>2</redundancy>" +
- " <group>" +
- " <node distribution-key='0' hostalias='node1'/>" +
- " <node distribution-key='1' hostalias='node1'/>" +
- " </group>" +
- " <tuning>" +
- " <cluster-controller>" +
- " <transition-time>0</transition-time>" +
- " </cluster-controller>" +
- " </tuning>" +
- " <documents>" +
- " <document mode='index' type='type1'/>" +
- " </documents>" +
- " <engine>" +
- " <proton/>" +
- " </engine>" +
- " </content>" +
- " </services>";
+ "<services version='1.0'>" +
+ " <admin version='2.0'>" +
+ " <adminserver hostalias='node1'/>" +
+ " </admin>" +
+ " <container id='default' version='1.0'>" +
+ " <search/>" +
+ " <nodes>" +
+ " <node hostalias='node1'/>" +
+ " </nodes>" +
+ " </container>" +
+ " <content id='storage' version='1.0'>" +
+ " <redundancy>2</redundancy>" +
+ " <group>" +
+ " <node distribution-key='0' hostalias='node1'/>" +
+ " <node distribution-key='1' hostalias='node1'/>" +
+ " </group>" +
+ " <tuning>" +
+ " <cluster-controller>" +
+ " <transition-time>0</transition-time>" +
+ " </cluster-controller>" +
+ " </tuning>" +
+ " <documents>" +
+ " <document mode='index' type='type1'/>" +
+ " </documents>" +
+ " <engine>" +
+ " <proton/>" +
+ " </engine>" +
+ " </content>" +
+ " </services>";
+ return createEnd2EndOneNode(properties, services);
+ }
+ VespaModel createEnd2EndOneNode(ModelContext.Properties properties, String services) {
DeployState.Builder deployStateBuilder = new DeployState.Builder().properties(properties);
List<String> sds = ApplicationPackageUtils.generateSchemas("type1");
return (new VespaModelCreatorWithMockPkg(null, services, sds)).create(deployStateBuilder);
}
+
@Test
public void testEndToEndOneNode() {
VespaModel model = createEnd2EndOneNode(new TestProperties());
@@ -1059,4 +1071,85 @@ public class ContentClusterTest extends ContentBaseTest {
assertEquals(2, resolveMaxInhibitedGroupsConfigWithFeatureFlag(2));
}
+ @Test
+ public void testDedicatedClusterControllers() {
+ VespaModel noContentModel = createEnd2EndOneNode(new TestProperties().setDedicatedClusterControllerCluster(true)
+ .setMultitenant(true),
+ "<?xml version='1.0' encoding='UTF-8' ?>" +
+ "<services version='1.0'>" +
+ " <container id='default' version='1.0' />" +
+ " </services>");
+ assertEquals(Map.of(), noContentModel.getContentClusters());
+ assertNull("No cluster controller without content", noContentModel.getAdmin().getClusterControllers());
+
+ VespaModel oneContentModel = createEnd2EndOneNode(new TestProperties().setDedicatedClusterControllerCluster(true)
+ .setMultitenant(true),
+ "<?xml version='1.0' encoding='UTF-8' ?>" +
+ "<services version='1.0'>" +
+ " <container id='default' version='1.0' />" +
+ " <content id='storage' version='1.0'>" +
+ " <redundancy>1</redundancy>" +
+ " <documents>" +
+ " <document mode='index' type='type1' />" +
+ " </documents>" +
+ " </content>" +
+ " </services>");
+ assertNull("No own cluster controller for content", oneContentModel.getContentClusters().get("storage").getClusterControllers());
+ assertNotNull("Shared cluster controller with content", oneContentModel.getAdmin().getClusterControllers());
+
+ String twoContentServices = "<?xml version='1.0' encoding='UTF-8' ?>" +
+ "<services version='1.0'>" +
+ " <container id='default' version='1.0' />" +
+ " <content id='storage' version='1.0'>" +
+ " <redundancy>1</redundancy>" +
+ " <documents>" +
+ " <document mode='index' type='type1' />" +
+ " </documents>" +
+ " <tuning>" +
+ " <cluster-controller>" +
+ " <min-distributor-up-ratio>0.618</min-distributor-up-ratio>" +
+ " </cluster-controller>" +
+ " </tuning>" +
+ " </content>" +
+ " <content id='dev-null' version='1.0'>" +
+ " <redundancy>1</redundancy>" +
+ " <documents>" +
+ " <document mode='index' type='type1' />" +
+ " </documents>" +
+ " <tuning>" +
+ " <cluster-controller>" +
+ " <min-distributor-up-ratio>0.418</min-distributor-up-ratio>" +
+ " </cluster-controller>" +
+ " </tuning>" +
+ " </content>" +
+ " </services>";
+ VespaModel twoContentModel = createEnd2EndOneNode(new TestProperties().setDedicatedClusterControllerCluster(true)
+ .setMultitenant(true),
+ twoContentServices);
+ assertNull("No own cluster controller for content", twoContentModel.getContentClusters().get("storage").getClusterControllers());
+ assertNull("No own cluster controller for content", twoContentModel.getContentClusters().get("dev-null").getClusterControllers());
+ assertNotNull("Shared cluster controller with content", twoContentModel.getAdmin().getClusterControllers());
+
+ Map<String, ContentCluster> clustersWithOwnCCC = createEnd2EndOneNode(new TestProperties().setMultitenant(true), twoContentServices).getContentClusters();
+ ClusterControllerContainerCluster clusterControllers = twoContentModel.getAdmin().getClusterControllers();
+ assertEquals("Union of components in own clusters is equal to those in shared cluster",
+ clusterControllers.getAllComponents().stream()
+ .map(Component::getComponentId)
+ .collect(toList()),
+ clustersWithOwnCCC.values().stream()
+ .flatMap(cluster -> Optional.ofNullable(cluster.getClusterControllers()).stream()
+ .flatMap(c -> c.getAllComponents().stream()))
+ .map(Component::getComponentId)
+ .collect(toList()));
+
+ assertEquals(1, clusterControllers.reindexingContext().documentTypesForCluster("storage").size());
+ assertEquals(1, clusterControllers.reindexingContext().documentTypesForCluster("dev-null").size());
+ var storageBuilder = new FleetcontrollerConfig.Builder();
+ var devNullBuilder = new FleetcontrollerConfig.Builder();
+ twoContentModel.getConfig(storageBuilder, "storage/standalone/cluster-controllers/0/components/clustercontroller-storage-configurer");
+ twoContentModel.getConfig(devNullBuilder, "storage/standalone/cluster-controllers/0/components/clustercontroller-dev-null-configurer");
+ assertEquals(0.618, storageBuilder.build().min_distributor_up_ratio(), 1e-9);
+ assertEquals(0.418, devNullBuilder.build().min_distributor_up_ratio(), 1e-9);
+ }
+
}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/test/ModelAmendingTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/test/ModelAmendingTestCase.java
index da17044528f..001773abb92 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/test/ModelAmendingTestCase.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/test/ModelAmendingTestCase.java
@@ -74,14 +74,62 @@ public class ModelAmendingTestCase {
" </content>" +
"</services>";
VespaModelTester tester = new VespaModelTester(amendingModelRepo);
- tester.addHosts(10);
+ tester.addHosts(9);
VespaModel model = tester.createModel(services);
// Check that all hosts are amended
for (HostResource host : model.getAdmin().hostSystem().getHosts()) {
assertFalse(host + " is amended", host.getHost().getChildrenByTypeRecursive(AmendedService.class).isEmpty());
}
-
+
+ // Check that jdisc clusters are amended
+ assertEquals(2, model.getContainerClusters().size());
+ assertNotNull(model.getContainerClusters().get("test1").getComponentsMap().get(new ComponentId("com.yahoo.MyAmendedComponent")));
+ assertNotNull(model.getContainerClusters().get("test2").getComponentsMap().get(new ComponentId("com.yahoo.MyAmendedComponent")));
+ }
+
+ @Test
+ public void testModelAmendingWithDedicatedCC() {
+ ConfigModelRegistry amendingModelRepo = MapConfigModelRegistry.createFromList(new AdminModelAmenderBuilder(),
+ new ContainerModelAmenderBuilder(),
+ new ContentModelAmenderBuilder());
+ String services =
+ "<services version='1.0'>" +
+ " <admin version='4.0'/>" +
+ " <container id='test1' version='1.0'>" +
+ " <search/>" +
+ " <nodes count='2'/>" +
+ " </container>" +
+ " <container id='test2' version='1.0'>" +
+ " <http><server id='server1' port='" + Defaults.getDefaults().vespaWebServicePort() + "'/></http>" +
+ " <document-api/>" +
+ " <nodes count='2'/>" +
+ " </container>" +
+ " <content id='test3' version='1.0'>" +
+ " <redundancy>1</redundancy>" +
+ " <documents>" +
+ " <document mode='index' type='type1'/>" +
+ " </documents>" +
+ " <nodes count='2'/>" +
+ " </content>" +
+ " <content id='test4' version='1.0'>" +
+ " <redundancy>1</redundancy>" +
+ " <documents>" +
+ " <document mode='index' type='type1'/>" +
+ " </documents>" +
+ " <nodes count='3'/>" +
+ " </content>" +
+ "</services>";
+ VespaModelTester tester = new VespaModelTester(amendingModelRepo);
+ tester.dedicatedClusterControllerCluster(true);
+ tester.addHosts(12);
+ VespaModel model = tester.createModel(services);
+
+ // Check that all hosts are amended
+ for (HostResource host : model.getAdmin().hostSystem().getHosts()) {
+ assertFalse(host + " is amended", host.getHost().getChildrenByTypeRecursive(AmendedService.class).isEmpty());
+ }
+
// Check that jdisc clusters are amended
assertEquals(2, model.getContainerClusters().size());
assertNotNull(model.getContainerClusters().get("test1").getComponentsMap().get(new ComponentId("com.yahoo.MyAmendedComponent")));