aboutsummaryrefslogtreecommitdiffstats
path: root/node-repository
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2021-03-01 18:38:36 +0100
committerMartin Polden <mpolden@mpolden.no>2021-03-02 20:12:27 +0100
commite49336237655b4adb7e4da5f29d6267a6d660ed2 (patch)
tree5ce52b4434cfd0b6ff8135da8d02eda37c235db4 /node-repository
parentdf5d09f858e23d80ab24bfbd76ae532be9a2f0f8 (diff)
Consolidate HostProvisioner mocks
Diffstat (limited to 'node-repository')
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockHostProvisioner.java173
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java48
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainerTest.java158
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java49
4 files changed, 202 insertions, 226 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockHostProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockHostProvisioner.java
new file mode 100644
index 00000000000..4c9323ca68e
--- /dev/null
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockHostProvisioner.java
@@ -0,0 +1,173 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.provision.testutils;
+
+import com.yahoo.component.Version;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.Flavor;
+import com.yahoo.config.provision.NodeResources;
+import com.yahoo.config.provision.OutOfCapacityException;
+import com.yahoo.vespa.hosted.provision.Node;
+import com.yahoo.vespa.hosted.provision.node.Address;
+import com.yahoo.vespa.hosted.provision.node.IP;
+import com.yahoo.vespa.hosted.provision.provisioning.FatalProvisioningException;
+import com.yahoo.vespa.hosted.provision.provisioning.HostProvisioner;
+import com.yahoo.vespa.hosted.provision.provisioning.ProvisionedHost;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumSet;
+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.IntStream;
+
+/**
+ * @author mpolden
+ */
+public class MockHostProvisioner implements HostProvisioner {
+
+ private final List<ProvisionedHost> provisionedHosts = new ArrayList<>();
+ private final List<Flavor> flavors;
+ private final MockNameResolver nameResolver;
+ private final int memoryTaxGb;
+
+ private int deprovisionedHosts = 0;
+ private EnumSet<Behaviour> behaviours = EnumSet.noneOf(Behaviour.class);
+ private Optional<Flavor> hostFlavor = Optional.empty();
+
+ public MockHostProvisioner(List<Flavor> flavors, MockNameResolver nameResolver, int memoryTaxGb) {
+ this.flavors = List.copyOf(flavors);
+ this.nameResolver = nameResolver;
+ this.memoryTaxGb = memoryTaxGb;
+ }
+
+ public MockHostProvisioner(List<Flavor> flavors) {
+ this(flavors, 0);
+ }
+
+ public MockHostProvisioner(List<Flavor> flavors, int memoryTaxGb) {
+ this(flavors, new MockNameResolver().mockAnyLookup(), memoryTaxGb);
+ }
+
+ @Override
+ public List<ProvisionedHost> provisionHosts(List<Integer> provisionIndexes, NodeResources resources,
+ ApplicationId applicationId, Version osVersion, HostSharing sharing) {
+ Flavor hostFlavor = this.hostFlavor.orElseGet(() -> flavors.stream().filter(f -> compatible(f, resources))
+ .findFirst()
+ .orElseThrow(() -> new OutOfCapacityException("No host flavor matches " + resources)));
+ List<ProvisionedHost> hosts = new ArrayList<>();
+ for (int index : provisionIndexes) {
+ hosts.add(new ProvisionedHost("host" + index,
+ "hostname" + index,
+ hostFlavor,
+ Optional.empty(),
+ createAddressesForHost(hostFlavor, index),
+ resources,
+ osVersion));
+ }
+ provisionedHosts.addAll(hosts);
+ return hosts;
+ }
+
+ @Override
+ public List<Node> provision(Node host, Set<Node> children) throws FatalProvisioningException {
+ if (behaviours.contains(Behaviour.failProvisioning)) throw new FatalProvisioningException("Failed to provision node(s)");
+ if (host.state() != Node.State.provisioned) throw new IllegalStateException("Host to provision must be in " + Node.State.provisioned);
+ List<Node> result = new ArrayList<>();
+ result.add(withIpAssigned(host));
+ for (var child : children) {
+ if (child.state() != Node.State.reserved) throw new IllegalStateException("Child to provisioned must be in " + Node.State.reserved);
+ result.add(withIpAssigned(child));
+ }
+ return result;
+ }
+
+ @Override
+ public void deprovision(Node host) {
+ if (behaviours.contains(Behaviour.failDeprovisioning)) throw new FatalProvisioningException("Failed to deprovision node");
+ provisionedHosts.removeIf(provisionedHost -> provisionedHost.hostHostname().equals(host.hostname()));
+ deprovisionedHosts++;
+ }
+
+ /** Returns the hosts that have been provisioned by this */
+ public List<ProvisionedHost> provisionedHosts() {
+ return Collections.unmodifiableList(provisionedHosts);
+ }
+
+ /** Returns the number of hosts deprovisioned by this */
+ public int deprovisionedHosts() {
+ return deprovisionedHosts;
+ }
+
+ public MockHostProvisioner with(Behaviour first, Behaviour... rest) {
+ this.behaviours = EnumSet.of(first, rest);
+ return this;
+ }
+
+ public MockHostProvisioner without(Behaviour first, Behaviour... rest) {
+ Set<Behaviour> behaviours = new HashSet<>(this.behaviours);
+ behaviours.removeAll(EnumSet.of(first, rest));
+ this.behaviours = behaviours.isEmpty() ? EnumSet.noneOf(Behaviour.class) : EnumSet.copyOf(behaviours);
+ return this;
+ }
+
+ public MockHostProvisioner overrideHostFlavor(String flavorName) {
+ Flavor flavor = flavors.stream().filter(f -> f.name().equals(flavorName))
+ .findFirst()
+ .orElseThrow(() -> new IllegalArgumentException("No such flavor '" + flavorName + "'"));
+ hostFlavor = Optional.of(flavor);
+ return this;
+ }
+
+ public boolean compatible(Flavor flavor, NodeResources resources) {
+ NodeResources resourcesToVerify = resources.withMemoryGb(resources.memoryGb() - memoryTaxGb);
+
+ if (flavor.resources().storageType() == NodeResources.StorageType.remote
+ && flavor.resources().diskGb() >= resources.diskGb())
+ resourcesToVerify = resourcesToVerify.withDiskGb(flavor.resources().diskGb());
+ if (flavor.resources().bandwidthGbps() >= resources.bandwidthGbps())
+ resourcesToVerify = resourcesToVerify.withBandwidthGbps(flavor.resources().bandwidthGbps());
+ return flavor.resources().compatibleWith(resourcesToVerify);
+ }
+
+ private List<Address> createAddressesForHost(Flavor flavor, int hostIndex) {
+ long numAddresses = Math.max(1, Math.round(flavor.resources().bandwidthGbps()));
+ return IntStream.range(0, (int) numAddresses)
+ .mapToObj(i -> new Address("nodename" + hostIndex + "_" + i))
+ .collect(Collectors.toList());
+ }
+
+ private Node withIpAssigned(Node node) {
+ if (node.parentHostname().isPresent()) return node;
+ int hostIndex = Integer.parseInt(node.hostname().replaceAll("^[a-z]+|-\\d+$", ""));
+ Set<String> addresses = Set.of("::" + hostIndex + ":0");
+ Set<String> ipAddressPool = new HashSet<>();
+ if (!behaviours.contains(Behaviour.failDnsUpdate)) {
+ nameResolver.addRecord(node.hostname(), addresses.iterator().next());
+ for (int i = 1; i <= 2; i++) {
+ String ip = "::" + hostIndex + ":" + i;
+ ipAddressPool.add(ip);
+ nameResolver.addRecord(node.hostname() + "-" + i, ip);
+ }
+ }
+
+ IP.Pool pool = node.ipConfig().pool().withIpAddresses(ipAddressPool);
+ return node.with(node.ipConfig().withPrimary(addresses).withPool(pool));
+ }
+
+ public enum Behaviour {
+
+ /** Fail all calls to {@link MockHostProvisioner#provision(com.yahoo.vespa.hosted.provision.Node, java.util.Set)} */
+ failProvisioning,
+
+ /** Fail all calls to {@link MockHostProvisioner#deprovision(com.yahoo.vespa.hosted.provision.Node)} */
+ failDeprovisioning,
+
+ /** Fail DNS updates of provisioned hosts */
+ failDnsUpdate,
+
+ }
+
+}
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 ad6eb6535c2..156542ef1d4 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
@@ -2,7 +2,6 @@
package com.yahoo.vespa.hosted.provision.autoscale;
import com.yahoo.collections.Pair;
-import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Capacity;
import com.yahoo.config.provision.ClusterResources;
@@ -21,17 +20,12 @@ import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.Nodelike;
import com.yahoo.vespa.hosted.provision.applications.Application;
-import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.IP;
-import com.yahoo.vespa.hosted.provision.provisioning.FatalProvisioningException;
-import com.yahoo.vespa.hosted.provision.provisioning.HostProvisioner;
import com.yahoo.vespa.hosted.provision.provisioning.HostResourcesCalculator;
-import com.yahoo.vespa.hosted.provision.provisioning.ProvisionedHost;
import com.yahoo.vespa.hosted.provision.provisioning.ProvisioningTester;
import java.time.Duration;
-import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -39,6 +33,9 @@ import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+/**
+ * @author bratseth
+ */
class AutoscalingTester {
private final ProvisioningTester provisioningTester;
@@ -299,45 +296,14 @@ class AutoscalingTester {
}
- private class MockHostProvisioner implements HostProvisioner {
+ private class MockHostProvisioner extends com.yahoo.vespa.hosted.provision.testutils.MockHostProvisioner {
- private final List<Flavor> hostFlavors;
-
- public MockHostProvisioner(List<Flavor> hostFlavors) {
- this.hostFlavors = hostFlavors;
- }
-
- @Override
- public List<ProvisionedHost> provisionHosts(List<Integer> provisionIndexes, NodeResources resources,
- ApplicationId applicationId, Version osVersion,
- HostSharing sharing) {
- Flavor hostFlavor = hostFlavors.stream().filter(f -> matches(f, resources)).findAny()
- .orElseThrow(() -> new RuntimeException("No flavor matching " + resources + ". Flavors: " + hostFlavors));
-
- List<ProvisionedHost> hosts = new ArrayList<>();
- for (int index : provisionIndexes) {
- hosts.add(new ProvisionedHost("host" + index,
- "hostname" + index,
- hostFlavor,
- Optional.empty(),
- List.of(new Address("nodename" + index)),
- resources,
- osVersion));
- }
- return hosts;
+ public MockHostProvisioner(List<Flavor> flavors) {
+ super(flavors);
}
@Override
- public List<Node> provision(Node host, Set<Node> children) throws FatalProvisioningException {
- throw new RuntimeException("Not implemented");
- }
-
- @Override
- public void deprovision(Node host) {
- throw new RuntimeException("Not implemented");
- }
-
- private boolean matches(Flavor flavor, NodeResources resources) {
+ public boolean compatible(Flavor flavor, NodeResources resources) {
NodeResources flavorResources = hostResourcesCalculator.advertisedResourcesOf(flavor);
if (flavorResources.storageType() == NodeResources.StorageType.remote
&& resources.diskGb() <= flavorResources.diskGb())
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 56cf8d02149..43a7be81c88 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
@@ -27,11 +27,9 @@ import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.Allocation;
import com.yahoo.vespa.hosted.provision.node.Generation;
import com.yahoo.vespa.hosted.provision.node.IP;
-import com.yahoo.vespa.hosted.provision.provisioning.FatalProvisioningException;
import com.yahoo.vespa.hosted.provision.provisioning.FlavorConfigBuilder;
-import com.yahoo.vespa.hosted.provision.provisioning.HostProvisioner;
-import com.yahoo.vespa.hosted.provision.provisioning.ProvisionedHost;
import com.yahoo.vespa.hosted.provision.provisioning.ProvisioningTester;
+import com.yahoo.vespa.hosted.provision.testutils.MockHostProvisioner;
import com.yahoo.vespa.hosted.provision.testutils.MockNameResolver;
import com.yahoo.vespa.service.duper.ConfigServerApplication;
import com.yahoo.vespa.service.duper.ConfigServerHostApplication;
@@ -39,20 +37,15 @@ import org.junit.Test;
import java.time.Duration;
import java.time.Instant;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
-import java.util.stream.IntStream;
import java.util.stream.Stream;
-import static com.yahoo.vespa.hosted.provision.maintenance.DynamicProvisioningMaintainerTest.MockHostProvisioner.Behaviour;
+import static com.yahoo.vespa.hosted.provision.testutils.MockHostProvisioner.Behaviour;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
/**
@@ -115,7 +108,7 @@ public class DynamicProvisioningMaintainerTest {
tester.maintainer.maintain();
assertTrue("Failed host is deprovisioned", tester.nodeRepository.nodes().node(failedHost.get().hostname()).isEmpty());
- assertEquals(1, tester.hostProvisioner.deprovisionedHosts);
+ assertEquals(1, tester.hostProvisioner.deprovisionedHosts());
}
@Test
@@ -126,7 +119,7 @@ public class DynamicProvisioningMaintainerTest {
new ClusterCapacity(1, 16, 24, 100, 1.0)),
ClusterCapacity.class);
- assertEquals(0, tester.hostProvisioner.provisionedHosts.size());
+ assertEquals(0, tester.hostProvisioner.provisionedHosts().size());
assertEquals(11, tester.nodeRepository.nodes().list().size());
assertTrue(tester.nodeRepository.nodes().node("host2").isPresent());
assertTrue(tester.nodeRepository.nodes().node("host2-1").isPresent());
@@ -136,7 +129,7 @@ public class DynamicProvisioningMaintainerTest {
tester.maintainer.maintain();
- assertEquals(2, tester.hostProvisioner.provisionedHosts.size());
+ assertEquals(2, tester.hostProvisioner.provisionedHosts().size());
assertEquals(2, tester.provisionedHostsMatching(new NodeResources(48, 128, 1000, 10)));
NodeList nodesAfter = tester.nodeRepository.nodes().list();
assertEquals(11, nodesAfter.size()); // 2 removed, 2 added
@@ -151,13 +144,13 @@ public class DynamicProvisioningMaintainerTest {
public void preprovision_with_shared_host() {
var tester = new DynamicProvisioningTester().addInitialNodes();
// Makes provisioned hosts 48-128-1000-10
- tester.hostProvisioner.provisionSharedHost("host4");
+ tester.hostProvisioner.overrideHostFlavor("host4");
tester.flagSource.withListFlag(PermanentFlags.PREPROVISION_CAPACITY.id(),
List.of(new ClusterCapacity(2, 1, 30, 20, 3.0)),
ClusterCapacity.class);
- assertEquals(0, tester.hostProvisioner.provisionedHosts.size());
+ assertEquals(0, tester.hostProvisioner.provisionedHosts().size());
assertEquals(11, tester.nodeRepository.nodes().list().size());
assertTrue(tester.nodeRepository.nodes().node("host2").isPresent());
assertTrue(tester.nodeRepository.nodes().node("host2-1").isPresent());
@@ -196,7 +189,7 @@ public class DynamicProvisioningMaintainerTest {
tester.maintainer.maintain();
- assertEquals(2, tester.hostProvisioner.provisionedHosts.size());
+ assertEquals(2, tester.hostProvisioner.provisionedHosts().size());
assertEquals(2, tester.provisionedHostsMatching(new NodeResources(48, 128, 1000, 10)));
assertEquals(10, tester.nodeRepository.nodes().list().size()); // 3 removed, 2 added
assertTrue("preprovision capacity is prefered on shared hosts", tester.nodeRepository.nodes().node("host3").isEmpty());
@@ -212,7 +205,7 @@ public class DynamicProvisioningMaintainerTest {
tester.maintainer.maintain();
assertEquals("one provisioned host has been deprovisioned, so there are 2 -> 1 provisioned hosts",
- 1, tester.hostProvisioner.provisionedHosts.size());
+ 1, tester.hostProvisioner.provisionedHosts().size());
assertEquals(1, tester.provisionedHostsMatching(new NodeResources(48, 128, 1000, 10)));
assertEquals(9, tester.nodeRepository.nodes().list().size()); // 4 removed, 2 added
if (tester.nodeRepository.nodes().node("hostname100").isPresent()) {
@@ -226,7 +219,7 @@ public class DynamicProvisioningMaintainerTest {
}
private void verifyFirstMaintain(DynamicProvisioningTester tester) {
- assertEquals(1, tester.hostProvisioner.provisionedHosts.size());
+ assertEquals(1, tester.hostProvisioner.provisionedHosts().size());
assertEquals(1, tester.provisionedHostsMatching(new NodeResources(48, 128, 1000, 10)));
assertEquals(10, tester.nodeRepository.nodes().list().size()); // 2 removed, 1 added
assertTrue("Failed host 'host2' is deprovisioned", tester.nodeRepository.nodes().node("host2").isEmpty());
@@ -266,17 +259,17 @@ public class DynamicProvisioningMaintainerTest {
private void assertWithMinCount(int minCount, int provisionCount, int deprovisionCount) {
var tester = new DynamicProvisioningTester().addInitialNodes();
- tester.hostProvisioner.provisionSharedHost("host4");
+ tester.hostProvisioner.overrideHostFlavor("host4");
tester.flagSource.withJacksonFlag(PermanentFlags.SHARED_HOST.id(), new SharedHost(null, minCount), SharedHost.class);
tester.maintainer.maintain();
- assertEquals(provisionCount, tester.hostProvisioner.provisionedHosts.size());
- assertEquals(deprovisionCount, tester.hostProvisioner.deprovisionedHosts);
+ assertEquals(provisionCount, tester.hostProvisioner.provisionedHosts().size());
+ assertEquals(deprovisionCount, tester.hostProvisioner.deprovisionedHosts());
// Verify next maintain is a no-op
tester.maintainer.maintain();
- assertEquals(provisionCount, tester.hostProvisioner.provisionedHosts.size());
- assertEquals(deprovisionCount, tester.hostProvisioner.deprovisionedHosts);
+ assertEquals(provisionCount, tester.hostProvisioner.provisionedHosts().size());
+ assertEquals(deprovisionCount, tester.hostProvisioner.deprovisionedHosts());
}
@Test
@@ -300,14 +293,14 @@ public class DynamicProvisioningMaintainerTest {
// Hosts are provisioned
assertEquals(2, tester.provisionedHostsMatching(resources1));
- assertEquals(0, tester.hostProvisioner.deprovisionedHosts);
+ assertEquals(0, tester.hostProvisioner.deprovisionedHosts());
// Next maintenance run does nothing
tester.assertNodesUnchanged();
// Pretend shared-host flag has been set to host4's flavor
var sharedHostNodeResources = new NodeResources(48, 128, 1000, 10, NodeResources.DiskSpeed.fast, NodeResources.StorageType.remote);
- tester.hostProvisioner.provisionSharedHost("host4");
+ tester.hostProvisioner.overrideHostFlavor("host4");
// Next maintenance run does nothing
tester.assertNodesUnchanged();
@@ -456,7 +449,7 @@ public class DynamicProvisioningMaintainerTest {
public DynamicProvisioningTester(Cloud cloud) {
MockNameResolver nameResolver = new MockNameResolver();
- this.hostProvisioner = new MockHostProvisioner(flavors, nameResolver);
+ this.hostProvisioner = new MockHostProvisioner(flavors.getFlavors(), nameResolver, 0);
this.provisioningTester = new ProvisioningTester.Builder().zone(new Zone(cloud, SystemName.defaultSystem(),
Environment.defaultEnvironment(),
RegionName.defaultName()))
@@ -529,9 +522,9 @@ public class DynamicProvisioningMaintainerTest {
}
private long provisionedHostsMatching(NodeResources resources) {
- return hostProvisioner.provisionedHosts.stream()
- .filter(host -> host.generateHost().resources().compatibleWith(resources))
- .count();
+ return hostProvisioner.provisionedHosts().stream()
+ .filter(host -> host.generateHost().resources().compatibleWith(resources))
+ .count();
}
private void assertNodesUnchanged() {
@@ -542,113 +535,4 @@ public class DynamicProvisioningMaintainerTest {
}
- static class MockHostProvisioner implements HostProvisioner {
-
- private final List<ProvisionedHost> provisionedHosts = new ArrayList<>();
- private final NodeFlavors flavors;
- private final MockNameResolver nameResolver;
-
- private int deprovisionedHosts = 0;
- private EnumSet<Behaviour> behaviours = EnumSet.noneOf(Behaviour.class);
- private Optional<Flavor> provisionHostFlavor = Optional.empty();
-
- public MockHostProvisioner(NodeFlavors flavors, MockNameResolver nameResolver) {
- this.flavors = flavors;
- this.nameResolver = nameResolver;
- }
-
- public MockHostProvisioner provisionSharedHost(String flavorName) {
- provisionHostFlavor = Optional.of(flavors.getFlavorOrThrow(flavorName));
- return this;
- }
-
- @Override
- public List<ProvisionedHost> provisionHosts(List<Integer> provisionIndexes, NodeResources resources,
- ApplicationId applicationId, Version osVersion, HostSharing sharing) {
- Flavor hostFlavor = provisionHostFlavor
- .orElseGet(() -> flavors.getFlavors().stream()
- .filter(f -> !f.isDocker())
- .filter(f -> f.resources().compatibleWith(resources))
- .findFirst()
- .orElseThrow(() -> new IllegalArgumentException("No host flavor found satisfying " + resources)));
-
- List<ProvisionedHost> hosts = new ArrayList<>();
- for (int index : provisionIndexes) {
- hosts.add(new ProvisionedHost("host" + index,
- "hostname" + index,
- hostFlavor,
- Optional.empty(),
- createAddressesForHost(hostFlavor, index),
- resources,
- osVersion));
- }
- provisionedHosts.addAll(hosts);
- return hosts;
- }
-
- private List<Address> createAddressesForHost(Flavor flavor, int hostIndex) {
- long numAddresses = Math.max(1, Math.round(flavor.resources().bandwidthGbps()));
- return IntStream.range(0, (int) numAddresses)
- .mapToObj(i -> new Address("nodename" + hostIndex + "_" + i))
- .collect(Collectors.toList());
- }
-
- @Override
- public List<Node> provision(Node host, Set<Node> children) throws FatalProvisioningException {
- if (behaviours.contains(Behaviour.failProvisioning)) throw new FatalProvisioningException("Failed to provision node(s)");
- assertSame(Node.State.provisioned, host.state());
- List<Node> result = new ArrayList<>();
- result.add(withIpAssigned(host));
- for (var child : children) {
- assertSame(Node.State.reserved, child.state());
- result.add(withIpAssigned(child));
- }
- return result;
- }
-
- @Override
- public void deprovision(Node host) {
- if (behaviours.contains(Behaviour.failDeprovisioning)) throw new FatalProvisioningException("Failed to deprovision node");
- provisionedHosts.removeIf(provisionedHost -> provisionedHost.hostHostname().equals(host.hostname()));
- deprovisionedHosts++;
- }
-
- private MockHostProvisioner with(Behaviour first, Behaviour... rest) {
- this.behaviours = EnumSet.of(first, rest);
- return this;
- }
-
- private MockHostProvisioner without(Behaviour first, Behaviour... rest) {
- Set<Behaviour> behaviours = new HashSet<>(this.behaviours);
- behaviours.removeAll(EnumSet.of(first, rest));
- this.behaviours = behaviours.isEmpty() ? EnumSet.noneOf(Behaviour.class) : EnumSet.copyOf(behaviours);
- return this;
- }
-
- private Node withIpAssigned(Node node) {
- if (node.parentHostname().isPresent()) return node;
- int hostIndex = Integer.parseInt(node.hostname().replaceAll("^[a-z]+|-\\d+$", ""));
- Set<String> addresses = Set.of("::" + hostIndex + ":0");
- Set<String> ipAddressPool = new HashSet<>();
- if (!behaviours.contains(Behaviour.failDnsUpdate)) {
- nameResolver.addRecord(node.hostname(), addresses.iterator().next());
- for (int i = 1; i <= 2; i++) {
- String ip = "::" + hostIndex + ":" + i;
- ipAddressPool.add(ip);
- nameResolver.addRecord(node.hostname() + "-" + i, ip);
- }
- }
-
- IP.Pool pool = node.ipConfig().pool().withIpAddresses(ipAddressPool);
- return node.with(node.ipConfig().withPrimary(addresses).withPool(pool));
- }
-
- enum Behaviour {
- failProvisioning,
- failDeprovisioning,
- failDnsUpdate,
- }
-
- }
-
}
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 131c02015a1..3b8e7075488 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
@@ -14,22 +14,20 @@ import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeResources.DiskSpeed;
import com.yahoo.config.provision.NodeResources.StorageType;
import com.yahoo.config.provision.NodeType;
-import com.yahoo.config.provision.OutOfCapacityException;
import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
-import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.provisioning.HostProvisioner.HostSharing;
+import com.yahoo.vespa.hosted.provision.testutils.MockHostProvisioner;
import com.yahoo.vespa.hosted.provision.testutils.MockNameResolver;
import org.junit.Test;
import java.time.Instant;
import java.util.List;
-import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -454,49 +452,4 @@ public class DynamicDockerProvisionTest {
}).when(hostProvisioner).provisionHosts(any(), any(), any(), any(), any());
}
- private static class MockHostProvisioner implements HostProvisioner {
-
- private final List<Flavor> hostFlavors;
- private final int memoryTaxGb;
-
- public MockHostProvisioner(List<Flavor> hostFlavors, int memoryTaxGb) {
- this.hostFlavors = List.copyOf(hostFlavors);
- this.memoryTaxGb = memoryTaxGb;
- }
-
- @Override
- public List<ProvisionedHost> provisionHosts(List<Integer> provisionIndexes, NodeResources resources,
- ApplicationId applicationId, Version osVersion, HostSharing sharing) {
- Optional<Flavor> hostFlavor = hostFlavors.stream().filter(f -> compatible(f, resources)).findFirst();
- if (hostFlavor.isEmpty())
- throw new OutOfCapacityException("No host flavor matches " + resources);
- return provisionIndexes.stream()
- .map(i -> new ProvisionedHost("id-" + i, "host-" + i, hostFlavor.get(), Optional.empty(),
- List.of(new Address("host-" + i + "-1")), resources, osVersion))
- .collect(Collectors.toList());
- }
-
- private boolean compatible(Flavor hostFlavor, NodeResources resources) {
- NodeResources resourcesToVerify = resources.withMemoryGb(resources.memoryGb() - memoryTaxGb);
-
- if (hostFlavor.resources().storageType() == NodeResources.StorageType.remote
- && hostFlavor.resources().diskGb() >= resources.diskGb())
- resourcesToVerify = resourcesToVerify.withDiskGb(hostFlavor.resources().diskGb());
- if (hostFlavor.resources().bandwidthGbps() >= resources.bandwidthGbps())
- resourcesToVerify = resourcesToVerify.withBandwidthGbps(hostFlavor.resources().bandwidthGbps());
- return hostFlavor.resources().compatibleWith(resourcesToVerify);
- }
-
- @Override
- public List<Node> provision(Node host, Set<Node> children) throws FatalProvisioningException {
- throw new RuntimeException("Not implemented: provision");
- }
-
- @Override
- public void deprovision(Node host) {
- throw new RuntimeException("Not implemented: deprovision");
- }
-
- }
-
}