summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@oath.com>2018-03-20 21:23:51 +0100
committerJon Bratseth <bratseth@oath.com>2018-03-20 21:23:51 +0100
commit75df844174e47bec91a3067ab50b16a9833fd5f0 (patch)
tree50770bc317f9cff020751ae4c8f6b1919423fd9b
parent601b4777257e9d1694bdc6987d2b63ee6ccf52dc (diff)
Allow applications to request exlcusive access to hosts
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java28
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/NodesSpecification.java20
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java8
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/HostResourceTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/VespaModelFactoryTest.java4
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/Capacity.java41
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java62
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java38
-rw-r--r--config-provisioning/src/test/java/com/yahoo/config/provision/AllocatedHostsTest.java4
-rw-r--r--config-provisioning/src/test/java/com/yahoo/config/provision/ClusterMembershipTest.java30
-rw-r--r--config-provisioning/src/test/java/com/yahoo/config/provision/HostFilterTest.java52
-rw-r--r--container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLogHandler.java2
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/application/Application.java18
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Allocation.java8
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java1
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Activator.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java9
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java12
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java74
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java12
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java22
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java13
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/PrioritizableNode.java1
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java22
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java10
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java11
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java21
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java8
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java8
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java12
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/monitoring/MetricsReporterTest.java5
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/SerializationTest.java6
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AclProvisioningTest.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java26
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationVisualizer.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java146
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisioningTest.java41
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java16
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizerTest.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java12
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java25
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningTest.java4
45 files changed, 562 insertions, 296 deletions
diff --git a/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java b/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java
index fb58a991012..2e1d507eea1 100644
--- a/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java
+++ b/config-model/src/main/java/com/yahoo/config/model/provision/InMemoryProvisioner.java
@@ -4,9 +4,24 @@ package com.yahoo.config.model.provision;
import com.yahoo.collections.ListMap;
import com.yahoo.collections.Pair;
import com.yahoo.config.model.api.HostProvisioner;
-import com.yahoo.config.provision.*;
-
-import java.util.*;
+import com.yahoo.config.provision.Capacity;
+import com.yahoo.config.provision.ClusterMembership;
+import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.HostSpec;
+import com.yahoo.config.provision.ProvisionLogger;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
/**
* In memory host provisioner. NB! ATM cannot be reused after allocate has been called.
@@ -111,11 +126,14 @@ public class InMemoryProvisioner implements HostProvisioner {
List<HostSpec> allocation = new ArrayList<>();
if (groups == 1) {
- allocation.addAll(allocateHostGroup(cluster, flavor, capacity, startIndexForClusters));
+ allocation.addAll(allocateHostGroup(cluster.with(Optional.of(ClusterSpec.Group.from(0))),
+ flavor,
+ capacity,
+ startIndexForClusters));
}
else {
for (int i = 0; i < groups; i++) {
- allocation.addAll(allocateHostGroup(cluster.changeGroup(Optional.of(ClusterSpec.Group.from(i))),
+ allocation.addAll(allocateHostGroup(cluster.with(Optional.of(ClusterSpec.Group.from(i))),
flavor,
capacity / groups,
allocation.size()));
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 61dee04a444..f7ed7241133 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
@@ -38,16 +38,20 @@ public class NodesSpecification {
/** The flavor the nodes should have, or empty to use the default */
private final Optional<String> flavor;
+ private final boolean exclusive;
+
/** The identifier of the custom docker image layer to use (not supported yet) */
private final Optional<String> dockerImage;
private NodesSpecification(boolean dedicated, int count, int groups, Version version, boolean required,
+ boolean exclusive,
Optional<String> flavor, Optional<String> dockerImage) {
this.dedicated = dedicated;
this.count = count;
this.groups = groups;
this.version = version;
this.required = required;
+ this.exclusive = exclusive;
this.flavor = flavor;
this.dockerImage = dockerImage;
}
@@ -58,6 +62,7 @@ public class NodesSpecification {
nodesElement.getIntegerAttribute("groups", 1),
version,
nodesElement.getBooleanAttribute("required", false),
+ nodesElement.getBooleanAttribute("exclusive", false),
Optional.ofNullable(nodesElement.getStringAttribute("flavor")),
Optional.ofNullable(nodesElement.getStringAttribute("docker-image")));
}
@@ -90,12 +95,14 @@ public class NodesSpecification {
if (parentElement == null) return Optional.empty();
ModelElement nodesElement = parentElement.getChild("nodes");
if (nodesElement == null) return Optional.empty();
- return Optional.of(new NodesSpecification(nodesElement.getBooleanAttribute("dedicated", false), version, nodesElement));
+ return Optional.of(new NodesSpecification(nodesElement.getBooleanAttribute("dedicated", false),
+ version, nodesElement));
}
/** Returns a requirement from <code>count</code> nondedicated nodes in one group */
public static NodesSpecification nonDedicated(int count, Version version) {
- return new NodesSpecification(false, count, 1, version, false, Optional.empty(), Optional.empty());
+ return new NodesSpecification(false, count, 1, version, false, false,
+ Optional.empty(), Optional.empty());
}
/**
@@ -104,6 +111,13 @@ public class NodesSpecification {
*/
public boolean isDedicated() { return dedicated; }
+ /**
+ * Returns whether the physical hosts running the nodes of this application can
+ * also run nodes of other applications. Using exclusive nodes for containers increases security
+ * and increases cost.
+ */
+ public boolean isExclusive() { return exclusive; }
+
/** Returns the number of nodes required */
public int count() { return count; }
@@ -111,7 +125,7 @@ public class NodesSpecification {
public int groups() { return groups; }
public Map<HostResource, ClusterMembership> provision(HostSystem hostSystem, ClusterSpec.Type clusterType, ClusterSpec.Id clusterId, DeployLogger logger) {
- ClusterSpec cluster = ClusterSpec.request(clusterType, clusterId, version);
+ ClusterSpec cluster = ClusterSpec.request(clusterType, clusterId, version, exclusive);
return hostSystem.allocateHosts(cluster, Capacity.fromNodeCount(count, flavor, required), groups, logger);
}
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 4caf0baf012..39aba9cfdf9 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
@@ -509,7 +509,10 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
return singleContentHost.get();
}
else { // request 1 node
- ClusterSpec clusterSpec = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from(cluster.getName()), context.getDeployState().getWantedNodeVespaVersion());
+ ClusterSpec clusterSpec = ClusterSpec.request(ClusterSpec.Type.container,
+ ClusterSpec.Id.from(cluster.getName()),
+ context.getDeployState().getWantedNodeVespaVersion(),
+ false);
return cluster.getHostSystem().allocateHosts(clusterSpec, Capacity.fromNodeCount(1), 1, logger).keySet().iterator().next();
}
} else {
@@ -531,7 +534,8 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
NodeType type = NodeType.valueOf(nodesElement.getAttribute("type"));
ClusterSpec clusterSpec = ClusterSpec.request(ClusterSpec.Type.container,
ClusterSpec.Id.from(cluster.getName()),
- context.getDeployState().getWantedNodeVespaVersion());
+ context.getDeployState().getWantedNodeVespaVersion(),
+ false);
Map<HostResource, ClusterMembership> hosts =
cluster.getRoot().getHostSystem().allocateHosts(clusterSpec,
Capacity.fromRequiredNodeType(type), 1, log);
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/HostResourceTest.java b/config-model/src/test/java/com/yahoo/vespa/model/HostResourceTest.java
index 1a0e9b9f810..28e3dd9be78 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/HostResourceTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/HostResourceTest.java
@@ -144,7 +144,7 @@ public class HostResourceTest {
}
private static ClusterSpec clusterSpec(ClusterSpec.Type type, String id) {
- return ClusterSpec.from(type, ClusterSpec.Id.from(id), ClusterSpec.Group.from(0), Version.fromString("6.42"));
+ return ClusterSpec.from(type, ClusterSpec.Id.from(id), ClusterSpec.Group.from(0), Version.fromString("6.42"), false);
}
private HostResource mockHostResource() {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/VespaModelFactoryTest.java b/config-model/src/test/java/com/yahoo/vespa/model/VespaModelFactoryTest.java
index b5934da3178..cabe9c0969b 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/VespaModelFactoryTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/VespaModelFactoryTest.java
@@ -117,7 +117,7 @@ public class VespaModelFactoryTest {
ClusterMembership.from(ClusterSpec.from(ClusterSpec.Type.admin,
new ClusterSpec.Id(routingClusterName),
ClusterSpec.Group.from(0),
- Version.fromString("6.42")),
+ Version.fromString("6.42"), false),
0));
}
@@ -128,7 +128,7 @@ public class VespaModelFactoryTest {
ClusterMembership.from(ClusterSpec.from(ClusterSpec.Type.container,
new ClusterSpec.Id(routingClusterName),
ClusterSpec.Group.from(0),
- Version.fromString("6.42")),
+ Version.fromString("6.42"), false),
0)));
}
};
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/Capacity.java b/config-provisioning/src/main/java/com/yahoo/config/provision/Capacity.java
index 9b1fb45a548..e061c5d07ee 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/Capacity.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/Capacity.java
@@ -6,7 +6,7 @@ import java.util.Optional;
/**
* A capacity request.
*
- * @author lulf
+ * @author Ulf Lilleengen
* @author bratseth
*/
public final class Capacity {
@@ -16,28 +16,28 @@ public final class Capacity {
private final boolean required;
private final Optional<String> flavor;
-
+
private final NodeType type;
- private Capacity(int nodeCount, boolean required, Optional<String> flavor, NodeType type) {
+ private Capacity(int nodeCount, Optional<String> flavor, boolean required, NodeType type) {
this.nodeCount = nodeCount;
- this.flavor = flavor;
this.required = required;
+ this.flavor = flavor;
this.type = type;
}
/** Returns the number of nodes requested */
public int nodeCount() { return nodeCount; }
- /** Returns whether the requested number of nodes must be met exactly for a request for this to succeed */
- public boolean isRequired() { return required; }
-
/**
* The node flavor requested, or empty if no particular flavor is specified.
* This may be satisfied by the requested flavor or a suitable replacement
*/
public Optional<String> flavor() { return flavor; }
+ /** Returns whether the requested number of nodes must be met exactly for a request for this to succeed */
+ public boolean isRequired() { return required; }
+
/**
* Returns the node type (role) requested. This is tenant nodes by default.
* If some other type is requested the node count and flavor may be ignored
@@ -52,37 +52,16 @@ public final class Capacity {
/** Creates this from a desired node count: The request may be satisfied with a smaller number of nodes. */
public static Capacity fromNodeCount(int capacity) {
- return fromNodeCount(capacity, Optional.empty());
- }
- /** Creates this from a desired node count: The request may be satisfied with a smaller number of nodes. */
- public static Capacity fromNodeCount(int nodeCount, String flavor) {
- return fromNodeCount(nodeCount, Optional.of(flavor));
- }
- /** Creates this from a desired node count: The request may be satisfied with a smaller number of nodes. */
- public static Capacity fromNodeCount(int nodeCount, Optional<String> flavor) {
- return new Capacity(nodeCount, false, flavor, NodeType.tenant);
- }
-
- /** Creates this from a required node count: Requests must fail unless the node count can be satisfied exactly */
- public static Capacity fromRequiredNodeCount(int nodeCount) {
- return fromRequiredNodeCount(nodeCount, Optional.empty());
- }
- /** Creates this from a required node count: Requests must fail unless the node count can be satisfied exactly */
- public static Capacity fromRequiredNodeCount(int nodeCount, String flavor) {
- return fromRequiredNodeCount(nodeCount, Optional.of(flavor));
- }
- /** Creates this from a required node count: Requests must fail unless the node count can be satisfied exactly */
- public static Capacity fromRequiredNodeCount(int nodeCount, Optional<String> flavor) {
- return new Capacity(nodeCount, true, flavor, NodeType.tenant);
+ return fromNodeCount(capacity, Optional.empty(), false);
}
public static Capacity fromNodeCount(int nodeCount, Optional<String> flavor, boolean required) {
- return new Capacity(nodeCount, required, flavor, NodeType.tenant);
+ return new Capacity(nodeCount, flavor, required, NodeType.tenant);
}
/** Creates this from a node type */
public static Capacity fromRequiredNodeType(NodeType type) {
- return new Capacity(0, true, Optional.empty(), type);
+ return new Capacity(0, Optional.empty(), true, type);
}
}
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java
index 3f0da396431..6c08a796c20 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java
@@ -4,8 +4,8 @@ package com.yahoo.config.provision;
import com.yahoo.component.Version;
/**
- * A node's membership in a cluster.
- * This is a value object.
+ * A node's membership in a cluster. This is a value object.
+ * The format is "clusterType/clusterId/groupId/index[/exclusive][/retired]
*
* @author bratseth
*/
@@ -19,26 +19,20 @@ public class ClusterMembership {
protected ClusterMembership() {}
private ClusterMembership(String stringValue, Version vespaVersion) {
- String restValue;
- if (stringValue.endsWith("/retired")) {
- retired = true;
- restValue = stringValue.substring(0, stringValue.length() - "/retired".length());
- }
- else {
- retired = false;
- restValue = stringValue;
- }
-
- String[] components = restValue.split("/");
-
- if ( components.length == 3) // Aug 2016: This should never happen any more
- initWithoutGroup(components, vespaVersion);
- else if (components.length == 4)
- initWithGroup(components, vespaVersion);
- else
+ String[] components = stringValue.split("/");
+ if (components.length < 4 || components.length > 6)
throw new RuntimeException("Could not parse '" + stringValue + "' to a cluster membership. " +
- "Expected 'id/type.index[/group]'");
+ "Expected 'clusterType/clusterId/groupId/index[/retired][/exclusive]'");
+ boolean exclusive = false;
+ if (components.length > 4) {
+ exclusive = components[4].equals("exclusive");
+ retired = components[components.length-1].equals("retired");
+ }
+
+ this.cluster = ClusterSpec.from(ClusterSpec.Type.valueOf(components[0]), ClusterSpec.Id.from(components[1]),
+ ClusterSpec.Group.from(Integer.valueOf(components[2])), vespaVersion, exclusive);
+ this.index = Integer.parseInt(components[3]);
this.stringValue = toStringValue();
}
@@ -49,23 +43,14 @@ public class ClusterMembership {
this.stringValue = toStringValue();
}
- private void initWithoutGroup(String[] components, Version vespaVersion) {
- this.cluster = ClusterSpec.request(ClusterSpec.Type.valueOf(components[0]),
- ClusterSpec.Id.from(components[1]),
- vespaVersion);
- this.index = Integer.parseInt(components[2]);
- }
-
- private void initWithGroup(String[] components, Version vespaVersion) {
- this.cluster = ClusterSpec.from(ClusterSpec.Type.valueOf(components[0]), ClusterSpec.Id.from(components[1]),
- ClusterSpec.Group.from(Integer.valueOf(components[2])), vespaVersion);
- this.index = Integer.parseInt(components[3]);
- }
-
protected String toStringValue() {
- return cluster.type().name() + "/" + cluster.id().value() +
- ( cluster.group().isPresent() ? "/" + cluster.group().get().index() : "") + "/" + index +
- ( retired ? "/retired" : "");
+ return cluster.type().name() +
+ "/" + cluster.id().value() +
+ (cluster.group().isPresent() ? "/" + cluster.group().get().index() : "") +
+ "/" + index +
+ ( cluster.isExclusive() ? "/exclusive" : "") +
+ ( retired ? "/retired" : "");
+
}
/** Returns the cluster this node is a member of */
@@ -87,7 +72,12 @@ public class ClusterMembership {
return new ClusterMembership(cluster, index, false);
}
+ // TODO: Remove after April 2018
public ClusterMembership changeCluster(ClusterSpec newCluster) {
+ return with(newCluster);
+ }
+
+ public ClusterMembership with(ClusterSpec newCluster) {
return new ClusterMembership(newCluster, index, retired);
}
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java
index bcc13f2c3e9..a0df2547631 100644
--- a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java
+++ b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java
@@ -22,11 +22,14 @@ public final class ClusterSpec {
private final Version vespaVersion;
- private ClusterSpec(Type type, Id id, Optional<Group> groupId, Version vespaVersion) {
+ private boolean exclusive;
+
+ private ClusterSpec(Type type, Id id, Optional<Group> groupId, Version vespaVersion, boolean exclusive) {
this.type = type;
this.id = id;
this.groupId = groupId;
this.vespaVersion = vespaVersion;
+ this.exclusive = exclusive;
}
/** Returns the cluster type */
@@ -40,13 +43,40 @@ public final class ClusterSpec {
/** Returns the group within the cluster this specifies, or empty to specify the whole cluster */
public Optional<Group> group() { return groupId; }
- public ClusterSpec changeGroup(Optional<Group> newGroup) { return new ClusterSpec(type, id, newGroup, vespaVersion); }
+ /**
+ * Returns whether the physical hosts running the nodes of this application can
+ * also run nodes of other applications. Using exclusive nodes for containers increases security
+ * and increases cost.
+ */
+ public boolean isExclusive() { return exclusive; }
+
+ // TODO: Remove after April 2018
+ public ClusterSpec changeGroup(Optional<Group> newGroup) {
+ return with(newGroup);
+ }
+
+ public ClusterSpec with(Optional<Group> newGroup) {
+ return new ClusterSpec(type, id, newGroup, vespaVersion, exclusive);
+ }
+
+ public ClusterSpec exclusive(boolean exclusive) {
+ return new ClusterSpec(type, id, groupId, vespaVersion, exclusive);
+ }
+ // TODO: Remove after April 2018
public static ClusterSpec request(Type type, Id id, Version vespaVersion) {
- return new ClusterSpec(type, id, Optional.empty(), vespaVersion);
+ return request(type, id, vespaVersion, false);
+ }
+ public static ClusterSpec request(Type type, Id id, Version vespaVersion, boolean exclusive) {
+ return new ClusterSpec(type, id, Optional.empty(), vespaVersion, exclusive);
}
+
+ // TODO: Remove after April 2018
public static ClusterSpec from(Type type, Id id, Group groupId, Version vespaVersion) {
- return new ClusterSpec(type, id, Optional.of(groupId), vespaVersion);
+ return new ClusterSpec(type, id, Optional.of(groupId), vespaVersion, false);
+ }
+ public static ClusterSpec from(Type type, Id id, Group groupId, Version vespaVersion, boolean exclusive) {
+ return new ClusterSpec(type, id, Optional.of(groupId), vespaVersion, exclusive);
}
@Override
diff --git a/config-provisioning/src/test/java/com/yahoo/config/provision/AllocatedHostsTest.java b/config-provisioning/src/test/java/com/yahoo/config/provision/AllocatedHostsTest.java
index 675af88596a..a39b8d3ef34 100644
--- a/config-provisioning/src/test/java/com/yahoo/config/provision/AllocatedHostsTest.java
+++ b/config-provisioning/src/test/java/com/yahoo/config/provision/AllocatedHostsTest.java
@@ -18,7 +18,7 @@ public class AllocatedHostsTest {
private final HostSpec h1 = new HostSpec("host1", Optional.empty());
private final HostSpec h2 = new HostSpec("host2", Optional.empty());
- private final HostSpec h3 = new HostSpec("host3", Optional.of(ClusterMembership.from("container/test/0", com.yahoo.component.Version.fromString("6.73.1"))));
+ private final HostSpec h3 = new HostSpec("host3", Optional.of(ClusterMembership.from("container/test/0/0", com.yahoo.component.Version.fromString("6.73.1"))));
@Test
public void testAllocatedHostsSerialization() throws IOException {
@@ -37,7 +37,7 @@ public class AllocatedHostsTest {
assertTrue(serializedAllocatedHosts.getHosts().contains(h2));
assertTrue(serializedAllocatedHosts.getHosts().contains(h3));
assertTrue(!getHost(h1.hostname(), serializedAllocatedHosts.getHosts()).membership().isPresent());
- assertEquals("container/test/0", getHost(h3.hostname(), serializedAllocatedHosts.getHosts()).membership().get().stringValue());
+ assertEquals("container/test/0/0", getHost(h3.hostname(), serializedAllocatedHosts.getHosts()).membership().get().stringValue());
assertEquals(h3.membership().get().cluster().vespaVersion(), getHost(h3.hostname(),
serializedAllocatedHosts.getHosts()).membership().get().cluster().vespaVersion());
}
diff --git a/config-provisioning/src/test/java/com/yahoo/config/provision/ClusterMembershipTest.java b/config-provisioning/src/test/java/com/yahoo/config/provision/ClusterMembershipTest.java
index bc8586d5c52..f1970326137 100644
--- a/config-provisioning/src/test/java/com/yahoo/config/provision/ClusterMembershipTest.java
+++ b/config-provisioning/src/test/java/com/yahoo/config/provision/ClusterMembershipTest.java
@@ -18,30 +18,20 @@ public class ClusterMembershipTest {
@Test
public void testContainerServiceInstance() {
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("id1"), Version.fromString("6.42"));
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("id1"), Version.fromString("6.42"), false);
assertContainerService(ClusterMembership.from(cluster, 3));
}
@Test
- public void testContainerServiceInstanceFromString() {
- assertContainerService(ClusterMembership.from("container/id1/3", Vtag.currentVersion));
- }
-
- @Test
public void testServiceInstance() {
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("id1"), Version.fromString("6.42"));
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("id1"), Version.fromString("6.42"), false);
assertContentService(ClusterMembership.from(cluster, 37));
}
@Test
- public void testServiceInstanceFromString() {
- assertContentService(ClusterMembership.from("content/id1/37", Vtag.currentVersion));
- }
-
- @Test
public void testServiceInstanceWithGroup() {
ClusterSpec cluster = ClusterSpec.from(ClusterSpec.Type.content, ClusterSpec.Id.from("id1"),
- ClusterSpec.Group.from(4), Version.fromString("6.42"));
+ ClusterSpec.Group.from(4), Version.fromString("6.42"), false);
assertContentServiceWithGroup(ClusterMembership.from(cluster, 37));
}
@@ -52,19 +42,14 @@ public class ClusterMembershipTest {
@Test
public void testServiceInstanceWithRetire() {
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("id1"), Version.fromString("6.42"));
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("id1"), Version.fromString("6.42"), false);
assertContentServiceWithRetire(ClusterMembership.retiredFrom(cluster, 37));
}
@Test
- public void testServiceInstanceWithRetireFromString() {
- assertContentServiceWithRetire(ClusterMembership.from("content/id1/37/retired", Vtag.currentVersion));
- }
-
- @Test
public void testServiceInstanceWithGroupAndRetire() {
ClusterSpec cluster = ClusterSpec.from(ClusterSpec.Type.content, ClusterSpec.Id.from("id1"),
- ClusterSpec.Group.from(4), Version.fromString("6.42"));
+ ClusterSpec.Group.from(4), Version.fromString("6.42"), false);
assertContentServiceWithGroupAndRetire(ClusterMembership.retiredFrom(cluster, 37));
}
@@ -76,7 +61,7 @@ public class ClusterMembershipTest {
private void assertContainerService(ClusterMembership instance) {
assertEquals(ClusterSpec.Type.container, instance.cluster().type());
assertEquals("id1", instance.cluster().id().value());
- assertEquals(Optional.<ClusterSpec.Group>empty(), instance.cluster().group());
+ assertFalse(instance.cluster().group().isPresent());
assertEquals(3, instance.index());
assertEquals("container/id1/3", instance.stringValue());
}
@@ -84,7 +69,7 @@ public class ClusterMembershipTest {
private void assertContentService(ClusterMembership instance) {
assertEquals(ClusterSpec.Type.content, instance.cluster().type());
assertEquals("id1", instance.cluster().id().value());
- assertFalse("gr4", instance.cluster().group().isPresent());
+ assertFalse(instance.cluster().group().isPresent());
assertEquals(37, instance.index());
assertFalse(instance.retired());
assertEquals("content/id1/37", instance.stringValue());
@@ -99,6 +84,7 @@ public class ClusterMembershipTest {
assertEquals("content/id1/4/37", instance.stringValue());
}
+ /** Serializing a spec without a group assigned works, but not deserialization */
private void assertContentServiceWithRetire(ClusterMembership instance) {
assertEquals(ClusterSpec.Type.content, instance.cluster().type());
assertEquals("id1", instance.cluster().id().value());
diff --git a/config-provisioning/src/test/java/com/yahoo/config/provision/HostFilterTest.java b/config-provisioning/src/test/java/com/yahoo/config/provision/HostFilterTest.java
index adc7105b2a6..e1d6f061446 100644
--- a/config-provisioning/src/test/java/com/yahoo/config/provision/HostFilterTest.java
+++ b/config-provisioning/src/test/java/com/yahoo/config/provision/HostFilterTest.java
@@ -24,25 +24,25 @@ public class HostFilterTest {
HostFilter type = HostFilter.clusterType(ClusterSpec.Type.content);
HostFilter id = HostFilter.clusterId(ClusterSpec.Id.from("type1"));
- assertTrue( all.matches("anyhost", "flavor", membership("container/anytype/0")));
- assertFalse(hostname.matches("anyhost", "flavor", membership("container/anytype/0")));
- assertFalse(type.matches("anyhost", "flavor", membership("container/anytype/0")));
- assertFalse(id.matches("anyhost", "flavor", membership("container/anytype/0")));
+ assertTrue( all.matches("anyhost", "flavor", membership("container/anytype/0/0")));
+ assertFalse(hostname.matches("anyhost", "flavor", membership("container/anytype/0/0")));
+ assertFalse(type.matches("anyhost", "flavor", membership("container/anytype/0/0")));
+ assertFalse(id.matches("anyhost", "flavor", membership("container/anytype/0/0")));
- assertTrue( all.matches("anyhost", "flavor", membership("content/anytype/0")));
- assertFalse(hostname.matches("anyhost", "flavor", membership("content/anytype/0")));
- assertTrue( type.matches("anyhost", "flavor", membership("content/anytype/0")));
- assertFalse( id.matches("anyhost", "flavor", membership("content/anytype/0")));
+ assertTrue( all.matches("anyhost", "flavor", membership("content/anytype/0/0")));
+ assertFalse(hostname.matches("anyhost", "flavor", membership("content/anytype/0/0")));
+ assertTrue( type.matches("anyhost", "flavor", membership("content/anytype/0/0")));
+ assertFalse( id.matches("anyhost", "flavor", membership("content/anytype/0/0")));
- assertTrue( all.matches("host1", "flavor", membership("content/anytype/0")));
- assertTrue( hostname.matches("host1", "flavor", membership("content/anytype/0")));
- assertTrue( type.matches("host1", "flavor", membership("content/anytype/0")));
- assertFalse( id.matches("host1", "flavor", membership("content/anytype/0")));
+ assertTrue( all.matches("host1", "flavor", membership("content/anytype/0/0")));
+ assertTrue( hostname.matches("host1", "flavor", membership("content/anytype/0/0")));
+ assertTrue( type.matches("host1", "flavor", membership("content/anytype/0/0")));
+ assertFalse( id.matches("host1", "flavor", membership("content/anytype/0/0")));
- assertTrue( all.matches("host1", "flavor", membership("content/type1/0")));
- assertTrue( hostname.matches("host1", "flavor", membership("content/type1/0")));
- assertTrue( type.matches("host1", "flavor", membership("content/type1/0")));
- assertTrue( id.matches("host1", "flavor", membership("content/type1/0")));
+ assertTrue( all.matches("host1", "flavor", membership("content/type1/0/0")));
+ assertTrue( hostname.matches("host1", "flavor", membership("content/type1/0/0")));
+ assertTrue( type.matches("host1", "flavor", membership("content/type1/0/0")));
+ assertTrue( id.matches("host1", "flavor", membership("content/type1/0/0")));
}
@Test
@@ -52,22 +52,22 @@ public class HostFilterTest {
Collections.singletonList(ClusterSpec.Type.content),
Collections.singletonList(ClusterSpec.Id.from("type1")));
- assertFalse(typeAndId.matches("anyhost", "flavor", membership("content/anyType/0")));
- assertFalse(typeAndId.matches("anyhost", "flavor", membership("container/type1/0")));
- assertTrue(typeAndId.matches("anyhost", "flavor", membership("content/type1/0")));
+ assertFalse(typeAndId.matches("anyhost", "flavor", membership("content/anyType/0/0")));
+ assertFalse(typeAndId.matches("anyhost", "flavor", membership("container/type1/0/0")));
+ assertTrue(typeAndId.matches("anyhost", "flavor", membership("content/type1/0/0")));
}
@Test
public void testMultiConditionFilterFromStrings() {
HostFilter typeAndId = HostFilter.from("host1 host2, host3,host4", " , ,flavor", null, "type1 ");
- assertFalse(typeAndId.matches("anotherhost", "flavor", membership("content/type1/0")));
- assertTrue(typeAndId.matches("host1", "flavor", membership("content/type1/0")));
- assertTrue(typeAndId.matches("host2", "flavor", membership("content/type1/0")));
- assertTrue(typeAndId.matches("host3", "flavor", membership("content/type1/0")));
- assertTrue(typeAndId.matches("host4", "flavor", membership("content/type1/0")));
- assertFalse(typeAndId.matches("host1", "flavor", membership("content/type2/0")));
- assertFalse(typeAndId.matches("host4", "differentflavor", membership("content/type1/0")));
+ assertFalse(typeAndId.matches("anotherhost", "flavor", membership("content/type1/0/0")));
+ assertTrue(typeAndId.matches("host1", "flavor", membership("content/type1/0/0")));
+ assertTrue(typeAndId.matches("host2", "flavor", membership("content/type1/0/0")));
+ assertTrue(typeAndId.matches("host3", "flavor", membership("content/type1/0/0")));
+ assertTrue(typeAndId.matches("host4", "flavor", membership("content/type1/0/0")));
+ assertFalse(typeAndId.matches("host1", "flavor", membership("content/type2/0/0")));
+ assertFalse(typeAndId.matches("host4", "differentflavor", membership("content/type1/0/0")));
}
private Optional<ClusterMembership> membership(String membershipString) {
diff --git a/container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLogHandler.java b/container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLogHandler.java
index 5d56ed13e21..a31e7b3b849 100644
--- a/container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLogHandler.java
+++ b/container-accesslogging/src/main/java/com/yahoo/container/logging/AccessLogHandler.java
@@ -8,7 +8,7 @@ import static com.yahoo.container.core.AccessLogConfig.FileHandler.RotateScheme.
import java.util.logging.Logger;
/**
- * @author <a href="mailto:borud@yahoo-inc.com">Bjorn Borud</a>
+ * @author Bjorn Borud
*/
class AccessLogHandler {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/application/Application.java b/jdisc_core/src/main/java/com/yahoo/jdisc/application/Application.java
index 98e107ee351..74546951f15 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/application/Application.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/application/Application.java
@@ -7,35 +7,35 @@ import com.yahoo.jdisc.service.ClientProvider;
import com.yahoo.jdisc.service.ServerProvider;
/**
- * <p>This interface defines the API of the singleton Application that runs in a jDISC instance. An Application instance
+ * This interface defines the API of the singleton Application that runs in a jDISC instance. An Application instance
* will always have its {@link #destroy()} method called, regardless of whether {@link #start()} or {@link #stop()}
- * threw any exceptions.</p>
+ * threw any exceptions.
*
- * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ * @author Simon Thoresen
*/
public interface Application {
/**
- * <p>This method is called by the {@link ApplicationLoader} just after creating this Application instance. Use this
+ * This method is called by the {@link ApplicationLoader} just after creating this Application instance. Use this
* method to start the Application's worker thread, and to activate a {@link Container}. If you attempt to call
* {@link ContainerActivator#activateContainer(ContainerBuilder)} before this method is invoked, that call will
* throw an {@link ApplicationNotReadyException}. If this method does not throw an exception, the {@link #stop()}
- * method will be called at some time in the future.</p>
+ * method will be called at some time in the future.
*/
void start();
/**
- * <p>This method is called by the {@link ApplicationLoader} after the corresponding signal has been issued by the
+ * This method is called by the {@link ApplicationLoader} after the corresponding signal has been issued by the
* controlling start script. Once this method returns, all calls to {@link
* ContainerActivator#activateContainer(ContainerBuilder)} will throw {@link ApplicationNotReadyException}s. Use
- * this method to prepare for termination (see {@link #destroy()}).</p>
+ * this method to prepare for termination (see {@link #destroy()}).
*/
void stop();
/**
- * <p>This method is called by the {@link ApplicationLoader} after first calling {@link #stop()}, and all previous
+ * This method is called by the {@link ApplicationLoader} after first calling {@link #stop()}, and all previous
* {@link DeactivatedContainer}s have terminated. Use this method to shut down all Application components such as
- * {@link ClientProvider}s and {@link ServerProvider}s.</p>
+ * {@link ClientProvider}s and {@link ServerProvider}s.
*/
void destroy();
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Allocation.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Allocation.java
index 92c5fab6dc4..985a13d3511 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Allocation.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Allocation.java
@@ -43,10 +43,14 @@ public class Allocation {
public Generation restartGeneration() { return restartGeneration; }
/** Returns a copy of this which is retired */
- public Allocation retire() { return new Allocation(owner, clusterMembership.retire(), restartGeneration, removable); }
+ public Allocation retire() {
+ return new Allocation(owner, clusterMembership.retire(), restartGeneration, removable);
+ }
/** Returns a copy of this which is not retired */
- public Allocation unretire() { return new Allocation(owner, clusterMembership.unretire(), restartGeneration, removable); }
+ public Allocation unretire() {
+ return new Allocation(owner, clusterMembership.unretire(), restartGeneration, removable);
+ }
/** Return whether this node is ready to be removed from the application */
public boolean isRemovable() { return removable; }
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java
index 110313de197..d164252b018 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java
@@ -328,6 +328,7 @@ public class NodeSerializer {
default : throw new IllegalArgumentException("Unknown node type '" + typeString + "'");
}
}
+
private String toString(NodeType type) {
switch (type) {
case tenant: return "tenant";
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Activator.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Activator.java
index 5ffa99e0de1..4ba272c2366 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Activator.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Activator.java
@@ -24,11 +24,9 @@ import java.util.stream.Collectors;
class Activator {
private final NodeRepository nodeRepository;
- private final Clock clock;
- public Activator(NodeRepository nodeRepository, Clock clock) {
+ public Activator(NodeRepository nodeRepository) {
this.nodeRepository = nodeRepository;
- this.clock = clock;
}
/**
@@ -97,7 +95,7 @@ class Activator {
List<Node> updated = new ArrayList<>();
for (Node node : nodes) {
HostSpec hostSpec = getHost(node.hostname(), hosts);
- node = hostSpec.membership().get().retired() ? node.retire(clock.instant()) : node.unretire();
+ node = hostSpec.membership().get().retired() ? node.retire(nodeRepository.clock().instant()) : node.unretire();
node = node.with(node.allocation().get().with(hostSpec.membership().get()));
if (hostSpec.flavor().isPresent()) // Docker nodes may change flavor
node = node.with(hostSpec.flavor().get());
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java
index 3f392674b20..e9b86a9850b 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java
@@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.provision.provisioning;
import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.Zone;
import com.yahoo.config.provision.Flavor;
@@ -55,6 +56,14 @@ public class CapacityPolicies {
}
/**
+ * Whether or not the nodes requested can share physical host with other applications.
+ * A security feature which only makes sense for prod.
+ */
+ public boolean decideExclusivity(boolean requestedExclusivity) {
+ return requestedExclusivity && zone.environment() == Environment.prod;
+ }
+
+ /**
* Throw if the node count is 1
*
* @return the argument node count
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
index 49d802c7058..93704d244b5 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java
@@ -20,11 +20,9 @@ import java.util.List;
public class GroupPreparer {
private final NodeRepository nodeRepository;
- private final Clock clock;
- public GroupPreparer(NodeRepository nodeRepository, Clock clock) {
+ public GroupPreparer(NodeRepository nodeRepository) {
this.nodeRepository = nodeRepository;
- this.clock = clock;
}
/**
@@ -65,7 +63,7 @@ public class GroupPreparer {
prioritizer.addNewDockerNodes();
// Allocate from the prioritized list
- NodeAllocation allocation = new NodeAllocation(application, cluster, requestedNodes, highestIndex, clock);
+ NodeAllocation allocation = new NodeAllocation(application, cluster, requestedNodes, highestIndex, nodeRepository);
allocation.offer(prioritizer.prioritize());
if (! allocation.fullfilled())
throw new OutOfCapacityException("Could not satisfy " + requestedNodes + " for " + cluster +
@@ -84,9 +82,11 @@ public class GroupPreparer {
}
private String outOfCapacityDetails(NodeAllocation allocation) {
- if (allocation.wouldBeFulfilledWithClashingParentHost())
+ if (allocation.wouldBeFulfilledWithoutExclusivity())
+ return ": Not enough nodes available due to host exclusivity constraints.";
+ else if (allocation.wouldBeFulfilledWithClashingParentHost())
return ": Not enough nodes available on separate physical hosts.";
- if (allocation.wouldBeFulfilledWithRetiredNodes())
+ else if (allocation.wouldBeFulfilledWithRetiredNodes())
return ": Not enough nodes available due to retirement.";
else
return ".";
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java
index 21ed0d983f3..4df8b5ba04f 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeAllocation.java
@@ -6,15 +6,17 @@ import com.yahoo.config.provision.ClusterMembership;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.lang.MutableInteger;
import com.yahoo.vespa.hosted.provision.Node;
+import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.Agent;
+import com.yahoo.vespa.hosted.provision.node.Allocation;
-import java.time.Clock;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@@ -44,6 +46,9 @@ class NodeAllocation {
/** The number of nodes rejected because of clashing parentHostname */
private int rejectedWithClashingParentHost = 0;
+ /** The number of nodes rejected due to exclusivity constraints */
+ private int rejectedDueToExclusivity = 0;
+
/** The number of nodes that just now was changed to retired */
private int wasRetiredJustNow = 0;
@@ -53,15 +58,15 @@ class NodeAllocation {
/** The next membership index to assign to a new node */
private final MutableInteger highestIndex;
- /** Used to record event timestamps **/
- private final Clock clock;
+ private final NodeRepository nodeRepository;
- NodeAllocation(ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes, MutableInteger highestIndex, Clock clock) {
+ NodeAllocation(ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes, MutableInteger highestIndex,
+ NodeRepository nodeRepository) {
this.application = application;
this.cluster = cluster;
this.requestedNodes = requestedNodes;
this.highestIndex = highestIndex;
- this.clock = clock;
+ this.nodeRepository = nodeRepository;
}
/**
@@ -93,6 +98,7 @@ class NodeAllocation {
if ( ! hasCompatibleFlavor(offered)) wantToRetireNode = true;
if ( offered.flavor().isRetired()) wantToRetireNode = true;
if ( offered.status().wantToRetire()) wantToRetireNode = true;
+ if ( requestedNodes.isExclusive() && ! hostHasOnly(application, offered.parentHostname())) wantToRetireNode = true;
if (( ! saturated() && hasCompatibleFlavor(offered)) || acceptToRetire(offered) ) {
accepted.add(acceptNode(offeredPriority, wantToRetireNode));
@@ -103,13 +109,23 @@ class NodeAllocation {
++rejectedWithClashingParentHost;
continue;
}
+ if ( ! hostCanHost(application, offered.parentHostname())) {
+ ++rejectedDueToExclusivity;
+ continue;
+ }
+ if ( requestedNodes.isExclusive() && ! hostHasOnly(application, offered.parentHostname())) {
+ ++rejectedDueToExclusivity;
+ continue;
+ }
if (offered.flavor().isRetired()) {
continue;
}
if (offered.status().wantToRetire()) {
continue;
}
- offeredPriority.node = offered.allocate(application, ClusterMembership.from(cluster, highestIndex.add(1)), clock.instant());
+ offeredPriority.node = offered.allocate(application,
+ ClusterMembership.from(cluster, highestIndex.add(1)),
+ nodeRepository.clock().instant());
accepted.add(acceptNode(offeredPriority, false));
}
}
@@ -128,6 +144,33 @@ class NodeAllocation {
}
/**
+ * If a parent host is given, and it hosts another application which requires exclusive access to the physical
+ * host, then we cannot host this application on it.
+ */
+ private boolean hostCanHost(ApplicationId application, Optional<String> parentHostname) {
+ if ( ! parentHostname.isPresent()) return true;
+ for (Node nodeOnHost : nodeRepository.getChildNodes(parentHostname.get())) {
+ if ( ! nodeOnHost.allocation().isPresent()) continue;
+
+ if ( nodeOnHost.allocation().get().membership().cluster().isExclusive() &&
+ ! nodeOnHost.allocation().get().owner().equals(application))
+ return false;
+ }
+ return true;
+ }
+
+ private boolean hostHasOnly(ApplicationId application, Optional<String> parentHostname) {
+ if ( ! parentHostname.isPresent()) return true; // yes, as host is exclusive
+
+ for (Node nodeOnHost : nodeRepository.getChildNodes(parentHostname.get())) {
+ if ( ! nodeOnHost.allocation().isPresent()) continue;
+ if ( ! nodeOnHost.allocation().get().owner().equals(application))
+ return false;
+ }
+ return true;
+ }
+
+ /**
* Returns whether this node should be accepted into the cluster even if it is not currently desired
* (already enough nodes, or wrong flavor).
* Such nodes will be marked retired during finalization of the list of accepted nodes.
@@ -166,7 +209,7 @@ class NodeAllocation {
} else {
++wasRetiredJustNow;
// Retire nodes which are of an unwanted flavor, retired flavor or have an overlapping parent host
- node = node.retire(clock.instant());
+ node = node.retire(nodeRepository.clock().instant());
prioritizableNode.node= node;
}
if ( ! node.allocation().get().membership().cluster().equals(cluster)) {
@@ -181,7 +224,7 @@ class NodeAllocation {
}
private Node setCluster(ClusterSpec cluster, Node node) {
- ClusterMembership membership = node.allocation().get().membership().changeCluster(cluster);
+ ClusterMembership membership = node.allocation().get().membership().with(cluster);
return node.with(node.allocation().get().with(membership));
}
@@ -203,6 +246,10 @@ class NodeAllocation {
return requestedNodes.fulfilledBy(acceptedOfRequestedFlavor + rejectedWithClashingParentHost);
}
+ boolean wouldBeFulfilledWithoutExclusivity() {
+ return requestedNodes.fulfilledBy(acceptedOfRequestedFlavor + rejectedDueToExclusivity);
+ }
+
/**
* Make the number of <i>non-retired</i> nodes in the list equal to the requested number
* of nodes, and retire the rest of the list. Only retire currently active nodes.
@@ -219,7 +266,7 @@ class NodeAllocation {
if (deltaRetiredCount > 0) { // retire until deltaRetiredCount is 0, prefer to retire higher indexes to minimize redistribution
for (PrioritizableNode node : byDecreasingIndex(nodes)) {
if ( ! node.node.allocation().get().membership().retired() && node.node.state().equals(Node.State.active)) {
- node.node = node.node.retire(Agent.application, clock.instant());
+ node.node = node.node.retire(Agent.application, nodeRepository.clock().instant());
surplusNodes.add(node.node); // offer this node to other groups
if (--deltaRetiredCount == 0) break;
}
@@ -234,10 +281,13 @@ class NodeAllocation {
}
}
- // Update flavor of allocated docker nodes as we can change it in place
for (PrioritizableNode node : nodes) {
- if (node.node.allocation().isPresent())
- node.node = requestedNodes.assignRequestedFlavor(node.node);
+ node.node = requestedNodes.assignRequestedFlavor(node.node);
+
+ // Set whether the node is exclusive
+ Allocation allocation = node.node.allocation().get();
+ node.node = node.node.with(allocation.with(allocation.membership()
+ .with(allocation.membership().cluster().exclusive(requestedNodes.isExclusive()))));
}
return nodes.stream().map(n -> n.node).collect(Collectors.toList());
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java
index f45c10f97f1..01651293c81 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java
@@ -56,17 +56,17 @@ public class NodeRepositoryProvisioner implements Provisioner {
@Inject
public NodeRepositoryProvisioner(NodeRepository nodeRepository, NodeFlavors flavors, Zone zone) {
- this(nodeRepository, flavors, zone, Clock.systemUTC(), (x, y) -> {});
+ this(nodeRepository, flavors, zone, (x, y) -> {});
}
- public NodeRepositoryProvisioner(NodeRepository nodeRepository, NodeFlavors flavors, Zone zone, Clock clock, BiConsumer<List<Node>, String> debugRecorder) {
+ public NodeRepositoryProvisioner(NodeRepository nodeRepository, NodeFlavors flavors, Zone zone, BiConsumer<List<Node>, String> debugRecorder) {
this.nodeRepository = nodeRepository;
this.capacityPolicies = new CapacityPolicies(zone, flavors);
this.zone = zone;
- this.preparer = new Preparer(nodeRepository, clock, zone.environment().equals(Environment.prod)
+ this.preparer = new Preparer(nodeRepository, zone.environment().equals(Environment.prod)
? SPARE_CAPACITY_PROD
: SPARE_CAPACITY_NONPROD);
- this.activator = new Activator(nodeRepository, clock);
+ this.activator = new Activator(nodeRepository);
this.debugRecorder = debugRecorder;
}
@@ -95,8 +95,9 @@ public class NodeRepositoryProvisioner implements Provisioner {
Optional<String> defaultFlavorOverride = nodeRepository.getDefaultFlavorOverride(application);
Flavor flavor = capacityPolicies.decideFlavor(requestedCapacity, cluster, defaultFlavorOverride);
log.log(LogLevel.DEBUG, () -> "Decided flavor for requested tenant nodes: " + flavor);
+ boolean exclusive = capacityPolicies.decideExclusivity(cluster.isExclusive());
effectiveGroups = wantedGroups > nodeCount ? nodeCount : wantedGroups; // cannot have more groups than nodes
- requestedNodes = NodeSpec.from(nodeCount, flavor);
+ requestedNodes = NodeSpec.from(nodeCount, flavor, exclusive);
}
else {
requestedNodes = NodeSpec.from(requestedCapacity.type());
@@ -132,4 +133,5 @@ public class NodeRepositoryProvisioner implements Provisioner {
}
return hosts;
}
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java
index dc3f4a64421..b2572a781fe 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java
@@ -19,6 +19,12 @@ public interface NodeSpec {
/** The node type this requests */
NodeType type();
+ /**
+ * Returns whether the physical hosts running the nodes of this application can
+ * also run nodes of other applications.
+ */
+ boolean isExclusive();
+
/** Returns whether the given flavor is compatible with this spec */
boolean isCompatible(Flavor flavor);
@@ -47,8 +53,8 @@ public interface NodeSpec {
*/
Node assignRequestedFlavor(Node node);
- static NodeSpec from(int nodeCount, Flavor flavor) {
- return new CountNodeSpec(nodeCount, flavor);
+ static NodeSpec from(int nodeCount, Flavor flavor, boolean exclusive) {
+ return new CountNodeSpec(nodeCount, flavor, exclusive);
}
static NodeSpec from(NodeType type) {
@@ -60,11 +66,13 @@ public interface NodeSpec {
private final int count;
private final Flavor requestedFlavor;
+ private final boolean exclusive;
- public CountNodeSpec(int count, Flavor flavor) {
+ public CountNodeSpec(int count, Flavor flavor, boolean exclusive) {
Objects.requireNonNull(flavor, "A flavor must be specified");
this.count = count;
this.requestedFlavor = flavor;
+ this.exclusive = exclusive;
}
// TODO: Remove usage of this
@@ -76,6 +84,9 @@ public interface NodeSpec {
public int getCount() { return count; }
@Override
+ public boolean isExclusive() { return exclusive; }
+
+ @Override
public NodeType type() { return NodeType.tenant; }
@Override
@@ -100,7 +111,7 @@ public interface NodeSpec {
public int idealRetiredCount(int acceptedCount, int currentRetiredCount) { return acceptedCount - this.count; }
@Override
- public NodeSpec fraction(int divisor) { return new CountNodeSpec(count/divisor, requestedFlavor); }
+ public NodeSpec fraction(int divisor) { return new CountNodeSpec(count/divisor, requestedFlavor, exclusive); }
@Override
public Node assignRequestedFlavor(Node node) {
@@ -137,6 +148,9 @@ public interface NodeSpec {
public NodeType type() { return type; }
@Override
+ public boolean isExclusive() { return false; }
+
+ @Override
public boolean isCompatible(Flavor flavor) { return true; }
@Override
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java
index e7a6014816e..969b1d0a687 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java
@@ -9,7 +9,6 @@ import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.Agent;
-import java.time.Clock;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
@@ -23,15 +22,13 @@ import java.util.Optional;
class Preparer {
private final NodeRepository nodeRepository;
- private final Clock clock;
private final GroupPreparer groupPreparer;
private final int spareCount;
- public Preparer(NodeRepository nodeRepository, Clock clock, int spareCount) {
+ public Preparer(NodeRepository nodeRepository, int spareCount) {
this.nodeRepository = nodeRepository;
- this.clock = clock;
this.spareCount = spareCount;
- this.groupPreparer = new GroupPreparer(nodeRepository, clock);
+ this.groupPreparer = new GroupPreparer(nodeRepository);
}
/**
@@ -48,7 +45,7 @@ class Preparer {
MutableInteger highestIndex = new MutableInteger(findHighestIndex(application, cluster));
List<Node> acceptedNodes = new ArrayList<>();
for (int groupIndex = 0; groupIndex < wantedGroups; groupIndex++) {
- ClusterSpec clusterGroup = cluster.changeGroup(Optional.of(ClusterSpec.Group.from(groupIndex)));
+ ClusterSpec clusterGroup = cluster.with(Optional.of(ClusterSpec.Group.from(groupIndex)));
List<Node> accepted = groupPreparer.prepare(application, clusterGroup,
requestedNodes.fraction(wantedGroups), surplusNodes,
highestIndex, spareCount);
@@ -83,7 +80,7 @@ class Preparer {
ClusterSpec cluster = membership.cluster();
if (cluster.group().get().index() >= wantedGroups) {
ClusterSpec.Group newGroup = targetGroup.orElse(ClusterSpec.Group.from(0));
- ClusterMembership newGroupMembership = membership.changeCluster(cluster.changeGroup(Optional.of(newGroup)));
+ ClusterMembership newGroupMembership = membership.with(cluster.with(Optional.of(newGroup)));
i.set(node.with(node.allocation().get().with(newGroupMembership)));
}
}
@@ -122,7 +119,7 @@ class Preparer {
List<Node> retired = new ArrayList<>(nodes.size());
for (Node node : nodes) {
if ( ! node.allocation().get().isRemovable())
- retired.add(node.retire(Agent.application, clock.instant()));
+ retired.add(node.retire(Agent.application, nodeRepository.clock().instant()));
}
return retired;
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/PrioritizableNode.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/PrioritizableNode.java
index 807fbfae1c9..40f76125064 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/PrioritizableNode.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/PrioritizableNode.java
@@ -99,4 +99,5 @@ class PrioritizableNode implements Comparable<PrioritizableNode> {
if (nodePri.isNewNode) return false;
return nodePri.node.state().equals(Node.State.reserved);
}
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
index b7392f3aee6..5386e3f07ff 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java
@@ -111,21 +111,31 @@ public class MockNodeRepository extends NodeRepository {
ApplicationId zoneApp = ApplicationId.from(TenantName.from("zoneapp"), ApplicationName.from("zoneapp"), InstanceName.from("zoneapp"));
ClusterSpec zoneCluster = ClusterSpec.request(ClusterSpec.Type.container,
- ClusterSpec.Id.from("node-admin"),
- Version.fromString("6.42"));
+ ClusterSpec.Id.from("node-admin"),
+ Version.fromString("6.42"),
+ false);
activate(provisioner.prepare(zoneApp, zoneCluster, Capacity.fromRequiredNodeType(NodeType.host), 1, null), zoneApp, provisioner);
ApplicationId app1 = ApplicationId.from(TenantName.from("tenant1"), ApplicationName.from("application1"), InstanceName.from("instance1"));
- ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("id1"), Version.fromString("6.42"));
+ ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.container,
+ ClusterSpec.Id.from("id1"),
+ Version.fromString("6.42"),
+ false);
provisioner.prepare(app1, cluster1, Capacity.fromNodeCount(2), 1, null);
ApplicationId app2 = ApplicationId.from(TenantName.from("tenant2"), ApplicationName.from("application2"), InstanceName.from("instance2"));
- ClusterSpec cluster2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("id2"), Version.fromString("6.42"));
+ ClusterSpec cluster2 = ClusterSpec.request(ClusterSpec.Type.content,
+ ClusterSpec.Id.from("id2"),
+ Version.fromString("6.42"),
+ false);
activate(provisioner.prepare(app2, cluster2, Capacity.fromNodeCount(2), 1, null), app2, provisioner);
ApplicationId app3 = ApplicationId.from(TenantName.from("tenant3"), ApplicationName.from("application3"), InstanceName.from("instance3"));
- ClusterSpec cluster3 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("id3"), Version.fromString("6.42"));
- activate(provisioner.prepare(app3, cluster3, Capacity.fromNodeCount(2, "docker"), 1, null), app3, provisioner);
+ ClusterSpec cluster3 = ClusterSpec.request(ClusterSpec.Type.content,
+ ClusterSpec.Id.from("id3"),
+ Version.fromString("6.42"),
+ false);
+ activate(provisioner.prepare(app3, cluster3, Capacity.fromNodeCount(2, Optional.of("docker"), false), 1, null), app3, provisioner);
}
private void activate(List<HostSpec> hosts, ApplicationId application, NodeRepositoryProvisioner provisioner) {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java
index 7c064c5faf1..18881c11a64 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java
@@ -218,7 +218,7 @@ public class FailedExpirerTest {
this.nodeRepository = new NodeRepository(nodeFlavors, curator, clock, zone,
new MockNameResolver().mockAnyLookup(),
new DockerImage("docker-image"));
- this.provisioner = new NodeRepositoryProvisioner(nodeRepository, nodeFlavors, Zone.defaultZone(), clock,
+ this.provisioner = new NodeRepositoryProvisioner(nodeRepository, nodeFlavors, Zone.defaultZone(),
(x, y) -> {});
this.expirer = new FailedExpirer(nodeRepository, zone, clock, Duration.ofMinutes(30),
new JobControl(nodeRepository.database()));
@@ -289,9 +289,11 @@ public class FailedExpirerTest {
Set<HostSpec> hosts = Stream.of(hostname)
.map(h -> new HostSpec(h, Optional.empty()))
.collect(Collectors.toSet());
- ClusterSpec clusterSpec = ClusterSpec.request(clusterType, ClusterSpec.Id.from("test"),
- Version.fromString("6.42"));
- provisioner.prepare(applicationId, clusterSpec, Capacity.fromNodeCount(hostname.length, flavor.name()),
+ ClusterSpec clusterSpec = ClusterSpec.request(clusterType,
+ ClusterSpec.Id.from("test"),
+ Version.fromString("6.42"),
+ false);
+ provisioner.prepare(applicationId, clusterSpec, Capacity.fromNodeCount(hostname.length, Optional.of(flavor.name()), false),
1, null);
NestedTransaction transaction = new NestedTransaction().add(new CuratorTransaction(curator));
provisioner.activate(transaction, applicationId, hosts);
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java
index 3d5de590bf4..f870257c111 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/InactiveAndFailedExpirerTest.java
@@ -51,7 +51,7 @@ public class InactiveAndFailedExpirerTest {
List<Node> nodes = tester.makeReadyNodes(2, "default");
// Allocate then deallocate 2 nodes
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"));
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
tester.prepare(applicationId, cluster, Capacity.fromNodeCount(2), 1);
tester.activate(applicationId, ProvisioningTester.toHostSpecs(nodes));
assertEquals(2, tester.getNodes(applicationId, Node.State.active).size());
@@ -91,7 +91,8 @@ public class InactiveAndFailedExpirerTest {
// Allocate and deallocate a single node
ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content,
ClusterSpec.Id.from("test"),
- Version.fromString("6.42"));
+ Version.fromString("6.42"),
+ false);
tester.prepare(applicationId, cluster, Capacity.fromNodeCount(2), 1);
tester.activate(applicationId, ProvisioningTester.toHostSpecs(nodes));
assertEquals(2, tester.getNodes(applicationId, Node.State.active).size());
@@ -118,7 +119,7 @@ public class InactiveAndFailedExpirerTest {
public void node_that_wants_to_retire_is_moved_to_parked() throws OrchestrationException {
ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east")));
ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"),
- Version.fromString("6.42"));
+ Version.fromString("6.42"), false);
tester.makeReadyNodes(5, "default");
// Allocate two nodes
@@ -144,7 +145,9 @@ public class InactiveAndFailedExpirerTest {
Collections.singletonMap(
applicationId,
new MockDeployer.ApplicationContext(applicationId, cluster,
- Capacity.fromNodeCount(2, Optional.of("default")),
+ Capacity.fromNodeCount(2,
+ Optional.of("default"),
+ false),
1)
)
);
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java
index a03b06fda13..f7592bd7422 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java
@@ -99,8 +99,8 @@ public class NodeFailTester {
tester.createHostNodes(3);
// Create applications
- ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.42"));
- ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"));
+ ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
+ ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
int wantedNodesApp1 = 5;
int wantedNodesApp2 = 7;
tester.activate(app1, clusterApp1, wantedNodesApp1);
@@ -109,8 +109,8 @@ public class NodeFailTester {
assertEquals(wantedNodesApp2, tester.nodeRepository.getNodes(app2, Node.State.active).size());
Map<ApplicationId, MockDeployer.ApplicationContext> apps = new HashMap<>();
- apps.put(app1, new MockDeployer.ApplicationContext(app1, clusterApp1, Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default")), 1));
- apps.put(app2, new MockDeployer.ApplicationContext(app2, clusterApp2, Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default")), 1));
+ apps.put(app1, new MockDeployer.ApplicationContext(app1, clusterApp1, Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default"), false), 1));
+ apps.put(app2, new MockDeployer.ApplicationContext(app2, clusterApp2, Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default"), false), 1));
tester.deployer = new MockDeployer(tester.provisioner, apps);
tester.serviceMonitor = new ServiceMonitorStub(apps, tester.nodeRepository);
tester.metric = new MetricsReporterTest.TestMetric();
@@ -129,12 +129,12 @@ public class NodeFailTester {
}
// Create applications
- ClusterSpec clusterNodeAdminApp = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("node-admin"), Version.fromString("6.42"));
- ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.75.0"));
- ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.75.0"));
+ ClusterSpec clusterNodeAdminApp = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("node-admin"), Version.fromString("6.42"), false);
+ ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.75.0"), false);
+ ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.75.0"), false);
Capacity allHosts = Capacity.fromRequiredNodeType(NodeType.host);
- Capacity capacity1 = Capacity.fromNodeCount(3, Optional.of("docker"));
- Capacity capacity2 = Capacity.fromNodeCount(5, Optional.of("docker"));
+ Capacity capacity1 = Capacity.fromNodeCount(3, Optional.of("docker"), false);
+ Capacity capacity2 = Capacity.fromNodeCount(5, Optional.of("docker"), false);
tester.activate(nodeAdminApp, clusterNodeAdminApp, allHosts);
tester.activate(app1, clusterApp1, capacity1);
tester.activate(app2, clusterApp2, capacity2);
@@ -163,7 +163,8 @@ public class NodeFailTester {
Capacity allProxies = Capacity.fromRequiredNodeType(NodeType.proxy);
ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container,
ClusterSpec.Id.from("test"),
- Version.fromString("6.42"));
+ Version.fromString("6.42"),
+ false);
tester.activate(app1, clusterApp1, allProxies);
assertEquals(16, tester.nodeRepository.getNodes(NodeType.proxy, Node.State.active).size());
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java
index b05f4178419..4e9a8f4b608 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTester.java
@@ -111,8 +111,8 @@ public class NodeRetirerTester {
for (int i = 0; i < flavorIds.length; i++) {
Flavor flavor = flavors.get(flavorIds[i]);
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("cluster-" + i), Version.fromString("6.99"));
- Capacity capacity = Capacity.fromNodeCount(numNodes[i], flavor.name());
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("cluster-" + i), Version.fromString("6.99"), false);
+ Capacity capacity = Capacity.fromNodeCount(numNodes[i], Optional.of(flavor.name()), false);
// If the number of node the app wants is divisible by 2, make it into 2 groups, otherwise as 1
int numGroups = numNodes[i] % 2 == 0 ? 2 : 1;
clusterContexts.add(new MockDeployer.ClusterContext(applicationId, cluster, capacity, numGroups));
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java
index b6afc74b81d..f7abd823c77 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OperatorChangeApplicationMaintainerTest.java
@@ -112,8 +112,8 @@ public class OperatorChangeApplicationMaintainerTest {
final ApplicationId app1 = ApplicationId.from(TenantName.from("foo1"), ApplicationName.from("bar"), InstanceName.from("fuz"));
final ApplicationId app2 = ApplicationId.from(TenantName.from("foo2"), ApplicationName.from("bar"), InstanceName.from("fuz"));
- final ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.42"));
- final ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"));
+ final ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
+ final ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
final int wantedNodesApp1 = 5;
final int wantedNodesApp2 = 7;
MockDeployer deployer; // created on activation
@@ -132,9 +132,9 @@ public class OperatorChangeApplicationMaintainerTest {
Map<ApplicationId, MockDeployer.ApplicationContext> apps = new HashMap<>();
apps.put(app1, new MockDeployer.ApplicationContext(app1, clusterApp1,
- Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default")), 1));
+ Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default"), false), 1));
apps.put(app2, new MockDeployer.ApplicationContext(app2, clusterApp2,
- Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default")), 1));
+ Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default"), false), 1));
this.deployer = new MockDeployer(provisioner, apps);
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java
index f6a040a1d7a..fe87f34a3a5 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/PeriodicApplicationMaintainerTest.java
@@ -154,8 +154,8 @@ public class PeriodicApplicationMaintainerTest {
final ApplicationId app1 = ApplicationId.from(TenantName.from("foo1"), ApplicationName.from("bar"), InstanceName.from("fuz"));
final ApplicationId app2 = ApplicationId.from(TenantName.from("foo2"), ApplicationName.from("bar"), InstanceName.from("fuz"));
- final ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.42"));
- final ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"));
+ final ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
+ final ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
final int wantedNodesApp1 = 5;
final int wantedNodesApp2 = 7;
@@ -192,9 +192,9 @@ public class PeriodicApplicationMaintainerTest {
void runApplicationMaintainer(Optional<List<Node>> overriddenNodesNeedingMaintenance) {
Map<ApplicationId, MockDeployer.ApplicationContext> apps = new HashMap<>();
apps.put(app1, new MockDeployer.ApplicationContext(app1, clusterApp1,
- Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default")), 1));
+ Capacity.fromNodeCount(wantedNodesApp1, Optional.of("default"), false), 1));
apps.put(app2, new MockDeployer.ApplicationContext(app2, clusterApp2,
- Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default")), 1));
+ Capacity.fromNodeCount(wantedNodesApp2, Optional.of("default"), false), 1));
MockDeployer deployer = new MockDeployer(provisioner, apps);
new TestablePeriodicApplicationMaintainer(deployer, nodeRepository, Duration.ofMinutes(30), overriddenNodesNeedingMaintenance).run();
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java
index 3766bbba602..6091fb9a3b5 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ReservationExpirerTest.java
@@ -43,7 +43,7 @@ public class ReservationExpirerTest {
NodeRepository nodeRepository = new NodeRepository(flavors, curator, clock, Zone.defaultZone(),
new MockNameResolver().mockAnyLookup(),
new DockerImage("docker-registry.domain.tld:8080/dist/vespa"));
- NodeRepositoryProvisioner provisioner = new NodeRepositoryProvisioner(nodeRepository, flavors, Zone.defaultZone(), clock, (x,y) -> {});
+ NodeRepositoryProvisioner provisioner = new NodeRepositoryProvisioner(nodeRepository, flavors, Zone.defaultZone(), (x,y) -> {});
List<Node> nodes = new ArrayList<>(2);
nodes.add(nodeRepository.createNode(UUID.randomUUID().toString(), UUID.randomUUID().toString(), Optional.empty(), flavors.getFlavorOrThrow("default"), NodeType.tenant));
@@ -56,7 +56,7 @@ public class ReservationExpirerTest {
assertEquals(2, nodeRepository.getNodes(NodeType.tenant, Node.State.dirty).size());
nodeRepository.setReady(nodes, Agent.system, getClass().getSimpleName());
ApplicationId applicationId = new ApplicationId.Builder().tenant("foo").applicationName("bar").instanceName("fuz").build();
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"));
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
provisioner.prepare(applicationId, cluster, Capacity.fromNodeCount(2), 1, null);
assertEquals(2, nodeRepository.getNodes(NodeType.tenant, Node.State.reserved).size());
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java
index 0cf30fce071..f49185b04ec 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java
@@ -80,7 +80,7 @@ public class RetiredExpirerTest {
// Allocate content cluster of sizes 7 -> 2 -> 3:
// Should end up with 3 nodes in the cluster (one previously retired), and 4 retired
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"));
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
int wantedNodes;
activate(applicationId, cluster, wantedNodes=7, 1, provisioner);
activate(applicationId, cluster, wantedNodes=2, 1, provisioner);
@@ -92,7 +92,7 @@ public class RetiredExpirerTest {
clock.advance(Duration.ofHours(30)); // Retire period spent
MockDeployer deployer =
new MockDeployer(provisioner,
- Collections.singletonMap(applicationId, new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(wantedNodes, Optional.of("default")), 1)));
+ Collections.singletonMap(applicationId, new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(wantedNodes, Optional.of("default"), false), 1)));
createRetiredExpirer(deployer).run();
assertEquals(3, nodeRepository.getNodes(applicationId, Node.State.active).size());
assertEquals(4, nodeRepository.getNodes(applicationId, Node.State.inactive).size());
@@ -110,7 +110,7 @@ public class RetiredExpirerTest {
ApplicationId applicationId = ApplicationId.from(TenantName.from("foo"), ApplicationName.from("bar"), InstanceName.from("fuz"));
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"));
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
activate(applicationId, cluster, 8, 8, provisioner);
activate(applicationId, cluster, 2, 2, provisioner);
assertEquals(8, nodeRepository.getNodes(applicationId, Node.State.active).size());
@@ -120,7 +120,7 @@ public class RetiredExpirerTest {
clock.advance(Duration.ofHours(30)); // Retire period spent
MockDeployer deployer =
new MockDeployer(provisioner,
- Collections.singletonMap(applicationId, new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(2, Optional.of("default")), 1)));
+ Collections.singletonMap(applicationId, new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(2, Optional.of("default"), false), 1)));
createRetiredExpirer(deployer).run();
assertEquals(2, nodeRepository.getNodes(applicationId, Node.State.active).size());
assertEquals(6, nodeRepository.getNodes(applicationId, Node.State.inactive).size());
@@ -140,7 +140,7 @@ public class RetiredExpirerTest {
// Allocate content cluster of sizes 7 -> 2 -> 3:
// Should end up with 3 nodes in the cluster (one previously retired), and 4 retired
- ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"));
+ ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
int wantedNodes;
activate(applicationId, cluster, wantedNodes=7, 1, provisioner);
activate(applicationId, cluster, wantedNodes=2, 1, provisioner);
@@ -153,7 +153,7 @@ public class RetiredExpirerTest {
new MockDeployer(provisioner,
Collections.singletonMap(
applicationId,
- new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(wantedNodes, Optional.of("default")), 1)));
+ new MockDeployer.ApplicationContext(applicationId, cluster, Capacity.fromNodeCount(wantedNodes, Optional.of("default"), false), 1)));
// Allow the 1st and 3rd retired nodes permission to inactivate
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/monitoring/MetricsReporterTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/monitoring/MetricsReporterTest.java
index b7eb1b480d9..066227e007f 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/monitoring/MetricsReporterTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/monitoring/MetricsReporterTest.java
@@ -188,7 +188,10 @@ public class MetricsReporterTest {
private Optional<Allocation> allocation(Optional<String> tenant) {
if (tenant.isPresent()) {
- Allocation allocation = new Allocation(app(tenant.get()), ClusterMembership.from("container/id1/3", new Version()), Generation.inital(), false);
+ Allocation allocation = new Allocation(app(tenant.get()),
+ ClusterMembership.from("container/id1/0/3", new Version()),
+ Generation.inital(),
+ false);
return Optional.of(allocation);
}
return Optional.empty();
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/SerializationTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/SerializationTest.java
index f91b4863eeb..fe216c49888 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/SerializationTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/SerializationTest.java
@@ -129,7 +129,7 @@ public class SerializationTest {
" \"applicationId\" : \"myApplication\",\n" +
" \"tenantId\" : \"myTenant\",\n" +
" \"instanceId\" : \"myInstance\",\n" +
- " \"serviceId\" : \"content/myId/0\",\n" +
+ " \"serviceId\" : \"content/myId/0/0\",\n" +
" \"restartGeneration\" : 3,\n" +
" \"currentRestartGeneration\" : 4,\n" +
" \"removable\" : true,\n" +
@@ -162,7 +162,7 @@ public class SerializationTest {
node = node.allocate(ApplicationId.from(TenantName.from("myTenant"),
ApplicationName.from("myApplication"),
InstanceName.from("myInstance")),
- ClusterMembership.from("content/myId/0", Vtag.currentVersion),
+ ClusterMembership.from("content/myId/0/0", Vtag.currentVersion),
clock.instant());
assertEquals(1, node.history().events().size());
clock.advance(Duration.ofMinutes(2));
@@ -332,7 +332,7 @@ public class SerializationTest {
" \"hostname\" : \"myHostname\",\n" +
" \"ipAddresses\" : [\"127.0.0.1\"],\n" +
" \"instance\": {\n" +
- " \"serviceId\": \"content/myId/0\",\n" +
+ " \"serviceId\": \"content/myId/0/0\",\n" +
" \"wantedVespaVersion\": \"6.42.2\"\n" +
" }\n" +
"}";
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AclProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AclProvisioningTest.java
index 492bcaa5462..cb780f08cd9 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AclProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AclProvisioningTest.java
@@ -222,7 +222,7 @@ public class AclProvisioningTest {
private List<Node> allocateNodes(Capacity capacity, ApplicationId applicationId) {
ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"),
- Version.fromString("6.42"));
+ Version.fromString("6.42"), false);
List<HostSpec> prepared = tester.prepare(applicationId, cluster, capacity, 1);
tester.activate(applicationId, new HashSet<>(prepared));
return tester.getNodes(applicationId, Node.State.active).asList();
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java
index dc30f0ed1a8..943cb60bf04 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java
@@ -34,7 +34,7 @@ import java.util.Set;
public class AllocationSimulator {
private AllocationVisualizer visualizer;
- private NodeList nodes = new NodeList(new ArrayList<>());
+ private NodeList nodes;
private NodeFlavors flavors;
private AllocationSimulator(AllocationVisualizer visualizer) {
@@ -94,7 +94,10 @@ public class AllocationSimulator {
private Optional<Allocation> allocation(Optional<String> tenant) {
if (tenant.isPresent()) {
- Allocation allocation = new Allocation(app(tenant.get()), ClusterMembership.from("container/id1/3", new Version()), Generation.inital(), false);
+ Allocation allocation = new Allocation(app(tenant.get()),
+ ClusterMembership.from("container/id1/3", new Version()),
+ Generation.inital(),
+ false);
return Optional.of(allocation);
}
return Optional.empty();
@@ -108,35 +111,31 @@ public class AllocationSimulator {
}
private ClusterSpec cluster() {
- return ClusterSpec.from(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), ClusterSpec.Group.from(1), Version.fromString("6.41"));
+ return ClusterSpec.from(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), ClusterSpec.Group.from(1), Version.fromString("6.41"), false);
}
/* ------------ Methods to add events to the system ----------------*/
public void addCluster(String task, int count, Flavor flavor, String id) {
- NodeSpec.CountNodeSpec nodeSpec = new NodeSpec.CountNodeSpec(count, flavor);
- NodeAllocation allocation = new NodeAllocation(app(id), cluster(), nodeSpec, new MutableInteger(0), Clock.systemUTC());
-
- List<Node> accepted = new ArrayList<>(); //TODO adpot the new allocation algoritm
-
- accepted.addAll(nodes.asList());
- nodes = new NodeList(accepted);
+ // TODO: Implement
+ NodeSpec.CountNodeSpec nodeSpec = new NodeSpec.CountNodeSpec(count, flavor, false);
+ nodes = new NodeList(nodes.asList());
}
public static void main(String[] args) {
- AllocationVisualizer visualisator = new AllocationVisualizer();
+ AllocationVisualizer visualizer = new AllocationVisualizer();
javax.swing.SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Allocation Simulator");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- frame.setContentPane(visualisator);
+ frame.setContentPane(visualizer);
frame.pack();
frame.setVisible(true);
});
- AllocationSimulator simulator = new AllocationSimulator(visualisator);
+ AllocationSimulator simulator = new AllocationSimulator(visualizer);
simulator.addCluster("App1 : 3 * d-1 nodes", 3, simulator.flavors.getFlavorOrThrow("d-1"), "App1");
simulator.addCluster("App2 : 2 * d-2 nodes", 2, simulator.flavors.getFlavorOrThrow("d-2"), "App2");
simulator.addCluster("App3 : 3 * d-2 nodes", 3, simulator.flavors.getFlavorOrThrow("d-2"), "App3");
@@ -144,4 +143,5 @@ public class AllocationSimulator {
simulator.addCluster("App5 : 3 * d-3 nodes", 3, simulator.flavors.getFlavorOrThrow("d-3"), "App5");
simulator.addCluster("App6 : 5 * d-2 nodes", 5, simulator.flavors.getFlavorOrThrow("d-2"), "App6");
}
+
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationVisualizer.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationVisualizer.java
index ff2591a02f4..24011bd4b49 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationVisualizer.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationVisualizer.java
@@ -19,6 +19,7 @@ import java.util.List;
* @author smorgrav
*/
public class AllocationVisualizer extends JPanel {
+
// Container box's width and height
private static final int BOX_WIDTH = 1024;
private static final int BOX_HEIGHT = 480;
@@ -141,4 +142,5 @@ public class AllocationVisualizer extends JPanel {
}
}
}
+
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java
index a6760ef37ce..a85aca9d647 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningTest.java
@@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.provision.provisioning;
import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.HostSpec;
@@ -14,8 +15,15 @@ import org.junit.Test;
import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
/**
* Tests deployment to docker images which share the same physical host.
@@ -31,14 +39,13 @@ public class DockerProvisioningTest {
ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east")));
ApplicationId application1 = tester.makeApplicationId();
- for (int i = 1; i < 10; i++) {
+ for (int i = 1; i < 10; i++)
tester.makeReadyDockerNodes(1, dockerFlavor, "dockerHost" + i);
- }
Version wantedVespaVersion = Version.fromString("6.39");
int nodeCount = 7;
List<HostSpec> hosts = tester.prepare(application1,
- ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), wantedVespaVersion),
+ ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), wantedVespaVersion, false),
nodeCount, 1, dockerFlavor);
tester.activate(application1, new HashSet<>(hosts));
@@ -49,15 +56,126 @@ public class DockerProvisioningTest {
// Upgrade Vespa version on nodes
Version upgradedWantedVespaVersion = Version.fromString("6.40");
List<HostSpec> upgradedHosts = tester.prepare(application1,
- ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), upgradedWantedVespaVersion),
+ ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), upgradedWantedVespaVersion, false),
nodeCount, 1, dockerFlavor);
tester.activate(application1, new HashSet<>(upgradedHosts));
- final NodeList upgradedNodes = tester.getNodes(application1, Node.State.active);
+ NodeList upgradedNodes = tester.getNodes(application1, Node.State.active);
assertEquals(nodeCount, upgradedNodes.size());
assertEquals(dockerFlavor, upgradedNodes.asList().get(0).flavor().canonicalName());
assertEquals(hosts, upgradedHosts);
}
+ /** Exclusive app first, then non-exclusive: Should give the same result as below */
+ @Test
+ public void docker_application_deployment_with_exclusive_app_first() {
+ ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east")));
+ for (int i = 1; i <= 4; i++)
+ tester.makeReadyVirtualNode(i, dockerFlavor, "host1");
+ for (int i = 5; i <= 8; i++)
+ tester.makeReadyVirtualNode(i, dockerFlavor, "host2");
+ for (int i = 9; i <= 12; i++)
+ tester.makeReadyVirtualNode(i, dockerFlavor, "host3");
+ for (int i = 13; i <= 16; i++)
+ tester.makeReadyVirtualNode(i, dockerFlavor, "host4");
+
+ ApplicationId application1 = tester.makeApplicationId();
+ prepareAndActivate(application1, 2, true, tester);
+ assertEquals(setOf("host1", "host2"), hostsOf(tester.getNodes(application1, Node.State.active)));
+
+ ApplicationId application2 = tester.makeApplicationId();
+ prepareAndActivate(application2, 2, false, tester);
+ assertEquals("Application is assigned to separate hosts",
+ setOf("host3", "host4"), hostsOf(tester.getNodes(application2, Node.State.active)));
+ }
+
+ /** Non-exclusive app first, then an exclusive: Should give the same result as above */
+ @Test
+ public void docker_application_deployment_with_exclusive_app_last() {
+ ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east")));
+ for (int i = 1; i <= 4; i++)
+ tester.makeReadyVirtualNode(i, dockerFlavor, "host1");
+ for (int i = 5; i <= 8; i++)
+ tester.makeReadyVirtualNode(i, dockerFlavor, "host2");
+ for (int i = 9; i <= 12; i++)
+ tester.makeReadyVirtualNode(i, dockerFlavor, "host3");
+ for (int i = 13; i <= 16; i++)
+ tester.makeReadyVirtualNode(i, dockerFlavor, "host4");
+
+ ApplicationId application1 = tester.makeApplicationId();
+ prepareAndActivate(application1, 2, false, tester);
+ assertEquals(setOf("host1", "host2"), hostsOf(tester.getNodes(application1, Node.State.active)));
+
+ ApplicationId application2 = tester.makeApplicationId();
+ prepareAndActivate(application2, 2, true, tester);
+ assertEquals("Application is assigned to separate hosts",
+ setOf("host3", "host4"), hostsOf(tester.getNodes(application2, Node.State.active)));
+ }
+
+ /** Test making an application exclusive */
+ @Test
+ public void docker_application_deployment_change_to_exclusive_and_back() {
+ ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east")));
+ for (int i = 1; i <= 4; i++)
+ tester.makeReadyVirtualNode(i, dockerFlavor, "host1");
+ for (int i = 5; i <= 8; i++)
+ tester.makeReadyVirtualNode(i, dockerFlavor, "host2");
+ for (int i = 9; i <= 12; i++)
+ tester.makeReadyVirtualNode(i, dockerFlavor, "host3");
+ for (int i = 13; i <= 16; i++)
+ tester.makeReadyVirtualNode(i, dockerFlavor, "host4");
+
+ ApplicationId application1 = tester.makeApplicationId();
+ prepareAndActivate(application1, 2, false, tester);
+ for (Node node : tester.getNodes(application1, Node.State.active).asList())
+ assertFalse(node.allocation().get().membership().cluster().isExclusive());
+
+ prepareAndActivate(application1, 2, true, tester);
+ assertEquals(setOf("host1", "host2"), hostsOf(tester.getNodes(application1, Node.State.active)));
+ for (Node node : tester.getNodes(application1, Node.State.active).asList())
+ assertTrue(node.allocation().get().membership().cluster().isExclusive());
+
+ prepareAndActivate(application1, 2, false, tester);
+ assertEquals(setOf("host1", "host2"), hostsOf(tester.getNodes(application1, Node.State.active)));
+ for (Node node : tester.getNodes(application1, Node.State.active).asList())
+ assertFalse(node.allocation().get().membership().cluster().isExclusive());
+ }
+
+ /** Non-exclusive app first, then an exclusive: Should give the same result as above */
+ @Test
+ public void docker_application_deployment_with_exclusive_app_causing_allocation_failure() {
+ ProvisioningTester tester = new ProvisioningTester(new Zone(Environment.prod, RegionName.from("us-east")));
+ for (int i = 1; i <= 4; i++)
+ tester.makeReadyVirtualNode(i, dockerFlavor, "host1");
+ for (int i = 5; i <= 8; i++)
+ tester.makeReadyVirtualNode(i, dockerFlavor, "host2");
+ for (int i = 9; i <= 12; i++)
+ tester.makeReadyVirtualNode(i, dockerFlavor, "host3");
+ for (int i = 13; i <= 16; i++)
+ tester.makeReadyVirtualNode(i, dockerFlavor, "host4");
+
+ ApplicationId application1 = tester.makeApplicationId();
+ prepareAndActivate(application1, 2, true, tester);
+ assertEquals(setOf("host1", "host2"), hostsOf(tester.getNodes(application1, Node.State.active)));
+
+ try {
+ ApplicationId application2 = tester.makeApplicationId();
+ prepareAndActivate(application2, 3, false, tester);
+ fail("Expected allocation failure");
+ }
+ catch (Exception e) {
+ assertEquals("No room for 3 nodes as 2 of 4 hosts are exclusive",
+ "Could not satisfy request for 3 nodes of flavor 'dockerSmall' for container cluster 'myContainer' group 0 6.39: Not enough nodes available due to host exclusivity constraints.",
+ e.getMessage());
+ }
+
+ // Adding 3 nodes of another cluster for the same application works:
+ Set<HostSpec> hosts = new HashSet<>(tester.prepare(application1,
+ ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContainer2"), Version.fromString("6.39"), false),
+ Capacity.fromNodeCount(3, Optional.of(dockerFlavor), false),
+ 1));
+ tester.activate(application1, hosts);
+ }
+
// In dev, test and staging you get nodes with default flavor, but we should get specified flavor for docker nodes
@Test
public void get_specified_flavor_not_default_flavor_for_docker() {
@@ -65,7 +183,7 @@ public class DockerProvisioningTest {
ApplicationId application1 = tester.makeApplicationId();
tester.makeReadyDockerNodes(1, dockerFlavor, "dockerHost");
- List<HostSpec> hosts = tester.prepare(application1, ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.42")), 1, 1, dockerFlavor);
+ List<HostSpec> hosts = tester.prepare(application1, ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.42"), false), 1, 1, dockerFlavor);
tester.activate(application1, new HashSet<>(hosts));
NodeList nodes = tester.getNodes(application1, Node.State.active);
@@ -73,4 +191,20 @@ public class DockerProvisioningTest {
assertEquals(dockerFlavor, nodes.asList().get(0).flavor().canonicalName());
}
+ private Set setOf(String ... strings) {
+ return Stream.of(strings).collect(Collectors.toSet());
+ }
+
+ private Set hostsOf(NodeList nodes) {
+ return nodes.asList().stream().map(Node::parentHostname).map(Optional::get).collect(Collectors.toSet());
+ }
+
+ private void prepareAndActivate(ApplicationId application, int nodeCount, boolean exclusive, ProvisioningTester tester) {
+ Set<HostSpec> hosts = new HashSet<>(tester.prepare(application,
+ ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContainer"), Version.fromString("6.39"), exclusive),
+ Capacity.fromNodeCount(nodeCount, Optional.of(dockerFlavor), false),
+ 1));
+ tester.activate(application, hosts);
+ }
+
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisioningTest.java
index 9d035304ab7..b6f262775d7 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisioningTest.java
@@ -68,13 +68,13 @@ public class DynamicDockerProvisioningTest {
// Application 1
ApplicationId application1 = makeApplicationId("t1", "a1");
- ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"));
+ ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
addAndAssignNode(application1, "1a", dockerHosts.get(2).hostname(), flavor, 0, tester);
addAndAssignNode(application1, "1b", dockerHosts.get(3).hostname(), flavor, 1, tester);
// Application 2
ApplicationId application2 = makeApplicationId("t2", "a2");
- ClusterSpec clusterSpec2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"));
+ ClusterSpec clusterSpec2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
addAndAssignNode(application2, "2a", dockerHosts.get(2).hostname(), flavor, 0, tester);
addAndAssignNode(application2, "2b", dockerHosts.get(3).hostname(), flavor, 1, tester);
@@ -115,13 +115,13 @@ public class DynamicDockerProvisioningTest {
// Application 1
ApplicationId application1 = makeApplicationId("t1", "a1");
- ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"));
+ ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
addAndAssignNode(application1, "1a", dockerHosts.get(0).hostname(), flavor, 0, tester);
addAndAssignNode(application1, "1b", dockerHosts.get(1).hostname(), flavor, 1, tester);
// Application 2
ApplicationId application2 = makeApplicationId("t2", "a2");
- ClusterSpec clusterSpec2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"));
+ ClusterSpec clusterSpec2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
addAndAssignNode(application2, "2a", dockerHosts.get(2).hostname(), flavor, 0, tester);
addAndAssignNode(application2, "2b", dockerHosts.get(3).hostname(), flavor, 1, tester);
@@ -162,7 +162,7 @@ public class DynamicDockerProvisioningTest {
// Application 1
ApplicationId application1 = makeApplicationId("t1", "1");
- ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"));
+ ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
String hostParent2 = dockerHosts.get(2).hostname();
String hostParent3 = dockerHosts.get(3).hostname();
addAndAssignNode(application1, "1a", hostParent2, flavorD2, 0, tester);
@@ -170,7 +170,7 @@ public class DynamicDockerProvisioningTest {
// Application 2
ApplicationId application2 = makeApplicationId("t2", "2");
- ClusterSpec clusterSpec2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"));
+ ClusterSpec clusterSpec2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
addAndAssignNode(application2, "2a", hostParent2, flavorD1, 0, tester);
addAndAssignNode(application2, "2b", hostParent3, flavorD1, 1, tester);
@@ -217,7 +217,7 @@ public class DynamicDockerProvisioningTest {
// Application 1
ApplicationId application1 = makeApplicationId("t1", "1");
- ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"));
+ ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
String hostParent2 = dockerHosts.get(2).hostname();
String hostParent3 = dockerHosts.get(3).hostname();
addAndAssignNode(application1, "1a", hostParent2, flavorD2, 0, tester);
@@ -225,7 +225,7 @@ public class DynamicDockerProvisioningTest {
// Application 2
ApplicationId application2 = makeApplicationId("t2", "2");
- ClusterSpec clusterSpec2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"));
+ ClusterSpec clusterSpec2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
addAndAssignNode(application2, "2a", hostParent2, flavorD1, 0, tester);
addAndAssignNode(application2, "2b", hostParent3, flavorD1, 1, tester);
@@ -284,17 +284,17 @@ public class DynamicDockerProvisioningTest {
// Application 1
ApplicationId application1 = makeApplicationId("t1", "a1");
- ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"));
+ ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
deployapp(application1, clusterSpec1, flavor, tester, 3);
// Application 2
ApplicationId application2 = makeApplicationId("t2", "a2");
- ClusterSpec clusterSpec2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"));
+ ClusterSpec clusterSpec2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
deployapp(application2, clusterSpec2, flavor, tester, 2);
// Application 3
ApplicationId application3 = makeApplicationId("t3", "a3");
- ClusterSpec clusterSpec3 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"));
+ ClusterSpec clusterSpec3 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
deployapp(application3, clusterSpec3, flavor, tester, 2);
// App 2 and 3 should have been allocated to the same nodes - fail on of the parent hosts from there
@@ -342,7 +342,7 @@ public class DynamicDockerProvisioningTest {
// Application 1
ApplicationId application1 = makeApplicationId("t1", "a1");
- ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"));
+ ClusterSpec clusterSpec1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false);
addAndAssignNode(application1, "1a", dockerHosts.get(0).hostname(), flavor, 0, tester);
addAndAssignNode(application1, "1b", dockerHosts.get(1).hostname(), flavor, 1, tester);
@@ -369,7 +369,7 @@ public class DynamicDockerProvisioningTest {
//Deploy an application of 6 nodes of 3 nodes in each cluster. We only have 3 docker hosts available
ApplicationId application1 = tester.makeApplicationId();
tester.prepare(application1,
- ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100")),
+ ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false),
6, 2, flavor.canonicalName());
fail("Two groups have been allocated to the same parent host");
@@ -397,7 +397,7 @@ public class DynamicDockerProvisioningTest {
// Deploy initial state (can max deploy 3 nodes due to redundancy requirements)
List<HostSpec> hosts = tester.prepare(application1,
- ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100")),
+ ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false),
3, 1, flavor.canonicalName());
tester.activate(application1, ImmutableSet.copyOf(hosts));
@@ -409,7 +409,7 @@ public class DynamicDockerProvisioningTest {
try {
hosts = tester.prepare(application1,
- ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100")),
+ ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false),
4, 1, flavor.canonicalName());
fail("Was able to deploy with 4 nodes, should not be able to use spare capacity");
} catch (OutOfCapacityException e) {
@@ -417,7 +417,7 @@ public class DynamicDockerProvisioningTest {
tester.fail(hosts.get(0));
hosts = tester.prepare(application1,
- ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100")),
+ ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false),
3, 1, flavor.canonicalName());
tester.activate(application1, ImmutableSet.copyOf(hosts));
@@ -435,7 +435,7 @@ public class DynamicDockerProvisioningTest {
ApplicationId application1 = tester.makeApplicationId();
List<HostSpec> hosts = tester.prepare(application1,
- ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100")),
+ ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false),
3, 1, flavor.canonicalName());
tester.activate(application1, ImmutableSet.copyOf(hosts));
@@ -452,7 +452,7 @@ public class DynamicDockerProvisioningTest {
ApplicationId application = tester.makeApplicationId();
Flavor flavor = tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("d-3");
- tester.prepare(application, ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100")),
+ tester.prepare(application, ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false),
2, 1, flavor.canonicalName());
}
@@ -467,7 +467,7 @@ public class DynamicDockerProvisioningTest {
private Node addAndAssignNode(ApplicationId id, String hostname, String parentHostname, Flavor flavor, int index, ProvisioningTester tester) {
Node node1a = Node.create("open1", Collections.singleton("127.0.0.100"), new HashSet<>(), hostname, Optional.of(parentHostname), flavor, NodeType.tenant);
- ClusterSpec clusterSpec = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100")).changeGroup(Optional.of(ClusterSpec.Group.from(0)));
+ ClusterSpec clusterSpec = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.100"), false).with(Optional.of(ClusterSpec.Group.from(0)));
ClusterMembership clusterMembership1 = ClusterMembership.from(clusterSpec, index);
Node node1aAllocation = node1a.allocate(id, clusterMembership1, Instant.now());
@@ -523,7 +523,8 @@ public class DynamicDockerProvisioningTest {
List<HostSpec> list = tester.prepare(applicationId,
ClusterSpec.request(ClusterSpec.Type.container,
ClusterSpec.Id.from("node-admin"),
- Version.fromString("6.42")),
+ Version.fromString("6.42"),
+ false),
Capacity.fromRequiredNodeType(NodeType.host),
1);
tester.activate(applicationId, ImmutableSet.copyOf(list));
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java
index 9587fedb7b2..071c7549884 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/MultigroupProvisioningTest.java
@@ -100,8 +100,8 @@ public class MultigroupProvisioningTest {
tester.makeReadyNodes(10, "small");
- deploy(application1, Capacity.fromRequiredNodeCount(1, "small"), 1, tester);
- deploy(application1, Capacity.fromRequiredNodeCount(2, "small"), 2, tester);
+ deploy(application1, Capacity.fromNodeCount(1, Optional.of("small"), true), 1, tester);
+ deploy(application1, Capacity.fromNodeCount(2, Optional.of("small"), true), 2, tester);
}
@Test
@@ -113,8 +113,8 @@ public class MultigroupProvisioningTest {
tester.makeReadyNodes(10, "small");
tester.makeReadyNodes(10, "large");
- deploy(application1, Capacity.fromRequiredNodeCount(1, "small"), 1, tester);
- deploy(application1, Capacity.fromRequiredNodeCount(2, "large"), 2, tester);
+ deploy(application1, Capacity.fromNodeCount(1, Optional.of("small"), true), 1, tester);
+ deploy(application1, Capacity.fromNodeCount(2, Optional.of("large"), true), 2, tester);
}
@Test
@@ -135,7 +135,7 @@ public class MultigroupProvisioningTest {
new MockDeployer(tester.provisioner(),
Collections.singletonMap(application1,
new MockDeployer.ApplicationContext(application1, cluster(),
- Capacity.fromNodeCount(8, Optional.of("large")), 1)));
+ Capacity.fromNodeCount(8, Optional.of("large"), false), 1)));
new RetiredExpirer(tester.nodeRepository(), tester.orchestrator(), deployer, tester.clock(), Duration.ofDays(30),
Duration.ofHours(12), new JobControl(tester.nodeRepository().database())).run();
@@ -144,10 +144,10 @@ public class MultigroupProvisioningTest {
}
private void deploy(ApplicationId application, int nodeCount, int groupCount, String flavor, ProvisioningTester tester) {
- deploy(application, Capacity.fromNodeCount(nodeCount, Optional.of(flavor)), groupCount, tester);
+ deploy(application, Capacity.fromNodeCount(nodeCount, Optional.of(flavor), false), groupCount, tester);
}
private void deploy(ApplicationId application, int nodeCount, int groupCount, ProvisioningTester tester) {
- deploy(application, Capacity.fromNodeCount(nodeCount, "default"), groupCount, tester);
+ deploy(application, Capacity.fromNodeCount(nodeCount, Optional.of("default"), false), groupCount, tester);
}
private void deploy(ApplicationId application, Capacity capacity, int wantedGroups, ProvisioningTester tester) {
int nodeCount = capacity.nodeCount();
@@ -199,7 +199,7 @@ public class MultigroupProvisioningTest {
assertEquals("No additional groups are retained containing retired nodes", wantedGroups, allGroups.size());
}
- private ClusterSpec cluster() { return ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42")); }
+ private ClusterSpec cluster() { return ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false); }
private Set<HostSpec> prepare(ApplicationId application, Capacity capacity, int groupCount, ProvisioningTester tester) {
return new HashSet<>(tester.prepare(application, cluster(), capacity, groupCount));
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizerTest.java
index 47ead201509..6e07585afd4 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizerTest.java
@@ -49,7 +49,7 @@ public class NodePrioritizerTest {
Assert.assertTrue(NodePrioritizer.isPreferredNodeToBeReloacted(nodes, c, parent));
// Unallocated over allocated
- ClusterSpec spec = ClusterSpec.from(ClusterSpec.Type.content, ClusterSpec.Id.from("mycluster"), ClusterSpec.Group.from(0), Version.fromString("6.142.22"));
+ ClusterSpec spec = ClusterSpec.from(ClusterSpec.Type.content, ClusterSpec.Id.from("mycluster"), ClusterSpec.Group.from(0), Version.fromString("6.142.22"), false);
c = c.allocate(ApplicationId.defaultId(), ClusterMembership.from(spec, 0), Instant.now());
nodes.remove(c);
nodes.add(c);
@@ -59,7 +59,7 @@ public class NodePrioritizerTest {
Assert.assertFalse(NodePrioritizer.isPreferredNodeToBeReloacted(nodes, c, parent));
// Container over content
- ClusterSpec spec2 = ClusterSpec.from(ClusterSpec.Type.container, ClusterSpec.Id.from("mycluster"), ClusterSpec.Group.from(0), Version.fromString("6.142.22"));
+ ClusterSpec spec2 = ClusterSpec.from(ClusterSpec.Type.container, ClusterSpec.Id.from("mycluster"), ClusterSpec.Group.from(0), Version.fromString("6.142.22"), false);
d = d.allocate(ApplicationId.defaultId(), ClusterMembership.from(spec2, 0), Instant.now());
nodes.remove(d);
nodes.add(d);
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java
index 97fde2274f2..5f3976ab137 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeTypeProvisioningTest.java
@@ -40,7 +40,7 @@ public class NodeTypeProvisioningTest {
private final ApplicationId application = tester.makeApplicationId(); // application using proxy nodes
private final Capacity capacity = Capacity.fromRequiredNodeType(NodeType.proxy);
private final ClusterSpec clusterSpec = ClusterSpec.request(
- ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.42"));
+ ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false);
@Before
public void setup() {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java
index 26e0e714201..11691a691e8 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java
@@ -742,8 +742,8 @@ public class ProvisioningTest {
tester.makeReadyNodes(6, "large-variant-variant"); //cost = 11
ApplicationId applicationId = tester.makeApplicationId();
- ClusterSpec contentClusterSpec = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.42"));
- ClusterSpec containerClusterSpec = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContainer"), Version.fromString("6.42"));
+ ClusterSpec contentClusterSpec = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.42"), false);
+ ClusterSpec containerClusterSpec = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContainer"), Version.fromString("6.42"), false);
List<HostSpec> containerNodes = tester.prepare(applicationId, containerClusterSpec, 5, 1, "large");
List<HostSpec> contentNodes = tester.prepare(applicationId, contentClusterSpec, 10, 1, "large");
@@ -768,10 +768,10 @@ public class ProvisioningTest {
private SystemState prepare(ApplicationId application, int container0Size, int container1Size, int content0Size, int content1Size, String flavor, Version wantedVersion, ProvisioningTester tester) {
// "deploy prepare" with a two container clusters and a storage cluster having of two groups
- ClusterSpec containerCluster0 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("container0"), wantedVersion);
- ClusterSpec containerCluster1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("container1"), wantedVersion);
- ClusterSpec contentCluster0 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("content0"), wantedVersion);
- ClusterSpec contentCluster1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("content1"), wantedVersion);
+ ClusterSpec containerCluster0 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("container0"), wantedVersion, false);
+ ClusterSpec containerCluster1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("container1"), wantedVersion, false);
+ ClusterSpec contentCluster0 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("content0"), wantedVersion, false);
+ ClusterSpec contentCluster1 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("content1"), wantedVersion, false);
Set<HostSpec> container0 = prepare(application, containerCluster0, container0Size, 1, flavor, tester);
Set<HostSpec> container1 = prepare(application, containerCluster1, container1Size, 1, flavor, tester);
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
index 976069758ea..61032759532 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java
@@ -40,6 +40,8 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
+import java.util.function.Function;
+import java.util.function.UnaryOperator;
import java.util.logging.Level;
import java.util.stream.Collectors;
@@ -86,8 +88,8 @@ public class ProvisioningTester {
new DockerImage("docker-registry.domain.tld:8080/dist/vespa"));
this.orchestrator = mock(Orchestrator.class);
doThrow(new RuntimeException()).when(orchestrator).acquirePermissionToRemove(any());
- this.provisioner = new NodeRepositoryProvisioner(nodeRepository, nodeFlavors, zone, clock,
- (x,y) -> allocationSnapshots.add(new AllocationSnapshot(new NodeList(x), "Provision tester", y)));
+ this.provisioner = new NodeRepositoryProvisioner(nodeRepository, nodeFlavors, zone,
+ (x,y) -> allocationSnapshots.add(new AllocationSnapshot(new NodeList(x), "Provision tester", y)));
this.capacityPolicies = new CapacityPolicies(zone, nodeFlavors);
this.provisionLogger = new NullProvisionLogger();
}
@@ -130,7 +132,7 @@ public class ProvisioningTester {
public void patchNode(Node node) { nodeRepository.write(node); }
public List<HostSpec> prepare(ApplicationId application, ClusterSpec cluster, int nodeCount, int groups, String flavor) {
- return prepare(application, cluster, Capacity.fromNodeCount(nodeCount, Optional.ofNullable(flavor)), groups);
+ return prepare(application, cluster, Capacity.fromNodeCount(nodeCount, Optional.ofNullable(flavor), false), groups);
}
public List<HostSpec> prepare(ApplicationId application, ClusterSpec cluster, Capacity capacity, int groups) {
@@ -285,9 +287,20 @@ public class ProvisioningTester {
/** Creates a set of virtual nodes on a single parent host */
List<Node> makeReadyVirtualNodes(int n, String flavor, Optional<String> parentHostId) {
- List<Node> nodes = new ArrayList<>(n);
- for (int i = 0; i < n; i++) {
- final String hostname = UUID.randomUUID().toString();
+ return makeReadyVirtualNodes(n, 0, flavor, parentHostId, index -> UUID.randomUUID().toString());
+ }
+
+ /** Creates a set of virtual nodes on a single parent host */
+ List<Node> makeReadyVirtualNode(int index, String flavor, String parentHostId) {
+ return makeReadyVirtualNodes(1, index, flavor, Optional.of(parentHostId), i -> String.format("node%03d", i));
+ }
+
+ /** Creates a set of virtual nodes on a single parent host */
+ List<Node> makeReadyVirtualNodes(int count, int startIndex, String flavor, Optional<String> parentHostId,
+ Function<Integer, String> nodeNamer) {
+ List<Node> nodes = new ArrayList<>(count);
+ for (int i = startIndex; i < count + startIndex; i++) {
+ String hostname = nodeNamer.apply(i);
nodes.add(nodeRepository.createNode("openstack-id", hostname, parentHostId,
nodeFlavors.getFlavorOrThrow(flavor), NodeType.tenant));
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningTest.java
index 4366cd641ec..9e505702270 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/VirtualNodeProvisioningTest.java
@@ -32,8 +32,8 @@ import static org.junit.Assert.assertNotNull;
public class VirtualNodeProvisioningTest {
private static final String flavor = "v-4-8-100";
- private static final ClusterSpec contentClusterSpec = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.42"));
- private static final ClusterSpec containerClusterSpec = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContainer"), Version.fromString("6.42"));
+ private static final ClusterSpec contentClusterSpec = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.42"), false);
+ private static final ClusterSpec containerClusterSpec = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContainer"), Version.fromString("6.42"), false);
private ProvisioningTester tester;
private ApplicationId applicationId;