diff options
Diffstat (limited to 'config-model/src')
3 files changed, 73 insertions, 1 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java index b95571de936..b7d64483e57 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java @@ -112,6 +112,10 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { private static final String DEPRECATED_CONTAINER_TAG = "jdisc"; private static final String ENVIRONMENT_VARIABLES_ELEMENT = "environment-variables"; + // The node count to enforce in a cluster running ZooKeeper + private static final int MIN_ZOOKEEPER_NODE_COUNT = 3; + private static final int MAX_ZOOKEEPER_NODE_COUNT = 7; + public enum Networking { disable, enable } private ApplicationPackage app; @@ -195,7 +199,26 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { addClientProviders(deployState, spec, cluster); addServerProviders(deployState, spec, cluster); - addAthensCopperArgos(cluster, context); // Must be added after nodes. + // Must be added after nodes: + addAthensCopperArgos(cluster, context); + addZooKeeper(cluster, spec); + } + + private void addZooKeeper(ApplicationContainerCluster cluster, Element spec) { + Element zkElement = XML.getChild(spec, "zookeeper"); + if (zkElement == null) return; + Element nodesElement = XML.getChild(spec, "nodes"); + boolean isCombined = nodesElement != null && nodesElement.hasAttribute("of"); + if (isCombined) { + throw new IllegalArgumentException("A combined cluster cannot run ZooKeeper"); + } + int nodeCount = cluster.getContainers().size(); + if (nodeCount < MIN_ZOOKEEPER_NODE_COUNT || nodeCount > MAX_ZOOKEEPER_NODE_COUNT || nodeCount % 2 == 0) { + throw new IllegalArgumentException("Clusters running ZooKeeper must have an odd number of nodes, between " + + MIN_ZOOKEEPER_NODE_COUNT + " and " + MAX_ZOOKEEPER_NODE_COUNT); + } + cluster.addSimpleComponent("com.yahoo.vespa.curator.Curator", null, "zkfacade"); + // TODO: Add server component } private void addSecretStore(ApplicationContainerCluster cluster, Element spec) { diff --git a/config-model/src/main/resources/schema/containercluster.rnc b/config-model/src/main/resources/schema/containercluster.rnc index 25d10e0d9b3..ca7efd5a938 100644 --- a/config-model/src/main/resources/schema/containercluster.rnc +++ b/config-model/src/main/resources/schema/containercluster.rnc @@ -27,6 +27,7 @@ ContainerServices = Http? & AccessLog* & SecretStore? & + ZooKeeper? & GenericConfig* # TODO(ogronnesby): Change this configuration syntax @@ -91,6 +92,10 @@ SecretStore = element secret-store { } + } +ZooKeeper = element zookeeper { + empty +} + ModelEvaluation = element model-evaluation { empty } diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java index 13c1631e0ce..32dd5cc944c 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilderTest.java @@ -40,11 +40,13 @@ import com.yahoo.vespa.defaults.Defaults; import com.yahoo.vespa.model.AbstractService; import com.yahoo.vespa.model.VespaModel; import com.yahoo.vespa.model.container.ApplicationContainer; +import com.yahoo.vespa.model.container.ApplicationContainerCluster; import com.yahoo.vespa.model.container.ContainerCluster; import com.yahoo.vespa.model.container.SecretStore; import com.yahoo.vespa.model.container.component.Component; import com.yahoo.vespa.model.container.http.ConnectorFactory; import com.yahoo.vespa.model.content.utils.ContentClusterUtils; +import com.yahoo.vespa.model.test.VespaModelTester; import com.yahoo.vespa.model.test.utils.VespaModelCreatorWithFilePkg; import org.hamcrest.Matchers; import org.junit.Rule; @@ -59,6 +61,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Function; import java.util.logging.Level; import java.util.stream.Collectors; @@ -871,6 +874,46 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase { assertThat(connectorConfig.ssl().caCertificate(), isEmptyString()); } + @Test + public void cluster_with_zookeeper() { + Function<Integer, String> servicesXml = (nodeCount) -> "<container version='1.0' id='default'>" + + "<nodes count='" + nodeCount + "'/>" + + "<zookeeper/>" + + "</container>"; + VespaModelTester tester = new VespaModelTester(); + tester.addHosts(3); + { + VespaModel model = tester.createModel(servicesXml.apply(3), true); + String componentId = "com.yahoo.vespa.curator.Curator"; + ApplicationContainerCluster cluster = model.getContainerClusters().get("default"); + assertNotNull(cluster); + Component<?, ?> curatorComponent = cluster.getComponentsMap().get(ComponentId.fromString(componentId)); + assertNotNull(curatorComponent); + } + { + try { + tester.createModel(servicesXml.apply(1), true); + fail("Expected exception"); + } catch (IllegalArgumentException ignored) {} + } + { + String xmlWithNodes = + "<?xml version='1.0' encoding='utf-8' ?>" + + "<services>" + + " <container version='1.0' id='container1'>" + + " <zookeeper/>" + + " <nodes of='content1'/>" + + " </container>" + + " <content version='1.0' id='content1'>" + + " <nodes count='3'/>" + + " </content>" + + "</services>"; + try { + tester.createModel(xmlWithNodes, true); + fail("Expected exception"); + } catch (IllegalArgumentException ignored) {} + } + } private Element generateContainerElementWithRenderer(String rendererId) { return DomBuilderTest.parse( @@ -880,4 +923,5 @@ public class ContainerModelBuilderTest extends ContainerModelBuilderTestBase { " </search>", "</container>"); } + } |