diff options
author | Jon Marius Venstad <venstad@gmail.com> | 2021-02-16 18:48:31 +0100 |
---|---|---|
committer | Jon Marius Venstad <venstad@gmail.com> | 2021-02-19 17:40:10 +0100 |
commit | 08f8d5424752c522c4b074b7d47945647dc298b0 (patch) | |
tree | 2ed9bef00a26e5ebff20babc6c89f08ea9450cea | |
parent | 7f49e00aecfe16e83d6bec9ec5bc7e1777e65a9f (diff) |
Provision dedicated CC cluster when this is asked for
4 files changed, 79 insertions, 14 deletions
diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java index 583f7bd9ec6..502072de1c7 100644 --- a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java +++ b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java @@ -35,6 +35,7 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea private Zone zone; private final Set<ContainerEndpoint> endpoints = Collections.emptySet(); private boolean useDedicatedNodeForLogserver = false; + private boolean dedicatedClusterControllerCluster = false; private boolean useThreePhaseUpdates = false; private double defaultTermwiseLimit = 1.0; private String jvmGCOptions = null; @@ -73,6 +74,7 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea @Override public boolean isBootstrap() { return false; } @Override public boolean isFirstTimeDeployment() { return false; } @Override public boolean useDedicatedNodeForLogserver() { return useDedicatedNodeForLogserver; } + @Override public boolean dedicatedClusterControllerCluster() { return dedicatedClusterControllerCluster; } @Override public Optional<EndpointCertificateSecrets> endpointCertificateSecrets() { return endpointCertificateSecrets; } @Override public double defaultTermwiseLimit() { return defaultTermwiseLimit; } @Override public boolean useThreePhaseUpdates() { return useThreePhaseUpdates; } @@ -162,6 +164,11 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea return this; } + public TestProperties setDedicatedClusterControllerCluster(boolean dedicatedClusterControllerCluster) { + this.dedicatedClusterControllerCluster = dedicatedClusterControllerCluster; + return this; + } + public TestProperties setEndpointCertificateSecrets(Optional<EndpointCertificateSecrets> endpointCertificateSecrets) { this.endpointCertificateSecrets = endpointCertificateSecrets; return this; diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java index 56b74134ebe..08f7ff82f4e 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java @@ -142,7 +142,6 @@ public class Admin extends AbstractConfigProducer<Admin> implements Serializable public ClusterControllerContainerCluster getClusterControllers() { return clusterControllers; } public void setClusterControllers(ClusterControllerContainerCluster clusterControllers) { - if (multitenant) throw new RuntimeException("Should not use admin cluster controller in a multitenant environment"); this.clusterControllers = clusterControllers; } 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 aace6818ba6..8e94a786b4c 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 @@ -22,6 +22,9 @@ 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; /** * A common utility class to represent a requirement for nodes during model building. @@ -183,6 +186,28 @@ public class NodesSpecification { Optional.empty()); } + /** + * Returns a requirement for {@code count} nodes with {@code exclusive} and {@code required} taken as + * the OR over all content clusters, and with the given resources. + */ + public static NodesSpecification dedicatedFromSharedParents(int count, NodeResources resources, + ModelElement element, ConfigModelContext context) { + List<NodesSpecification> allContent = findParentByTag("services", element.getXml()).map(services -> XML.getChildren(services, "content")) + .orElse(List.of()) + .stream() + .map(content -> from(new ModelElement(content), context)) + .collect(toList()); + return new NodesSpecification(new ClusterResources(count, 1, resources), + new ClusterResources(count, 1, resources), + true, + context.getDeployState().getWantedNodeVespaVersion(), + allContent.stream().anyMatch(content -> content.required), + ! context.getDeployState().getProperties().isBootstrap(), + allContent.stream().anyMatch(content -> content.exclusive), + context.getDeployState().getWantedDockerImageRepo(), + Optional.empty()); + } + public ClusterResources minResources() { return min; } public ClusterResources maxResources() { return max; } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java index ea84c72e16c..9d5eda672d5 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java @@ -10,6 +10,7 @@ import com.yahoo.config.model.producer.AbstractConfigProducer; import com.yahoo.config.model.producer.AbstractConfigProducerRoot; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.Zone; import com.yahoo.documentapi.messagebus.protocol.DocumentProtocol; import com.yahoo.documentmodel.NewDocumentType; @@ -64,6 +65,8 @@ import java.util.TreeMap; import java.util.logging.Level; import java.util.stream.Collectors; +import static java.util.stream.Collectors.toList; + /** * A content cluster. * @@ -299,18 +302,20 @@ public class ContentCluster extends AbstractConfigProducer implements } else if (admin.multitenant()) { String clusterName = contentClusterName + "-controllers"; - NodesSpecification nodesSpecification = - NodesSpecification.optionalDedicatedFromParent(contentElement.child("controllers"), context) - .orElse(NodesSpecification.nonDedicated(3, context)); - Collection<HostResource> hosts = nodesSpecification.isDedicated() ? - getControllerHosts(nodesSpecification, admin, clusterName, context) : - drawControllerHosts(nodesSpecification.minResources().nodes(), rootGroup); - clusterControllers = createClusterControllers(new ClusterControllerCluster(contentCluster, "standalone"), - hosts, - clusterName, - true, - context.getDeployState()); - contentCluster.clusterControllers = clusterControllers; + Optional<NodesSpecification> nodesSpecification = NodesSpecification.optionalDedicatedFromParent(contentElement.child("controllers"), context); + if (nodesSpecification.isEmpty() && context.properties().dedicatedClusterControllerCluster()) + clusterControllers = getDedicatedSharedControllers(contentElement, admin, contentCluster, context); + else { + Collection<HostResource> hosts = nodesSpecification.isPresent() && nodesSpecification.get().isDedicated() + ? getControllerHosts(nodesSpecification.get(), admin, clusterName, context) + : drawControllerHosts(nodesSpecification.map(spec -> spec.minResources().nodes()).orElse(3), rootGroup); + clusterControllers = createClusterControllers(new ClusterControllerCluster(contentCluster, "standalone"), + hosts, + clusterName, + true, + context.getDeployState()); + contentCluster.clusterControllers = clusterControllers; + } } else { clusterControllers = admin.getClusterControllers(); @@ -346,6 +351,35 @@ public class ContentCluster extends AbstractConfigProducer implements return ! Sets.intersection(c1Hosts, c2Hosts).isEmpty(); } + private ClusterControllerContainerCluster getDedicatedSharedControllers(ModelElement contentElement, Admin admin, + ContentCluster contentCluster, ConfigModelContext context) { + if (admin.getClusterControllers() == null) { + NodesSpecification spec = NodesSpecification.dedicatedFromSharedParents(3, + new NodeResources(0.5, + 2, + 10, + 0.3, + NodeResources.DiskSpeed.any, + NodeResources.StorageType.any), + contentElement, + context); + + Collection<HostResource> hosts = spec.provision(admin.hostSystem(), + ClusterSpec.Type.admin, + ClusterSpec.Id.from("cluster-controllers"), + context.getDeployLogger(), + true) + .keySet(); + + admin.setClusterControllers(createClusterControllers(new ClusterControllerCluster(contentCluster, "standalone"), + hosts, + "cluster-controllers", + true, + context.getDeployState())); + } + return admin.getClusterControllers(); + } + private Collection<HostResource> getControllerHosts(NodesSpecification nodesSpecification, Admin admin, String clusterName, ConfigModelContext context) { return nodesSpecification.provision(admin.hostSystem(), ClusterSpec.Type.admin, ClusterSpec.Id.from(clusterName), context.getDeployLogger(), false).keySet(); } @@ -384,7 +418,7 @@ public class ContentCluster extends AbstractConfigProducer implements else { hosts.addAll(group.getNodes().stream() .filter(node -> node.isRetired() == retired) - .map(StorageNode::getHostResource).collect(Collectors.toList())); + .map(StorageNode::getHostResource).collect(toList())); } List<HostResource> sortedHosts = new ArrayList<>(hosts); |