aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Marius Venstad <venstad@gmail.com>2021-02-16 18:48:31 +0100
committerJon Marius Venstad <venstad@gmail.com>2021-02-19 17:40:10 +0100
commit08f8d5424752c522c4b074b7d47945647dc298b0 (patch)
tree2ed9bef00a26e5ebff20babc6c89f08ea9450cea
parent7f49e00aecfe16e83d6bec9ec5bc7e1777e65a9f (diff)
Provision dedicated CC cluster when this is asked for
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java7
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/admin/Admin.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java25
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/cluster/ContentCluster.java60
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);