From a219da1a7379606860c874a64f94aafb8973d205 Mon Sep 17 00:00:00 2001 From: Jon Marius Venstad Date: Fri, 19 Feb 2021 17:38:26 +0100 Subject: Test models with dedicated cluster controllers --- .../model/builder/xml/dom/NodesSpecification.java | 5 +- .../model/provision/ModelProvisioningTest.java | 84 ++++++++++++ .../metricsproxy/MetricsProxyContainerTest.java | 26 +++- .../vespa/model/content/ContentClusterTest.java | 151 +++++++++++++++++---- .../vespa/model/test/ModelAmendingTestCase.java | 52 ++++++- 5 files changed, 283 insertions(+), 35 deletions(-) (limited to 'config-model') 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 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 @@ -808,6 +808,46 @@ public class ModelProvisioningTest { assertEquals(1, clusterControllers.getContainers().size()); } + @Test + public void testDedicatedClusterControllers() { + String services = + "\n" + + "" + + " " + + " 2" + + " " + + " " + + " " + + " " + + " " + + " " + + " 2" + + " " + + " " + + " " + + " " + + " " + + ""; + + 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 = @@ -1034,6 +1074,50 @@ public class ModelProvisioningTest { tester.createModel(services, false); } + @Test(expected = IllegalArgumentException.class) + public void testRequiredNodesAndDedicatedClusterControllers() { + String services = + "\n" + + "" + + " " + + " 1" + + " " + + " " + + " " + + " " + + " " + + ""; + + 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 = + "\n" + + "" + + " " + + " 1" + + " " + + " " + + " " + + " " + + " " + + ""; + + 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 = "" + - "" + - " " + - " " + - " " + - " " + - " " + - " " + - " " + - " " + - " " + - " " + - " 2" + - " " + - " " + - " " + - " " + - " " + - " " + - " 0" + - " " + - " " + - " " + - " " + - " " + - " " + - " " + - " " + - " " + - " "; + "" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " 2" + + " " + + " " + + " " + + " " + + " " + + " " + + " 0" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " "; + return createEnd2EndOneNode(properties, services); + } + VespaModel createEnd2EndOneNode(ModelContext.Properties properties, String services) { DeployState.Builder deployStateBuilder = new DeployState.Builder().properties(properties); List 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), + "" + + "" + + " " + + " "); + assertEquals(Map.of(), noContentModel.getContentClusters()); + assertNull("No cluster controller without content", noContentModel.getAdmin().getClusterControllers()); + + VespaModel oneContentModel = createEnd2EndOneNode(new TestProperties().setDedicatedClusterControllerCluster(true) + .setMultitenant(true), + "" + + "" + + " " + + " " + + " 1" + + " " + + " " + + " " + + " " + + " "); + assertNull("No own cluster controller for content", oneContentModel.getContentClusters().get("storage").getClusterControllers()); + assertNotNull("Shared cluster controller with content", oneContentModel.getAdmin().getClusterControllers()); + + String twoContentServices = "" + + "" + + " " + + " " + + " 1" + + " " + + " " + + " " + + " " + + " " + + " 0.618" + + " " + + " " + + " " + + " " + + " 1" + + " " + + " " + + " " + + " " + + " " + + " 0.418" + + " " + + " " + + " " + + " "; + 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 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 { " " + ""; 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 = + "" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " 1" + + " " + + " " + + " " + + " " + + " " + + " " + + " 1" + + " " + + " " + + " " + + " " + + " " + + ""; + 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"))); -- cgit v1.2.3