diff options
author | Jon Bratseth <bratseth@gmail.com> | 2022-11-28 22:58:01 +0100 |
---|---|---|
committer | Jon Bratseth <bratseth@gmail.com> | 2022-11-29 17:02:20 +0100 |
commit | 99d87eb3689a0d606bd2aef18ef126e377192dc2 (patch) | |
tree | 8487b5d45a65499335db5c2015ce40ec99c4d650 | |
parent | 7d4c85bd682642d0abe0de89c3b5a589cfa9e667 (diff) |
More realistic tests
9 files changed, 99 insertions, 120 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Address.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Address.java index fd9771103de..532e6747d9a 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Address.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Address.java @@ -9,6 +9,7 @@ import java.util.Objects; * @author hakon */ public class Address { + private final String hostname; public Address(String hostname) { 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 ad2973ff435..f0848f5caeb 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 @@ -74,9 +74,9 @@ public class GroupPreparer { public PrepareResult prepare(ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes, List<Node> surplusActiveNodes, NodeIndices indices, int wantedGroups, NodesAndHosts<LockedNodeList> allNodesAndHosts) { - log.log(Level.FINE, "Preparing " + cluster.type().name() + " " + cluster.id() + " with requested resources " + requestedNodes.resources().orElse(NodeResources.unspecified())); - // Try preparing in memory without global unallocated lock. Most of the time there should be no changes and we - // can return nodes previously allocated. + log.log(Level.FINE, () -> "Preparing " + cluster.type().name() + " " + cluster.id() + " with requested resources " + requestedNodes.resources().orElse(NodeResources.unspecified())); + // Try preparing in memory without global unallocated lock. Most of the time there should be no changes, + // and we can return nodes previously allocated. NodeAllocation probeAllocation = prepareAllocation(application, cluster, requestedNodes, surplusActiveNodes, indices::probeNext, wantedGroups, allNodesAndHosts); if (probeAllocation.fulfilledAndNoChanges()) { 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 178b42096e6..21efd1b014c 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 @@ -34,8 +34,7 @@ import java.util.logging.Logger; import java.util.stream.Collectors; /** - * Used to manage a list of nodes during the node reservation process - * in order to fulfill the nodespec. + * Used to manage a list of nodes during the node reservation process to fulfill the nodespec. * * @author bratseth */ @@ -316,7 +315,7 @@ class NodeAllocation { * Returns {@link HostDeficit} describing the host deficit for the given {@link NodeSpec}. * * @return empty if the requested spec is already fulfilled. Otherwise returns {@link HostDeficit} containing the - * flavor and host count required to cover the deficit. + * flavor and host count required to cover the deficit. */ Optional<HostDeficit> hostDeficit() { if (nodeType().isHost()) { @@ -521,6 +520,11 @@ class NodeAllocation { return count; } + @Override + public String toString() { + return "deficit of " + count + " nodes with " + resources; + } + } } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java index fe4eb5d68c9..c1d65e0df4e 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java @@ -146,12 +146,13 @@ public class NodePrioritizer { if (spareHosts.contains(host) && !canAllocateToSpareHosts) continue; if ( ! capacity.hasCapacity(host, requestedNodes.resources().get())) continue; if ( ! allNodesAndHosts.childrenOf(host).owner(application).cluster(clusterSpec.id()).isEmpty()) continue; + candidates.add(NodeCandidate.createNewChild(requestedNodes.resources().get(), - capacity.availableCapacityOf(host), - host, - spareHosts.contains(host), - allNodesAndHosts.nodes(), - nameResolver)); + capacity.availableCapacityOf(host), + host, + spareHosts.contains(host), + allNodesAndHosts.nodes(), + nameResolver)); } } 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 index 5a8c5221c47..e190f1c3bd2 100644 --- 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 @@ -190,7 +190,7 @@ public class MockHostProvisioner implements HostProvisioner { .collect(Collectors.toList()); } - private Node withIpAssigned(Node node) { + public Node withIpAssigned(Node node) { if (!node.type().isHost()) { return node.with(node.ipConfig().withPrimary(nameResolver.resolveAll(node.hostname()))); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNameResolver.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNameResolver.java index 0764d4527d5..dbc74f32f6b 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNameResolver.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNameResolver.java @@ -64,9 +64,9 @@ public class MockNameResolver implements NameResolver { return records.get(name); } if (mockAnyLookup) { - Set<String> ipAddresses = Set.of(randomIpAddress()); - records.put(name, ipAddresses); - return ipAddresses; + records.computeIfAbsent(name, (k) -> new HashSet<>()) + .add(randomIpAddress()); + return records.get(name); } throw new RuntimeException(new UnknownHostException("Could not resolve: " + name)); } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java index e0301d0c329..13089da2063 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java @@ -1,16 +1,15 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. 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.Cloud; -import com.yahoo.config.provision.CloudAccount; import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.HostSpec; +import com.yahoo.config.provision.NodeFlavors; import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.NodeResources.Architecture; import com.yahoo.config.provision.NodeResources.DiskSpeed; @@ -23,18 +22,16 @@ import com.yahoo.vespa.flags.InMemoryFlagSource; import com.yahoo.vespa.flags.PermanentFlags; import com.yahoo.vespa.hosted.provision.Node; import com.yahoo.vespa.hosted.provision.NodeList; +import com.yahoo.vespa.hosted.provision.NodeRepository; 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.Collection; import java.util.List; -import java.util.Optional; import java.util.Set; -import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -45,13 +42,6 @@ import static com.yahoo.config.provision.NodeResources.StorageType.remote; 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.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; /** * @author freva @@ -59,79 +49,63 @@ import static org.mockito.Mockito.when; */ public class DynamicProvisioningTest { - private static final Zone zone = new Zone( - Cloud.builder().dynamicProvisioning(true).allowHostSharing(false).build(), - SystemName.main, - Environment.prod, - RegionName.from("us-east")); private final MockNameResolver nameResolver = new MockNameResolver().mockAnyLookup(); - private final HostProvisioner hostProvisioner = mock(HostProvisioner.class); - private final ProvisioningTester tester = new ProvisioningTester.Builder() - .zone(zone).hostProvisioner(hostProvisioner).nameResolver(nameResolver).build(); @Test public void dynamically_provision_with_empty_node_repo() { + var tester = tester(true); assertEquals(0, tester.nodeRepository().nodes().list().size()); - ApplicationId application1 = ProvisioningTester.applicationId(); + ApplicationId application1 = ProvisioningTester.applicationId("application1"); NodeResources resources = new NodeResources(1, 4, 10, 1); - - mockHostProvisioner(hostProvisioner, "large", 3, null); // Provision shared hosts - prepareAndActivate(application1, clusterSpec("mycluster"), 4, 1, resources); - verify(hostProvisioner).provisionHosts(eq(List.of(100, 101, 102, 103)), eq(NodeType.host), eq(resources), eq(application1), - eq(Version.emptyVersion), eq(HostSharing.any), eq(Optional.of(ClusterSpec.Type.content)), eq(CloudAccount.empty), any()); + prepareAndActivate(application1, clusterSpec("mycluster"), 4, 1, resources, tester); // Total of 8 nodes should now be in node-repo, 4 active hosts and 4 active nodes assertEquals(8, tester.nodeRepository().nodes().list().size()); assertEquals(4, tester.nodeRepository().nodes().list(Node.State.active).nodeType(NodeType.host).size()); - assertEquals(Set.of("host-100-1", "host-101-1", "host-102-1", "host-103-1"), + assertEquals(Set.of("host100-1", "host101-1", "host102-1", "host103-1"), tester.nodeRepository().nodes().list(Node.State.active).nodeType(NodeType.tenant).hostnames()); // Deploy new application - ApplicationId application2 = ProvisioningTester.applicationId(); - prepareAndActivate(application2, clusterSpec("mycluster"), 4, 1, resources); + ApplicationId application2 = ProvisioningTester.applicationId("application2"); + prepareAndActivate(application2, clusterSpec("mycluster"), 4, 1, resources, tester); // Total of 12 nodes should now be in node-repo, 4 active hosts and 8 active nodes assertEquals(12, tester.nodeRepository().nodes().list().size()); assertEquals(4, tester.nodeRepository().nodes().list(Node.State.active).nodeType(NodeType.host).size()); - assertEquals(Set.of("host-100-1", "host-100-2", "host-101-1", "host-101-2", "host-102-1", "host-102-2", - "host-103-1", "host-103-2"), + assertEquals(Set.of("host100-1", "host100-2", "host101-1", "host101-2", "host102-1", "host102-2", "host103-1", "host103-2"), tester.nodeRepository().nodes().list(Node.State.active).nodeType(NodeType.tenant).hostnames()); // Deploy new exclusive application - ApplicationId application3 = ProvisioningTester.applicationId(); - mockHostProvisioner(hostProvisioner, "large", 3, application3); - prepareAndActivate(application3, clusterSpec("mycluster", true), 4, 1, resources); - verify(hostProvisioner).provisionHosts(eq(List.of(104, 105, 106, 107)), eq(NodeType.host), eq(resources), eq(application3), - eq(Version.emptyVersion), eq(HostSharing.exclusive), eq(Optional.of(ClusterSpec.Type.content)), eq(CloudAccount.empty), any()); + ApplicationId application3 = ProvisioningTester.applicationId("application3"); + NodeResources exclusiveResources = new NodeResources(1, 10, 10, 1); + prepareAndActivate(application3, clusterSpec("mycluster", true), 4, 1, exclusiveResources, tester); // Total of 20 nodes should now be in node-repo, 8 active hosts and 12 active nodes assertEquals(20, tester.nodeRepository().nodes().list().size()); assertEquals(8, tester.nodeRepository().nodes().list(Node.State.active).nodeType(NodeType.host).size()); assertEquals(12, tester.nodeRepository().nodes().list(Node.State.active).nodeType(NodeType.tenant).size()); - - verifyNoMoreInteractions(hostProvisioner); } @Test public void in_place_resize_not_allowed_on_exclusive_to_hosts() { - NodeResources initialResources = new NodeResources(2, 8, 10, 1); - NodeResources smallResources = new NodeResources(1, 4, 10, 1); + var tester = tester(false); + + NodeResources initialResources = new NodeResources(4, 80, 100, 1); + NodeResources smallResources = new NodeResources(1, 20, 50, 1); ApplicationId application1 = ProvisioningTester.applicationId(); - mockHostProvisioner(hostProvisioner, "large", 3, null); // Provision shared hosts - prepareAndActivate(application1, clusterSpec("mycluster"), 4, 1, initialResources); + prepareAndActivate(application1, clusterSpec("mycluster"), 4, 1, initialResources, tester); ApplicationId application2 = ProvisioningTester.applicationId(); - mockHostProvisioner(hostProvisioner, "large", 3, application2); // Provision exclusive hosts - prepareAndActivate(application2, clusterSpec("mycluster", true), 4, 1, initialResources); + prepareAndActivate(application2, clusterSpec("mycluster", true), 4, 1, initialResources, tester); // Total of 16 nodes should now be in node-repo, 8 active hosts and 8 active nodes assertEquals(16, tester.nodeRepository().nodes().list().size()); assertEquals(8, tester.nodeRepository().nodes().list(Node.State.active).nodeType(NodeType.tenant).size()); - prepareAndActivate(application1, clusterSpec("mycluster"), 4, 1, smallResources); - prepareAndActivate(application2, clusterSpec("mycluster", true), 4, 1, smallResources); + prepareAndActivate(application1, clusterSpec("mycluster"), 4, 1, smallResources, tester); + prepareAndActivate(application2, clusterSpec("mycluster", true), 4, 1, smallResources, tester); // 24 nodes: 4 shared hosts with 4 app1 nodes + 8 exclusive hosts with 8 nodes of app2, 4 of which are retired NodeList nodes = tester.nodeRepository().nodes().list(); @@ -143,50 +117,51 @@ public class DynamicProvisioningTest { @Test public void avoids_allocating_to_empty_hosts() { + var tester = tester(false); tester.makeReadyHosts(6, new NodeResources(12, 12, 200, 12)); tester.activateTenantHosts(); NodeResources resources = new NodeResources(1, 4, 10, 4); ApplicationId application1 = ProvisioningTester.applicationId(); - prepareAndActivate(application1, clusterSpec("mycluster"), 4, 1, resources); + prepareAndActivate(application1, clusterSpec("mycluster"), 4, 1, resources, tester); ApplicationId application2 = ProvisioningTester.applicationId(); - prepareAndActivate(application2, clusterSpec("mycluster"), 3, 1, resources); + prepareAndActivate(application2, clusterSpec("mycluster"), 3, 1, resources, tester); ApplicationId application3 = ProvisioningTester.applicationId(); - prepareAndActivate(application3, clusterSpec("mycluster"), 3, 1, resources); + prepareAndActivate(application3, clusterSpec("mycluster"), 3, 1, resources, tester); assertEquals(4, tester.nodeRepository().nodes().list().nodeType(NodeType.tenant).stream().map(Node::parentHostname).distinct().count()); ApplicationId application4 = ProvisioningTester.applicationId(); - prepareAndActivate(application4, clusterSpec("mycluster"), 3, 1, resources); + prepareAndActivate(application4, clusterSpec("mycluster"), 3, 1, resources, tester); assertEquals(5, tester.nodeRepository().nodes().list().nodeType(NodeType.tenant).stream().map(Node::parentHostname).distinct().count()); } @Test public void retires_on_exclusivity_violation() { + var tester = tester(false); ApplicationId application1 = ProvisioningTester.applicationId(); - NodeResources resources = new NodeResources(1, 4, 10, 1); + NodeResources resources = new NodeResources(4, 80, 100, 1); - mockHostProvisioner(hostProvisioner, "large", 3, null); // Provision shared hosts - prepareAndActivate(application1, clusterSpec("mycluster"), 4, 1, resources); + prepareAndActivate(application1, clusterSpec("mycluster"), 4, 1, resources, tester); NodeList initialNodes = tester.nodeRepository().nodes().list().owner(application1); assertEquals(4, initialNodes.size()); // Redeploy same application with exclusive=true - mockHostProvisioner(hostProvisioner, "large", 3, application1); - prepareAndActivate(application1, clusterSpec("mycluster", true), 4, 1, resources); + prepareAndActivate(application1, clusterSpec("mycluster", true), 4, 1, resources, tester); assertEquals(8, tester.nodeRepository().nodes().list().owner(application1).size()); assertEquals(initialNodes, tester.nodeRepository().nodes().list().owner(application1).retired()); // Redeploy without exclusive again is no-op - prepareAndActivate(application1, clusterSpec("mycluster"), 4, 1, resources); + prepareAndActivate(application1, clusterSpec("mycluster"), 4, 1, resources, tester); assertEquals(8, tester.nodeRepository().nodes().list().owner(application1).size()); assertEquals(initialNodes, tester.nodeRepository().nodes().list().owner(application1).retired()); } @Test public void node_indices_are_unique_even_when_a_node_is_left_in_reserved_state() { + var tester = tester(false); NodeResources resources = new NodeResources(10, 10, 10, 10); ApplicationId app = ProvisioningTester.applicationId(); @@ -224,9 +199,9 @@ public class DynamicProvisioningTest { List<Flavor> flavors = List.of(new Flavor("2x", new NodeResources(2, 17, 200, 10, fast, remote))); - ProvisioningTester tester = new ProvisioningTester.Builder().zone(zone) + ProvisioningTester tester = new ProvisioningTester.Builder().zone(zone(false)) .flavors(flavors) - .hostProvisioner(new MockHostProvisioner(flavors, memoryTax, zone.cloud())) + .hostProvisioner(new MockHostProvisioner(flavors, memoryTax, zone(false).cloud())) .nameResolver(nameResolver) .resourcesCalculator(memoryTax, 0) .build(); @@ -268,8 +243,8 @@ public class DynamicProvisioningTest { InMemoryFlagSource flagSource = new InMemoryFlagSource(); List<Flavor> flavors = List.of(new Flavor("x86", new NodeResources(1, 4, 50, 0.1, fast, local, Architecture.x86_64)), new Flavor("arm", new NodeResources(1, 4, 50, 0.1, fast, local, Architecture.arm64))); - MockHostProvisioner hostProvisioner = new MockHostProvisioner(flavors, zone.cloud()); - ProvisioningTester tester = new ProvisioningTester.Builder().zone(zone) + MockHostProvisioner hostProvisioner = new MockHostProvisioner(flavors, zone(false).cloud()); + ProvisioningTester tester = new ProvisioningTester.Builder().zone(zone(false)) .flavors(flavors) .hostProvisioner(hostProvisioner) .resourcesCalculator(0, 0) @@ -312,9 +287,9 @@ public class DynamicProvisioningTest { new Flavor("2x", new NodeResources(2, 20 - memoryTax, 200, 0.1, fast, remote)), new Flavor("4x", new NodeResources(4, 40 - memoryTax, 400, 0.1, fast, remote))); - ProvisioningTester tester = new ProvisioningTester.Builder().zone(zone) + ProvisioningTester tester = new ProvisioningTester.Builder().zone(zone(false)) .flavors(flavors) - .hostProvisioner(new MockHostProvisioner(flavors, memoryTax, zone.cloud())) + .hostProvisioner(new MockHostProvisioner(flavors, memoryTax, zone(false).cloud())) .nameResolver(nameResolver) .resourcesCalculator(memoryTax, 0) .build(); @@ -387,9 +362,9 @@ public class DynamicProvisioningTest { new Flavor("4x", new NodeResources(4, 40 - memoryTax, 400, 0.1, fast, remote)), new Flavor("4xl", new NodeResources(4, 40 - memoryTax, 400, 0.1, fast, local))); - ProvisioningTester tester = new ProvisioningTester.Builder().zone(zone) + ProvisioningTester tester = new ProvisioningTester.Builder().zone(zone(false)) .flavors(flavors) - .hostProvisioner(new MockHostProvisioner(flavors, memoryTax, zone.cloud())) + .hostProvisioner(new MockHostProvisioner(flavors, memoryTax, zone(false).cloud())) .nameResolver(nameResolver) .resourcesCalculator(memoryTax, 0) .build(); @@ -422,9 +397,9 @@ public class DynamicProvisioningTest { new Flavor("2xl", new NodeResources(2, 20 - memoryTax, 200, 0.1, fast, remote)), new Flavor("4xl", new NodeResources(4, 40 - memoryTax, 400, 0.1, fast, remote))); - ProvisioningTester tester = new ProvisioningTester.Builder().zone(zone) + ProvisioningTester tester = new ProvisioningTester.Builder().zone(zone(false)) .flavors(flavors) - .hostProvisioner(new MockHostProvisioner(flavors, memoryTax, zone.cloud())) + .hostProvisioner(new MockHostProvisioner(flavors, memoryTax, zone(false).cloud())) .nameResolver(nameResolver) .resourcesCalculator(memoryTax, localDiskTax) .build(); @@ -445,9 +420,9 @@ public class DynamicProvisioningTest { public void gpu_host() { List<Flavor> flavors = List.of(new Flavor("gpu", new NodeResources(4, 16, 125, 10, fast, local, Architecture.x86_64, new NodeResources.GpuResources(1, 16)))); - ProvisioningTester tester = new ProvisioningTester.Builder().zone(zone) + ProvisioningTester tester = new ProvisioningTester.Builder().zone(zone(false)) .flavors(flavors) - .hostProvisioner(new MockHostProvisioner(flavors, zone.cloud())) + .hostProvisioner(new MockHostProvisioner(flavors, zone(false).cloud())) .nameResolver(nameResolver) .build(); NodeResources resources = new NodeResources(4, 16, 125, 0.3, @@ -458,16 +433,43 @@ public class DynamicProvisioningTest { 2, 1, resources); } - private void prepareAndActivate(ApplicationId application, ClusterSpec clusterSpec, int nodes, int groups, NodeResources resources) { + private Zone zone(boolean sharing) { + return new Zone( + Cloud.builder().dynamicProvisioning(true).allowHostSharing(sharing).build(), + SystemName.main, + Environment.prod, + RegionName.from("us-east")); + } + + private ProvisioningTester tester(boolean sharing) { + var hostProvisioner = new MockHostProvisioner(new NodeFlavors(ProvisioningTester.createConfig()).getFlavors(), nameResolver, 0, zone(sharing).cloud()); + return new ProvisioningTester.Builder().zone(zone(sharing)).hostProvisioner(hostProvisioner).nameResolver(nameResolver).build(); + } + + private void prepareAndActivate(ApplicationId application, ClusterSpec clusterSpec, int nodes, int groups, NodeResources resources, + ProvisioningTester tester) { List<HostSpec> prepared = tester.prepare(application, clusterSpec, nodes, groups, resources); NodeList provisionedHosts = tester.nodeRepository().nodes().list(Node.State.provisioned).nodeType(NodeType.host); if (!provisionedHosts.isEmpty()) { - tester.move(Node.State.ready, provisionedHosts.asList()); + List<Node> hosts = provisionedHosts.asList() + .stream() + .map(h -> ((MockHostProvisioner)tester.hostProvisioner()).withIpAssigned(h)) + .toList(); + tester.move(Node.State.ready, hosts); tester.activateTenantHosts(); } + assignIpAddresses(prepared, tester.nodeRepository()); tester.activate(application, prepared); } + private void assignIpAddresses(Collection<HostSpec> hosts, NodeRepository nodeRepository) { + for (var host : hosts) { + try (var nodeLock = nodeRepository.nodes().lockAndGetRequired(host.hostname())) { + var node = nodeLock.node(); + nodeRepository.nodes().write(node.with(node.ipConfig().withPrimary(nodeRepository.nameResolver().resolveAll(node.hostname()))), nodeLock); + } + } + } private static ClusterSpec clusterSpec(String clusterId) { return clusterSpec(clusterId, false); } @@ -482,39 +484,8 @@ public class DynamicProvisioningTest { } private static ClusterResources resources(int nodes, int groups, double vcpu, double memory, double disk, - DiskSpeed diskSpeed, StorageType storageType) { + DiskSpeed diskSpeed, StorageType storageType) { return new ClusterResources(nodes, groups, new NodeResources(vcpu, memory, disk, 0.1, diskSpeed, storageType)); } - private void mockHostProvisioner(HostProvisioner hostProvisioner, String hostFlavorName, int numIps, ApplicationId exclusiveTo) { - doAnswer(invocation -> { - Flavor hostFlavor = tester.nodeRepository().flavors().getFlavorOrThrow(hostFlavorName); - List<Integer> provisionIndexes = invocation.getArgument(0); - NodeResources nodeResources = invocation.getArgument(2); - Consumer<List<ProvisionedHost>> provisionedHostConsumer = invocation.getArgument(8); - - List<ProvisionedHost> provisionedHosts = provisionIndexes.stream() - .map(hostIndex -> { - String hostHostname = "host-" + hostIndex; - String hostIp = "::" + hostIndex + ":0"; - nameResolver.addRecord(hostHostname, hostIp); - Set<String> pool = IntStream.range(1, numIps + 1).mapToObj(i -> { - String ip = "::" + hostIndex + ":" + i; - nameResolver.addRecord(hostHostname + "-" + i, ip); - return ip; - }).collect(Collectors.toSet()); - - Node parent = Node.create(hostHostname, new IP.Config(Set.of(hostIp), pool), hostHostname, hostFlavor, NodeType.host) - .exclusiveToApplicationId(exclusiveTo).build(); - Node child = Node.reserve(Set.of("::" + hostIndex + ":1"), hostHostname + "-1", hostHostname, nodeResources, NodeType.tenant).build(); - ProvisionedHost provisionedHost = mock(ProvisionedHost.class); - when(provisionedHost.generateHost()).thenReturn(parent); - when(provisionedHost.generateNode()).thenReturn(child); - return provisionedHost; - }).toList(); - provisionedHostConsumer.accept(provisionedHosts); - return null; - }).when(hostProvisioner).provisionHosts(any(), any(), any(), any(), any(), any(), any(), any(), any()); - } - } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java index 5679201c77b..a19f986a177 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java @@ -161,8 +161,7 @@ public class LoadBalancerProvisionerTest { clusterRequest(ClusterSpec.Type.container, containerCluster), clusterRequest(ClusterSpec.Type.content, contentCluster))); List<LoadBalancer> activeLoadBalancers = lbApp1.get().stream() - .filter(lb -> lb.state() == LoadBalancer.State.active) - .collect(Collectors.toList()); + .filter(lb -> lb.state() == LoadBalancer.State.active).toList(); assertEquals(1, activeLoadBalancers.size()); assertEquals(Set.of(), activeLoadBalancers.get(0).instance().get().reals()); } 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 2ea8d95bc9d..bf15e4bbe1c 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 @@ -80,6 +80,7 @@ public class ProvisioningTester { private final NodeFlavors nodeFlavors; private final ManualClock clock; private final NodeRepository nodeRepository; + private final HostProvisioner hostProvisioner; private final NodeRepositoryProvisioner provisioner; private final CapacityPolicies capacityPolicies; private final ProvisionLogger provisionLogger; @@ -102,6 +103,7 @@ public class ProvisioningTester { this.curator = curator; this.nodeFlavors = nodeFlavors; this.clock = new ManualClock(); + this.hostProvisioner = hostProvisioner; ProvisionServiceProvider provisionServiceProvider = new MockProvisionServiceProvider(loadBalancerService, hostProvisioner, resourcesCalculator); this.nodeRepository = new NodeRepository(nodeFlavors, provisionServiceProvider, @@ -144,6 +146,7 @@ public class ProvisioningTester { public Orchestrator orchestrator() { return nodeRepository.orchestrator(); } public ManualClock clock() { return clock; } public NodeRepositoryProvisioner provisioner() { return provisioner; } + public HostProvisioner hostProvisioner() { return hostProvisioner; } public LoadBalancerServiceMock loadBalancerService() { return loadBalancerService; } public CapacityPolicies capacityPolicies() { return capacityPolicies; } public NodeList getNodes(ApplicationId id, Node.State ... inState) { return nodeRepository.nodes().list(inState).owner(id); } |