summaryrefslogtreecommitdiffstats
path: root/node-repository
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2020-05-28 11:04:11 +0200
committerMartin Polden <mpolden@mpolden.no>2020-05-28 11:16:19 +0200
commit8903332a7a6ce57c7777b2f6976c1da781d8b52e (patch)
treeaaff9464f617de4cc537a2d2f14816b9cd2ba6ed /node-repository
parent31c19ba00cd80f1b83edb11cba06d8e4d0a7428b (diff)
Ensure that capacity reduction does not remove non-empty hosts
Diffstat (limited to 'node-repository')
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainer.java43
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/DynamicProvisioningMaintainerTest.java23
2 files changed, 35 insertions, 31 deletions
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 b1031715c24..44bfed90106 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
@@ -95,14 +95,7 @@ public class DynamicProvisioningMaintainer extends NodeRepositoryMaintainer {
/** Converge zone to wanted capacity */
private void convergeToCapacity(NodeList nodes) {
List<NodeResources> capacity = targetCapacity();
- List<Node> existingHosts;
- if (nodeRepository().zone().getCloud().dynamicProvisioning()) {
- existingHosts = removableHostsOf(nodes);
- } else {
- if (capacity.isEmpty()) return;
- existingHosts = availableHostsOf(nodes);
- }
- List<Node> excessHosts = provision(capacity, existingHosts);
+ List<Node> excessHosts = provision(capacity, nodes);
excessHosts.forEach(host -> {
try {
hostProvisioner.deprovision(host);
@@ -117,9 +110,15 @@ public class DynamicProvisioningMaintainer extends NodeRepositoryMaintainer {
/**
* Provision the nodes necessary to satisfy given capacity.
*
- * @return Excess hosts that can be deprovisioned, if any.
+ * @return Excess hosts that can safely be deprovisioned, if any.
*/
- private List<Node> provision(List<NodeResources> capacity, List<Node> existingHosts) {
+ 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()) {
+ return List.of();
+ }
List<Node> excessHosts = new ArrayList<>(existingHosts);
for (Iterator<NodeResources> it = capacity.iterator(); it.hasNext() && !excessHosts.isEmpty(); ) {
NodeResources resources = it.next();
@@ -150,7 +149,7 @@ public class DynamicProvisioningMaintainer extends NodeRepositoryMaintainer {
log.log(Level.WARNING, "Failed to pre-provision " + resources + ", will retry in " + interval(), e);
}
});
- return excessHosts;
+ return removableHostsOf(excessHosts, nodes);
}
@@ -173,17 +172,17 @@ public class DynamicProvisioningMaintainer extends NodeRepositoryMaintainer {
.asList();
}
- /** Returns hosts that have no containers and are thus removable */
- private static List<Node> removableHostsOf(NodeList nodes) {
- Map<String, Node> hostsByHostname = availableHostsOf(nodes).stream()
- .collect(Collectors.toMap(Node::hostname,
- Function.identity()));
-
- nodes.asList().stream()
- .filter(node -> node.allocation().isPresent())
- .flatMap(node -> node.parentHostname().stream())
- .distinct()
- .forEach(hostsByHostname::remove);
+ /** 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/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 6b61c1b2774..ba859655ab7 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
@@ -160,9 +160,7 @@ public class DynamicProvisioningMaintainerTest {
assertEquals(2, tester.provisionedHostsMatching(resources2));
// Next maintenance run does nothing
- List<Node> nodes = tester.nodeRepository.getNodes();
- tester.maintainer.maintain();
- assertEquals(nodes, tester.nodeRepository.getNodes());
+ tester.assertNodesUnchanged();
// Target capacity is changed
NodeResources resources3 = new NodeResources(48, 128, 1000, 1);
@@ -188,15 +186,16 @@ public class DynamicProvisioningMaintainerTest {
tester.provisioningTester.deploy(application,
Capacity.from(new ClusterResources(2, 1, new NodeResources(4, 8, 50, 0.1))));
assertEquals(2, tester.nodeRepository.list().owner(application).size());
- nodes = tester.nodeRepository.getNodes();
- tester.maintainer.maintain();
- assertEquals(nodes, tester.nodeRepository.getNodes());
+ tester.assertNodesUnchanged();
// Clearing flag does nothing
tester.flagSource.withListFlag(Flags.TARGET_CAPACITY.id(), List.of(), HostCapacity.class);
- nodes = tester.nodeRepository.getNodes();
- tester.maintainer.maintain();
- assertEquals(nodes, tester.nodeRepository.getNodes());
+ tester.assertNodesUnchanged();
+
+ // Capacity reduction does not remove host with children
+ tester.flagSource.withListFlag(Flags.TARGET_CAPACITY.id(), List.of(new HostCapacity(resources1.vcpu(), resources1.memoryGb(), resources1.diskGb(), 1)),
+ HostCapacity.class);
+ tester.assertNodesUnchanged();
}
private static class DynamicProvisioningTester {
@@ -282,6 +281,12 @@ public class DynamicProvisioningMaintainerTest {
.count();
}
+ private void assertNodesUnchanged() {
+ List<Node> nodes = nodeRepository.getNodes();
+ maintainer.maintain();
+ assertEquals("Nodes are unchanged after maintenance run", nodes, nodeRepository.getNodes());
+ }
+
}
static class MockHostProvisioner implements HostProvisioner {