summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/Flags.java14
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/custom/HostResources.java132
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/custom/NodeResources.java90
-rw-r--r--flags/src/main/java/com/yahoo/vespa/flags/custom/SharedHost.java64
-rw-r--r--flags/src/test/java/com/yahoo/vespa/flags/FlagsTest.java11
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainer.java60
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostProvisioner.java6
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainerTest.java3
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java11
11 files changed, 257 insertions, 140 deletions
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
index 4633e0ae870..1a1a25c973a 100644
--- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
+++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java
@@ -4,6 +4,7 @@ package com.yahoo.vespa.flags;
import com.yahoo.component.Vtag;
import com.yahoo.vespa.defaults.Defaults;
import com.yahoo.vespa.flags.custom.HostCapacity;
+import com.yahoo.vespa.flags.custom.SharedHost;
import java.math.BigDecimal;
import java.util.List;
@@ -62,13 +63,6 @@ public class Flags {
"Takes effect on next tick.",
HOSTNAME);
- public static final UnboundLongFlag THIN_POOL_GB = defineLongFlag(
- "thin-pool-gb", -1,
- "The size of the disk reserved for the thin pool with dynamic provisioning in AWS, in base-2 GB. " +
- "If <0, the default is used (which may depend on the zone and node type).",
- "Takes effect immediately (but used only during provisioning).",
- NODE_TYPE);
-
public static final UnboundDoubleFlag CONTAINER_CPU_CAP = defineDoubleFlag(
"container-cpu-cap", 0,
"Hard limit on how many CPUs a container may use. This value is multiplied by CPU allocated to node, so " +
@@ -96,6 +90,12 @@ public class Flags {
"Otherwise it specifies the total (unallocated or not) capacity.",
"Takes effect on next iteration of DynamicProvisioningMaintainer.");
+ public static final UnboundJacksonFlag<SharedHost> SHARED_HOST = defineJacksonFlag(
+ "shared-host", SharedHost.createDisabled(), SharedHost.class,
+ "Specifies whether shared hosts can be provisioned, and if so, the advertised " +
+ "node resources of the host, the maximum number of containers, etc.",
+ "Takes effect on next iteration of DynamicProvisioningMaintainer.");
+
public static final UnboundListFlag<String> INACTIVE_MAINTENANCE_JOBS = defineListFlag(
"inactive-maintenance-jobs", List.of(), String.class,
"The list of maintenance jobs that are inactive.",
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/custom/HostResources.java b/flags/src/main/java/com/yahoo/vespa/flags/custom/HostResources.java
new file mode 100644
index 00000000000..c0b5d7a523c
--- /dev/null
+++ b/flags/src/main/java/com/yahoo/vespa/flags/custom/HostResources.java
@@ -0,0 +1,132 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.flags.custom;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * The advertised node resources of a host, similar to config-provision's NodeResources,
+ * but with additional host-specific resources like the number of containers.
+ *
+ * @author freva
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class HostResources {
+ private static final Set<String> validDiskSpeeds = Set.of("slow", "fast");
+ private static final Set<String> validStorageTypes = Set.of("remote", "local");
+
+ private final double vcpu;
+
+ private final double memoryGb;
+
+ private final double diskGb;
+
+ private final double bandwidthGbps;
+
+ private final String diskSpeed;
+
+ private final String storageType;
+
+ private final int containers;
+
+ @JsonCreator
+ public HostResources(@JsonProperty("vcpu") Double vcpu,
+ @JsonProperty("memoryGb") Double memoryGb,
+ @JsonProperty("diskGb") Double diskGb,
+ @JsonProperty("bandwidthGbps") Double bandwidthGbps,
+ @JsonProperty("diskSpeed") String diskSpeed,
+ @JsonProperty("storageType") String storageType,
+ @JsonProperty("containers") Integer containers) {
+ this.vcpu = requirePositive("vcpu", vcpu);
+ this.memoryGb = requirePositive("memoryGb", memoryGb);
+ this.diskGb = requirePositive("diskGb", diskGb);
+ this.bandwidthGbps = requirePositive("bandwidthGbps", Optional.ofNullable(bandwidthGbps).orElse(0.3));
+ this.diskSpeed = validateEnum("diskSpeed", validDiskSpeeds, diskSpeed);
+ this.storageType = validateEnum("storageType", validStorageTypes, storageType);
+ this.containers = requirePositive("containers", containers);
+ }
+
+ @JsonProperty("vcpu")
+ public double vcpu() { return vcpu; }
+
+ @JsonProperty("memoryGb")
+ public double memoryGb() { return memoryGb; }
+
+ @JsonProperty("diskGb")
+ public double diskGb() { return diskGb; }
+
+ @JsonProperty("bandwidthGbps")
+ public double bandwidthGbps() { return bandwidthGbps; }
+
+ @JsonProperty("diskSpeed")
+ public String diskSpeed() { return diskSpeed; }
+
+ @JsonProperty("storageType")
+ public String storageType() { return storageType; }
+
+ @JsonProperty("containers")
+ public int containers() { return containers; }
+
+ private static double requirePositive(String name, Double value) {
+ requireNonNull(name, value);
+ if (value <= 0)
+ throw new IllegalArgumentException("'" + name + "' must be positive, was " + value);
+ return value;
+ }
+
+ private static int requirePositive(String name, Integer value) {
+ requireNonNull(name, value);
+ if (value <= 0)
+ throw new IllegalArgumentException("'" + name + "' must be positive, was " + value);
+ return value;
+ }
+
+ private static String validateEnum(String name, Set<String> validValues, String value) {
+ requireNonNull(name, value);
+ if (!validValues.contains(value))
+ throw new IllegalArgumentException("Invalid " + name + ", valid values are: " +
+ validValues + ", got: " + value);
+ return value;
+ }
+
+ private static <T> T requireNonNull(String name, T value) {
+ return Objects.requireNonNull(value, () -> "'" + name + "' has not been specified");
+ }
+
+ @Override
+ public String toString() {
+ return "HostResources{" +
+ "vcpu=" + vcpu +
+ ", memoryGb=" + memoryGb +
+ ", diskGb=" + diskGb +
+ ", bandwidthGbps=" + bandwidthGbps +
+ ", diskSpeed='" + diskSpeed + '\'' +
+ ", storageType='" + storageType + '\'' +
+ ", containers=" + containers +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ HostResources resources = (HostResources) o;
+ return Double.compare(resources.vcpu, vcpu) == 0 &&
+ Double.compare(resources.memoryGb, memoryGb) == 0 &&
+ Double.compare(resources.diskGb, diskGb) == 0 &&
+ Double.compare(resources.bandwidthGbps, bandwidthGbps) == 0 &&
+ diskSpeed.equals(resources.diskSpeed) &&
+ storageType.equals(resources.storageType) &&
+ containers == resources.containers;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(vcpu, memoryGb, diskGb, bandwidthGbps, diskSpeed, storageType, containers);
+ }
+}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/custom/NodeResources.java b/flags/src/main/java/com/yahoo/vespa/flags/custom/NodeResources.java
deleted file mode 100644
index 38afc8a1481..00000000000
--- a/flags/src/main/java/com/yahoo/vespa/flags/custom/NodeResources.java
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.flags.custom;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-
-/**
- * @author freva
- */
-@JsonIgnoreProperties(ignoreUnknown = true)
-public class NodeResources {
- private static final Set<String> validDiskSpeeds = Set.of("any", "slow", "fast");
-
- @JsonProperty("vcpu")
- private final double vcpu;
-
- @JsonProperty("memoryGb")
- private final double memoryGb;
-
- @JsonProperty("diskGb")
- private final double diskGb;
-
- @JsonProperty("bandwidthGbps")
- private final double bandwidthGbps;
-
- @JsonProperty("diskSpeed")
- private final String diskSpeed;
-
- public NodeResources(@JsonProperty("vcpu") double vcpu,
- @JsonProperty("memoryGb") double memoryGb,
- @JsonProperty("diskGb") double diskGb,
- @JsonProperty("bandwidthGbps") Double bandwidthGbps,
- @JsonProperty("diskSpeed") String diskSpeed) {
- this.vcpu = requirePositive("vcpu", vcpu);
- this.memoryGb = requirePositive("memoryGb", memoryGb);
- this.diskGb = requirePositive("diskGb", diskGb);
- this.bandwidthGbps = requirePositive("bandwidthGbps", Optional.ofNullable(bandwidthGbps).orElse(0.3));
- this.diskSpeed = Optional.ofNullable(diskSpeed).orElse("fast");
-
- if (!validDiskSpeeds.contains(this.diskSpeed))
- throw new IllegalArgumentException("Invalid diskSpeed, valid values are: " + validDiskSpeeds + ", got: " + diskSpeed);
- }
-
- public double vcpu() {
- return vcpu;
- }
-
- public double memoryGb() {
- return memoryGb;
- }
-
- public double diskGb() {
- return diskGb;
- }
-
- public double bandwidthGbps() {
- return bandwidthGbps;
- }
-
- public String diskSpeed() {
- return diskSpeed;
- }
-
- private static double requirePositive(String name, double value) {
- if (value <= 0)
- throw new IllegalArgumentException("'" + name + "' must be positive, was " + value);
- return value;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- NodeResources resources = (NodeResources) o;
- return Double.compare(resources.vcpu, vcpu) == 0 &&
- Double.compare(resources.memoryGb, memoryGb) == 0 &&
- Double.compare(resources.diskGb, diskGb) == 0 &&
- Double.compare(resources.bandwidthGbps, bandwidthGbps) == 0 &&
- diskSpeed.equals(resources.diskSpeed);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(vcpu, memoryGb, diskGb, bandwidthGbps, diskSpeed);
- }
-}
diff --git a/flags/src/main/java/com/yahoo/vespa/flags/custom/SharedHost.java b/flags/src/main/java/com/yahoo/vespa/flags/custom/SharedHost.java
new file mode 100644
index 00000000000..e463159eb8f
--- /dev/null
+++ b/flags/src/main/java/com/yahoo/vespa/flags/custom/SharedHost.java
@@ -0,0 +1,64 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.flags.custom;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.yahoo.vespa.flags.Flags;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Defines properties related to shared hosts, see {@link Flags#SHARED_HOST}.
+ *
+ * @author hakon
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(value = JsonInclude.Include.NON_NULL)
+public class SharedHost {
+ private final List<HostResources> resources;
+
+ public static SharedHost createDisabled() {
+ return new SharedHost(null);
+ }
+
+ @JsonCreator
+ public SharedHost(@JsonProperty("resources") List<HostResources> resources) {
+ this.resources = resources == null ? List.of() : List.copyOf(resources);
+ }
+
+ @JsonProperty("resources")
+ public List<HostResources> getResourcesOrNull() {
+ return resources.isEmpty() ? null : resources;
+ }
+
+ @JsonIgnore
+ public List<HostResources> getHostResources() {
+ return resources;
+ }
+
+ public boolean isEnabled() {
+ return resources.size() > 0;
+ }
+
+ @Override
+ public String toString() {
+ return resources.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SharedHost that = (SharedHost) o;
+ return resources.equals(that.resources);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(resources);
+ }
+}
diff --git a/flags/src/test/java/com/yahoo/vespa/flags/FlagsTest.java b/flags/src/test/java/com/yahoo/vespa/flags/FlagsTest.java
index 0f486794c3a..28e84bcf3e5 100644
--- a/flags/src/test/java/com/yahoo/vespa/flags/FlagsTest.java
+++ b/flags/src/test/java/com/yahoo/vespa/flags/FlagsTest.java
@@ -4,6 +4,8 @@ package com.yahoo.vespa.flags;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.node.BooleanNode;
+import com.yahoo.vespa.flags.custom.HostResources;
+import com.yahoo.vespa.flags.custom.SharedHost;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -107,6 +109,15 @@ public class FlagsTest {
List.of(instance));
}
+ @Test
+ public void testSharedHostFlag() {
+ SharedHost sharedHost = new SharedHost(List.of(new HostResources(
+ 4.0, 16.0, 50.0, null,
+ "fast", "local",
+ 10)));
+ testGeneric(Flags.SHARED_HOST, sharedHost);
+ }
+
private <T> void testGeneric(UnboundFlag<T, ?, ?> unboundFlag, T value) {
FlagSource source = mock(FlagSource.class);
Flag<T, ?> flag = unboundFlag.bindTo(source);
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainer.java
index 0f70542f141..240e9f49328 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainer.java
@@ -26,6 +26,7 @@ import com.yahoo.yolean.Exceptions;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -113,18 +114,32 @@ public class DynamicProvisioningMaintainer extends NodeRepositoryMaintainer {
/**
- * Provision the nodes necessary to satisfy given capacity.
+ * Provision hosts to ensure there is room to allocate spare nodes.
*
- * @return excess hosts that can safely be deprovisioned, if any
+ * @param advertisedSpareCapacity the advertised resources of the spare nodes
+ * @param nodes list of all nodes
+ * @return excess hosts that can safely be deprovisioned: An excess host 1. contains no nodes allocated
+ * to an application, and assuming the spare nodes have been allocated, and 2. is not parked
+ * without wantToDeprovision (which means an operator is looking at the node).
*/
- private List<Node> provision(List<NodeResources> capacity, NodeList nodes) {
- List<Node> existingHosts = availableHostsOf(nodes);
- if (nodeRepository().zone().getCloud().dynamicProvisioning()) {
- existingHosts = removableHostsOf(existingHosts, nodes);
- } else if (capacity.isEmpty()) {
+ private List<Node> provision(List<NodeResources> advertisedSpareCapacity, NodeList nodes) {
+ if (!nodeRepository().zone().getCloud().dynamicProvisioning()) {
return List.of();
}
- List<Node> excessHosts = new ArrayList<>(existingHosts);
+
+ Map<String, Node> hostsByHostname = new HashMap<>(nodes.hosts().asList().stream()
+ .filter(host -> host.state() != Node.State.parked || host.status().wantToDeprovision())
+ .collect(Collectors.toMap(Node::hostname, Function.identity())));
+
+ nodes.asList().stream()
+ .filter(node -> node.allocation().isPresent())
+ .flatMap(node -> node.parentHostname().stream())
+ .distinct()
+ .forEach(hostsByHostname::remove);
+
+ List<Node> excessHosts = new ArrayList<>(hostsByHostname.values());
+
+ var capacity = new ArrayList<>(advertisedSpareCapacity);
for (Iterator<NodeResources> it = capacity.iterator(); it.hasNext() && !excessHosts.isEmpty(); ) {
NodeResources resources = it.next();
excessHosts.stream()
@@ -138,12 +153,13 @@ public class DynamicProvisioningMaintainer extends NodeRepositoryMaintainer {
it.remove();
});
}
+
// Pre-provisioning is best effort, do one host at a time
capacity.forEach(resources -> {
try {
Version osVersion = nodeRepository().osVersions().targetFor(NodeType.host).orElse(Version.emptyVersion);
List<Node> hosts = hostProvisioner.provisionHosts(nodeRepository().database().getProvisionIndexes(1),
- resources, preprovisionAppId, osVersion)
+ resources, preprovisionAppId, osVersion, false)
.stream()
.map(ProvisionedHost::generateHost)
.collect(Collectors.toList());
@@ -154,7 +170,8 @@ public class DynamicProvisioningMaintainer extends NodeRepositoryMaintainer {
log.log(Level.WARNING, "Failed to pre-provision " + resources + ", will retry in " + interval(), e);
}
});
- return removableHostsOf(excessHosts, nodes);
+
+ return excessHosts;
}
@@ -178,27 +195,4 @@ public class DynamicProvisioningMaintainer extends NodeRepositoryMaintainer {
}
}
}
-
- /** Returns hosts that are considered available, i.e. not parked or flagged for deprovisioning */
- private static List<Node> availableHostsOf(NodeList nodes) {
- return nodes.hosts()
- .matching(host -> host.state() != Node.State.parked || host.status().wantToDeprovision())
- .asList();
- }
-
- /** Returns the subset of given hosts that have no containers and are thus removable */
- private static List<Node> removableHostsOf(List<Node> hosts, NodeList allNodes) {
- Map<String, Node> hostsByHostname = hosts.stream()
- .collect(Collectors.toMap(Node::hostname,
- Function.identity()));
-
- allNodes.asList().stream()
- .filter(node -> node.allocation().isPresent())
- .flatMap(node -> node.parentHostname().stream())
- .distinct()
- .forEach(hostsByHostname::remove);
-
- return List.copyOf(hostsByHostname.values());
- }
-
}
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 99557cb0908..f9960716162 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
@@ -88,7 +88,7 @@ public class GroupPreparer {
.map(deficit -> hostProvisioner.get().provisionHosts(nodeRepository.database().getProvisionIndexes(deficit.getCount()),
deficit.getFlavor(),
application,
- osVersion))
+ osVersion, false))
.orElseGet(List::of);
// At this point we have started provisioning of the hosts, the first priority is to make sure that
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostProvisioner.java
index 816ef9459ba..5a7456ab997 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostProvisioner.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostProvisioner.java
@@ -21,14 +21,16 @@ public interface HostProvisioner {
*
* @param provisionIndexes list of unique provision indexes which will be used to generate the node hostnames
* on the form of <code>[prefix][index].[domain]</code>
- * @param resources the resources needed per node
+ * @param resources the resources needed per node - the provisioned host may be significantly larger
* @param applicationId id of the application that will own the provisioned host
* @param osVersion the OS version to use. If this version does not exist, implementations may choose a suitable
* fallback version.
+ * @param forceExclusive whether to force the provisioning of an exclusive host.
* @return list of {@link ProvisionedHost} describing the provisioned nodes
*/
List<ProvisionedHost> provisionHosts(List<Integer> provisionIndexes, NodeResources resources,
- ApplicationId applicationId, Version osVersion);
+ ApplicationId applicationId, Version osVersion,
+ boolean forceExclusive);
/**
* Continue provisioning of given list of Nodes.
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java
index 999ceeccc58..03c42bf20ff 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java
@@ -276,7 +276,9 @@ class AutoscalingTester {
}
@Override
- public List<ProvisionedHost> provisionHosts(List<Integer> provisionIndexes, NodeResources resources, ApplicationId applicationId, Version osVersion) {
+ public List<ProvisionedHost> provisionHosts(List<Integer> provisionIndexes, NodeResources resources,
+ ApplicationId applicationId, Version osVersion,
+ boolean forceExclusive) {
Flavor hostFlavor = hostFlavors.stream().filter(f -> matches(f, resources)).findAny()
.orElseThrow(() -> new RuntimeException("No flavor matching " + resources + ". Flavors: " + hostFlavors));
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainerTest.java
index e75f94123c7..c6052829faf 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainerTest.java
@@ -325,7 +325,8 @@ public class DynamicProvisioningMaintainerTest {
}
@Override
- public List<ProvisionedHost> provisionHosts(List<Integer> provisionIndexes, NodeResources resources, ApplicationId applicationId, Version osVersion) {
+ public List<ProvisionedHost> provisionHosts(List<Integer> provisionIndexes, NodeResources resources,
+ ApplicationId applicationId, Version osVersion, boolean forceExclusive) {
Flavor hostFlavor = flavors.getFlavors().stream()
.filter(f -> !f.isDocker())
.filter(f -> f.resources().compatibleWith(resources))
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java
index 41f13748a07..c678f8902b5 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java
@@ -39,6 +39,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -68,7 +69,7 @@ public class DynamicDockerProvisionTest {
mockHostProvisioner(hostProvisioner, tester.nodeRepository().flavors().getFlavorOrThrow("small"));
List<HostSpec> hostSpec = tester.prepare(application1, clusterSpec("myContent.t1.a1"), 4, 1, flavor);
- verify(hostProvisioner).provisionHosts(List.of(100, 101, 102, 103), flavor, application1, Version.emptyVersion);
+ verify(hostProvisioner).provisionHosts(List.of(100, 101, 102, 103), flavor, application1, Version.emptyVersion, false);
// Total of 8 nodes should now be in node-repo, 4 hosts in state provisioned, and 4 reserved nodes
assertEquals(8, tester.nodeRepository().list().size());
@@ -86,7 +87,7 @@ public class DynamicDockerProvisionTest {
List<Integer> expectedProvisionIndexes = List.of(100, 101);
mockHostProvisioner(hostProvisioner, tester.nodeRepository().flavors().getFlavorOrThrow("large"));
tester.prepare(application, clusterSpec("myContent.t2.a2"), 2, 1, flavor);
- verify(hostProvisioner).provisionHosts(expectedProvisionIndexes, flavor, application, Version.emptyVersion);
+ verify(hostProvisioner).provisionHosts(expectedProvisionIndexes, flavor, application, Version.emptyVersion, false);
// Ready the provisioned hosts, add an IP addresses to pool and activate them
for (Integer i : expectedProvisionIndexes) {
@@ -101,7 +102,7 @@ public class DynamicDockerProvisionTest {
mockHostProvisioner(hostProvisioner, tester.nodeRepository().flavors().getFlavorOrThrow("small"));
tester.prepare(application, clusterSpec("another-id"), 2, 1, flavor);
// Verify there was only 1 call to provision hosts (during the first prepare)
- verify(hostProvisioner).provisionHosts(any(), any(), any(), any());
+ verify(hostProvisioner).provisionHosts(any(), any(), any(), any(), anyBoolean());
// Node-repo should now consist of 2 active hosts with 2 reserved nodes on each
assertEquals(6, tester.nodeRepository().list().size());
@@ -345,7 +346,7 @@ public class DynamicDockerProvisionTest {
return provisionIndexes.stream()
.map(i -> new ProvisionedHost("id-" + i, "host-" + i, hostFlavor, "host-" + i + "-1", nodeResources, Version.emptyVersion))
.collect(Collectors.toList());
- }).when(hostProvisioner).provisionHosts(any(), any(), any(), any());
+ }).when(hostProvisioner).provisionHosts(any(), any(), any(), any(), anyBoolean());
}
private static class MockHostProvisioner implements HostProvisioner {
@@ -359,7 +360,7 @@ public class DynamicDockerProvisionTest {
}
@Override
- public List<ProvisionedHost> provisionHosts(List<Integer> provisionIndexes, NodeResources resources, ApplicationId applicationId, Version osVersion) {
+ public List<ProvisionedHost> provisionHosts(List<Integer> provisionIndexes, NodeResources resources, ApplicationId applicationId, Version osVersion, boolean forceExclusive) {
Optional<Flavor> hostFlavor = hostFlavors.stream().filter(f -> compatible(f, resources)).findFirst();
if (hostFlavor.isEmpty())
throw new OutOfCapacityException("No host flavor matches " + resources);