From 9db6c09a62822ae1a3e7b6bad70ed214e442c1c6 Mon Sep 17 00:00:00 2001 From: Martin Polden Date: Tue, 15 Sep 2020 11:21:29 +0200 Subject: Make PrioritizableNode immutable --- .../provision/provisioning/NodeAllocation.java | 66 ++++++++++++---------- .../provision/provisioning/NodePrioritizer.java | 7 +-- .../provision/provisioning/PrioritizableNode.java | 10 +++- 3 files changed, 45 insertions(+), 38 deletions(-) (limited to 'node-repository') 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 a37da10f5f0..758f46f5113 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 @@ -21,8 +21,9 @@ import java.util.Collection; import java.util.Comparator; import java.util.EnumSet; import java.util.HashSet; -import java.util.LinkedHashSet; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Predicate; @@ -49,7 +50,7 @@ class NodeAllocation { private final NodeSpec requestedNodes; /** The nodes this has accepted so far */ - private final Set nodes = new LinkedHashSet<>(); + private final Map nodes = new LinkedHashMap<>(); /** The number of already allocated nodes accepted and not retired */ private int accepted = 0; @@ -127,7 +128,7 @@ class NodeAllocation { ++rejectedDueToInsufficientRealResources; continue; } - if ( violatesParentHostPolicy(this.nodes, offered)) { + if ( violatesParentHostPolicy(offered)) { ++rejectedDueToClashingParentHost; continue; } @@ -142,10 +143,10 @@ class NodeAllocation { if (offered.status().wantToRetire()) { continue; } - node.node = offered.allocate(application, - ClusterMembership.from(cluster, highestIndex.add(1)), - requestedNodes.resources().orElse(node.node.resources()), - nodeRepository.clock().instant()); + node = node.withNode(offered.allocate(application, + ClusterMembership.from(cluster, highestIndex.add(1)), + requestedNodes.resources().orElse(node.node.resources()), + nodeRepository.clock().instant())); accepted.add(acceptNode(node, false, false)); } } @@ -156,15 +157,15 @@ class NodeAllocation { private boolean shouldRetire(PrioritizableNode node) { if ( ! requestedNodes.considerRetiring()) return false; if ( ! nodeResourceLimits.isWithinRealLimits(node.node, cluster)) return true; - if (violatesParentHostPolicy(this.nodes, node.node)) return true; + if (violatesParentHostPolicy(node.node)) return true; if ( ! hasCompatibleFlavor(node)) return true; if (node.node.status().wantToRetire()) return true; if (requestedNodes.isExclusive() && ! hostsOnly(application, node.node.parentHostname())) return true; return false; } - private boolean violatesParentHostPolicy(Collection accepted, Node offered) { - return checkForClashingParentHost() && offeredNodeHasParentHostnameAlreadyAccepted(accepted, offered); + private boolean violatesParentHostPolicy(Node offered) { + return checkForClashingParentHost() && offeredNodeHasParentHostnameAlreadyAccepted(offered); } private boolean checkForClashingParentHost() { @@ -173,8 +174,8 @@ class NodeAllocation { ! application.instance().isTester(); } - private boolean offeredNodeHasParentHostnameAlreadyAccepted(Collection accepted, Node offered) { - for (PrioritizableNode acceptedNode : accepted) { + private boolean offeredNodeHasParentHostnameAlreadyAccepted(Node offered) { + for (PrioritizableNode acceptedNode : nodes.values()) { if (acceptedNode.node.parentHostname().isPresent() && offered.parentHostname().isPresent() && acceptedNode.node.parentHostname().get().equals(offered.parentHostname().get())) { return true; @@ -271,13 +272,17 @@ class NodeAllocation { // group may be different node = setCluster(cluster, node); } - prioritizableNode.node = node; + prioritizableNode = prioritizableNode.withNode(node); indexes.add(node.allocation().get().membership().index()); highestIndex.set(Math.max(highestIndex.get(), node.allocation().get().membership().index())); - nodes.add(prioritizableNode); + update(prioritizableNode); return node; } + private void update(PrioritizableNode node) { + nodes.put(node.node.hostname(), node); + } + private Node resize(Node node) { NodeResources hostResources = allNodes.parentOf(node).get().flavor().resources(); return node.with(new Flavor(requestedNodes.resources().get() @@ -323,36 +328,39 @@ class NodeAllocation { * @return the final list of nodes */ List finalNodes() { - int currentRetiredCount = (int) nodes.stream().filter(node -> node.node.allocation().get().membership().retired()).count(); + int currentRetiredCount = (int) nodes.values().stream().filter(node -> node.node.allocation().get().membership().retired()).count(); int deltaRetiredCount = requestedNodes.idealRetiredCount(nodes.size(), currentRetiredCount) - currentRetiredCount; if (deltaRetiredCount > 0) { // retire until deltaRetiredCount is 0 - for (PrioritizableNode node : byRetiringPriority(nodes)) { + for (PrioritizableNode node : byRetiringPriority(nodes.values())) { if ( ! node.node.allocation().get().membership().retired() && node.node.state() == Node.State.active) { - node.node = node.node.retire(Agent.application, nodeRepository.clock().instant()); + PrioritizableNode newNode = node.withNode(node.node.retire(Agent.application, nodeRepository.clock().instant())); + update(newNode); if (--deltaRetiredCount == 0) break; } } } else if (deltaRetiredCount < 0) { // unretire until deltaRetiredCount is 0 - for (PrioritizableNode node : byUnretiringPriority(nodes)) { + for (PrioritizableNode node : byUnretiringPriority(nodes.values())) { if ( node.node.allocation().get().membership().retired() && hasCompatibleFlavor(node) ) { if (node.isResizable) - node.node = resize(node.node); - node.node = node.node.unretire(); + node = node.withNode(resize(node.node)); + node = node.withNode(node.node.unretire()); + update(node); if (++deltaRetiredCount == 0) break; } } } - for (PrioritizableNode node : nodes) { + for (PrioritizableNode node : nodes.values()) { // 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())))); + node = node.withNode(node.node.with(allocation.with(allocation.membership() + .with(allocation.membership().cluster().exclusive(requestedNodes.isExclusive()))))); + update(node); } - return nodes.stream().map(n -> n.node).collect(Collectors.toList()); + return nodes.values().stream().map(n -> n.node).collect(Collectors.toList()); } List reservableNodes() { @@ -361,28 +369,24 @@ class NodeAllocation { return nodesFilter(n -> !n.isNewNode && reservableStates.contains(n.node.state())); } - List surplusNodes() { - return nodesFilter(n -> n.isSurplusNode); - } - List newNodes() { return nodesFilter(n -> n.isNewNode); } private List nodesFilter(Predicate predicate) { - return nodes.stream() + return nodes.values().stream() .filter(predicate) .map(n -> n.node) .collect(Collectors.toList()); } /** Prefer to retire nodes we want the least */ - private List byRetiringPriority(Set nodes) { + private List byRetiringPriority(Collection nodes) { return nodes.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList()); } /** Prefer to unretire nodes we don't want to retire, and otherwise those with lower index */ - private List byUnretiringPriority(Set nodes) { + private List byUnretiringPriority(Collection nodes) { return nodes.stream() .sorted(Comparator.comparing((PrioritizableNode n) -> n.node.status().wantToRetire()) .thenComparing(n -> n.node.allocation().get().membership().index())) 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 3dc7eefa277..d5951490729 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 @@ -5,21 +5,20 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.NodeType; - -import java.util.ArrayList; -import java.util.logging.Level; import com.yahoo.vespa.hosted.provision.LockedNodeList; 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.IP; +import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -81,7 +80,7 @@ public class NodePrioritizer { this.isDocker = resources(requestedNodes) != null; } - /** Returns the list of nodes sorted by PrioritizableNode::compare */ + /** Returns the list of nodes sorted by {@link PrioritizableNode#compareTo(PrioritizableNode)} */ List prioritize() { return nodes.values().stream().sorted().collect(Collectors.toList()); } 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 9955d75a742..dd1ecb453db 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 @@ -8,7 +8,7 @@ import java.util.List; import java.util.Optional; /** - * A node with additional information required to prioritize it for allocation. + * A node with additional information required to prioritize it for allocation. This is immutable. * * @author smorgrav */ @@ -21,8 +21,7 @@ class PrioritizableNode implements Comparable { private static final NodeResources zeroResources = new NodeResources(0, 0, 0, 0, NodeResources.DiskSpeed.any, NodeResources.StorageType.any); - // TODO: Make immutable - Node node; + final Node node; /** The free capacity on the parent of this node, before adding this node to it */ private final NodeResources freeParentCapacity; @@ -138,6 +137,11 @@ class PrioritizableNode implements Comparable { /** Returns the allocation skew of the parent of this after adding this node to it */ double skewWithThis() { return skewWith(node.resources()); } + /** Returns a copy of this with node set to given value */ + PrioritizableNode withNode(Node node) { + return new PrioritizableNode(node, freeParentCapacity, parent, violatesSpares, isSurplusNode, isNewNode, isResizable); + } + private boolean lessThanHalfTheHost(PrioritizableNode node) { var n = node.node.resources(); var h = node.parent.get().resources(); -- cgit v1.2.3 From 65d3b3c3d953e5b97785e7e0452525eb7888f74c Mon Sep 17 00:00:00 2001 From: Martin Polden Date: Tue, 15 Sep 2020 11:31:01 +0200 Subject: Rename PrioritizableNode -> NodeCandidate --- .../provision/provisioning/GroupPreparer.java | 14 +- .../provision/provisioning/NodeAllocation.java | 112 +++++----- .../provision/provisioning/NodeCandidate.java | 234 +++++++++++++++++++++ .../provision/provisioning/NodePrioritizer.java | 33 ++- .../provision/provisioning/PrioritizableNode.java | 234 --------------------- .../provision/provisioning/NodeCandidateTest.java | 149 +++++++++++++ .../provisioning/PrioritizableNodeTest.java | 149 ------------- 7 files changed, 461 insertions(+), 464 deletions(-) create mode 100644 node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java delete mode 100644 node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/PrioritizableNode.java create mode 100644 node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidateTest.java delete mode 100644 node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/PrioritizableNodeTest.java (limited to 'node-repository') 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 d3faa4d80f5..75f3c892571 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 @@ -99,13 +99,13 @@ public class GroupPreparer { nodeRepository.addNodes(hosts, Agent.application); // Offer the nodes on the newly provisioned hosts, this should be enough to cover the deficit - List nodes = provisionedHosts.stream() - .map(provisionedHost -> new PrioritizableNode.Builder(provisionedHost.generateNode()) - .parent(provisionedHost.generateHost()) - .newNode(true) - .build()) - .collect(Collectors.toList()); - allocation.offer(nodes); + List candidates = provisionedHosts.stream() + .map(provisionedHost -> new NodeCandidate.Builder(provisionedHost.generateNode()) + .parent(provisionedHost.generateHost()) + .newNode(true) + .build()) + .collect(Collectors.toList()); + allocation.offer(candidates); } if (! allocation.fulfilled() && requestedNodes.canFail()) 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 758f46f5113..b07ce786685 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 @@ -49,8 +49,8 @@ class NodeAllocation { /** The requested nodes of this list */ private final NodeSpec requestedNodes; - /** The nodes this has accepted so far */ - private final Map nodes = new LinkedHashMap<>(); + /** The node candidates this has accepted so far, keyed on hostname */ + private final Map nodes = new LinkedHashMap<>(); /** The number of already allocated nodes accepted and not retired */ private int accepted = 0; @@ -99,31 +99,31 @@ class NodeAllocation { * @param nodesPrioritized the nodes which are potentially on offer. These may belong to a different application etc. * @return the subset of offeredNodes which was accepted, with the correct allocation assigned */ - List offer(List nodesPrioritized) { + List offer(List nodesPrioritized) { List accepted = new ArrayList<>(); - for (PrioritizableNode node : nodesPrioritized) { - Node offered = node.node; + for (NodeCandidate candidate : nodesPrioritized) { + Node offered = candidate.node; if (offered.allocation().isPresent()) { Allocation allocation = offered.allocation().get(); ClusterMembership membership = allocation.membership(); if ( ! allocation.owner().equals(application)) continue; // wrong application if ( ! membership.cluster().satisfies(cluster)) continue; // wrong cluster id/type - if ((! node.isSurplusNode || saturated()) && ! membership.cluster().group().equals(cluster.group())) continue; // wrong group and we can't or have no reason to change it + if ((! candidate.isSurplusNode || saturated()) && ! membership.cluster().group().equals(cluster.group())) continue; // wrong group and we can't or have no reason to change it if ( offered.state() == Node.State.active && allocation.isRemovable()) continue; // don't accept; causes removal if ( indexes.contains(membership.index())) continue; // duplicate index (just to be sure) boolean resizeable = false; boolean acceptToRetire = false; if (requestedNodes.considerRetiring()) { - resizeable = node.isResizable; - acceptToRetire = acceptToRetire(node); + resizeable = candidate.isResizable; + acceptToRetire = acceptToRetire(candidate); } - if ((! saturated() && hasCompatibleFlavor(node) && requestedNodes.acceptable(offered)) || acceptToRetire) - accepted.add(acceptNode(node, shouldRetire(node), resizeable)); + if ((! saturated() && hasCompatibleFlavor(candidate) && requestedNodes.acceptable(offered)) || acceptToRetire) + accepted.add(acceptNode(candidate, shouldRetire(candidate), resizeable)); } - else if (! saturated() && hasCompatibleFlavor(node)) { + else if (! saturated() && hasCompatibleFlavor(candidate)) { if ( ! nodeResourceLimits.isWithinRealLimits(offered, cluster)) { ++rejectedDueToInsufficientRealResources; continue; @@ -143,24 +143,24 @@ class NodeAllocation { if (offered.status().wantToRetire()) { continue; } - node = node.withNode(offered.allocate(application, + candidate = candidate.withNode(offered.allocate(application, ClusterMembership.from(cluster, highestIndex.add(1)), - requestedNodes.resources().orElse(node.node.resources()), + requestedNodes.resources().orElse(candidate.node.resources()), nodeRepository.clock().instant())); - accepted.add(acceptNode(node, false, false)); + accepted.add(acceptNode(candidate, false, false)); } } return accepted; } - private boolean shouldRetire(PrioritizableNode node) { + private boolean shouldRetire(NodeCandidate candidate) { if ( ! requestedNodes.considerRetiring()) return false; - if ( ! nodeResourceLimits.isWithinRealLimits(node.node, cluster)) return true; - if (violatesParentHostPolicy(node.node)) return true; - if ( ! hasCompatibleFlavor(node)) return true; - if (node.node.status().wantToRetire()) return true; - if (requestedNodes.isExclusive() && ! hostsOnly(application, node.node.parentHostname())) return true; + if ( ! nodeResourceLimits.isWithinRealLimits(candidate.node, cluster)) return true; + if (violatesParentHostPolicy(candidate.node)) return true; + if ( ! hasCompatibleFlavor(candidate)) return true; + if (candidate.node.status().wantToRetire()) return true; + if (requestedNodes.isExclusive() && ! hostsOnly(application, candidate.node.parentHostname())) return true; return false; } @@ -175,7 +175,7 @@ class NodeAllocation { } private boolean offeredNodeHasParentHostnameAlreadyAccepted(Node offered) { - for (PrioritizableNode acceptedNode : nodes.values()) { + for (NodeCandidate acceptedNode : nodes.values()) { if (acceptedNode.node.parentHostname().isPresent() && offered.parentHostname().isPresent() && acceptedNode.node.parentHostname().get().equals(offered.parentHostname().get())) { return true; @@ -231,21 +231,21 @@ class NodeAllocation { * initialized. (In the other case, where a container node is not desired because we have enough nodes we * do want to remove it immediately to get immediate feedback on how the size reduction works out.) */ - private boolean acceptToRetire(PrioritizableNode node) { - if (node.node.state() != Node.State.active) return false; - if (! node.node.allocation().get().membership().cluster().group().equals(cluster.group())) return false; - if (node.node.allocation().get().membership().retired()) return true; // don't second-guess if already retired + private boolean acceptToRetire(NodeCandidate candidate) { + if (candidate.node.state() != Node.State.active) return false; + if (! candidate.node.allocation().get().membership().cluster().group().equals(cluster.group())) return false; + if (candidate.node.allocation().get().membership().retired()) return true; // don't second-guess if already retired return cluster.type().isContent() || - (cluster.type() == ClusterSpec.Type.container && !hasCompatibleFlavor(node)); + (cluster.type() == ClusterSpec.Type.container && !hasCompatibleFlavor(candidate)); } - private boolean hasCompatibleFlavor(PrioritizableNode node) { - return requestedNodes.isCompatible(node.node.flavor(), nodeRepository.flavors()) || node.isResizable; + private boolean hasCompatibleFlavor(NodeCandidate candidate) { + return requestedNodes.isCompatible(candidate.node.flavor(), nodeRepository.flavors()) || candidate.isResizable; } - private Node acceptNode(PrioritizableNode prioritizableNode, boolean wantToRetire, boolean resizeable) { - Node node = prioritizableNode.node; + private Node acceptNode(NodeCandidate candidate, boolean wantToRetire, boolean resizeable) { + Node node = candidate.node; if (node.allocation().isPresent()) // Record the currently requested resources node = node.with(node.allocation().get().withRequestedResources(requestedNodes.resources().orElse(node.resources()))); @@ -272,15 +272,15 @@ class NodeAllocation { // group may be different node = setCluster(cluster, node); } - prioritizableNode = prioritizableNode.withNode(node); + candidate = candidate.withNode(node); indexes.add(node.allocation().get().membership().index()); highestIndex.set(Math.max(highestIndex.get(), node.allocation().get().membership().index())); - update(prioritizableNode); + put(candidate); return node; } - private void update(PrioritizableNode node) { - nodes.put(node.node.hostname(), node); + private void put(NodeCandidate candidate) { + nodes.put(candidate.node.hostname(), candidate); } private Node resize(Node node) { @@ -332,32 +332,32 @@ class NodeAllocation { int deltaRetiredCount = requestedNodes.idealRetiredCount(nodes.size(), currentRetiredCount) - currentRetiredCount; if (deltaRetiredCount > 0) { // retire until deltaRetiredCount is 0 - for (PrioritizableNode node : byRetiringPriority(nodes.values())) { - if ( ! node.node.allocation().get().membership().retired() && node.node.state() == Node.State.active) { - PrioritizableNode newNode = node.withNode(node.node.retire(Agent.application, nodeRepository.clock().instant())); - update(newNode); + for (NodeCandidate candidate : byRetiringPriority(nodes.values())) { + if ( ! candidate.node.allocation().get().membership().retired() && candidate.node.state() == Node.State.active) { + candidate = candidate.withNode(candidate.node.retire(Agent.application, nodeRepository.clock().instant())); + put(candidate); if (--deltaRetiredCount == 0) break; } } } else if (deltaRetiredCount < 0) { // unretire until deltaRetiredCount is 0 - for (PrioritizableNode node : byUnretiringPriority(nodes.values())) { - if ( node.node.allocation().get().membership().retired() && hasCompatibleFlavor(node) ) { - if (node.isResizable) - node = node.withNode(resize(node.node)); - node = node.withNode(node.node.unretire()); - update(node); + for (NodeCandidate candidate : byUnretiringPriority(nodes.values())) { + if ( candidate.node.allocation().get().membership().retired() && hasCompatibleFlavor(candidate) ) { + if (candidate.isResizable) + candidate = candidate.withNode(resize(candidate.node)); + candidate = candidate.withNode(candidate.node.unretire()); + put(candidate); if (++deltaRetiredCount == 0) break; } } } - for (PrioritizableNode node : nodes.values()) { + for (NodeCandidate candidate : nodes.values()) { // Set whether the node is exclusive - Allocation allocation = node.node.allocation().get(); - node = node.withNode(node.node.with(allocation.with(allocation.membership() + Allocation allocation = candidate.node.allocation().get(); + candidate = candidate.withNode(candidate.node.with(allocation.with(allocation.membership() .with(allocation.membership().cluster().exclusive(requestedNodes.isExclusive()))))); - update(node); + put(candidate); } return nodes.values().stream().map(n -> n.node).collect(Collectors.toList()); @@ -373,7 +373,7 @@ class NodeAllocation { return nodesFilter(n -> n.isNewNode); } - private List nodesFilter(Predicate predicate) { + private List nodesFilter(Predicate predicate) { return nodes.values().stream() .filter(predicate) .map(n -> n.node) @@ -381,16 +381,16 @@ class NodeAllocation { } /** Prefer to retire nodes we want the least */ - private List byRetiringPriority(Collection nodes) { - return nodes.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList()); + private List byRetiringPriority(Collection candidates) { + return candidates.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList()); } /** Prefer to unretire nodes we don't want to retire, and otherwise those with lower index */ - private List byUnretiringPriority(Collection nodes) { - return nodes.stream() - .sorted(Comparator.comparing((PrioritizableNode n) -> n.node.status().wantToRetire()) - .thenComparing(n -> n.node.allocation().get().membership().index())) - .collect(Collectors.toList()); + private List byUnretiringPriority(Collection candidates) { + return candidates.stream() + .sorted(Comparator.comparing((NodeCandidate n) -> n.node.status().wantToRetire()) + .thenComparing(n -> n.node.allocation().get().membership().index())) + .collect(Collectors.toList()); } public String outOfCapacityDetails() { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java new file mode 100644 index 00000000000..651ab9b1e09 --- /dev/null +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java @@ -0,0 +1,234 @@ +// Copyright 2017 Yahoo Holdings. 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.config.provision.NodeResources; +import com.yahoo.vespa.hosted.provision.Node; + +import java.util.List; +import java.util.Optional; + +/** + * A node candidate containing the details required to prioritize it for allocation. This is immutable. + * + * @author smorgrav + */ +class NodeCandidate implements Comparable { + + /** List of host states ordered by preference (ascending) */ + private static final List HOST_STATE_PRIORITY = + List.of(Node.State.provisioned, Node.State.ready, Node.State.active); + + private static final NodeResources zeroResources = + new NodeResources(0, 0, 0, 0, NodeResources.DiskSpeed.any, NodeResources.StorageType.any); + + final Node node; + + /** The free capacity on the parent of this node, before adding this node to it */ + private final NodeResources freeParentCapacity; + + /** The parent host (docker or hypervisor) */ + final Optional parent; + + /** True if the node is allocated to a host that should be dedicated as a spare */ + final boolean violatesSpares; + + /** True if this node belongs to a group which will not be needed after this deployment */ + final boolean isSurplusNode; + + /** This node does not exist in the node repository yet */ + final boolean isNewNode; + + /** This node can be resized to the new NodeResources */ + final boolean isResizable; + + NodeCandidate(Node node, NodeResources freeParentCapacity, Optional parent, boolean violatesSpares, boolean isSurplusNode, boolean isNewNode, boolean isResizeable) { + if (isResizeable && isNewNode) + throw new IllegalArgumentException("A new node cannot be resizable"); + + this.node = node; + this.freeParentCapacity = freeParentCapacity; + this.parent = parent; + this.violatesSpares = violatesSpares; + this.isSurplusNode = isSurplusNode; + this.isNewNode = isNewNode; + this.isResizable = isResizeable; + } + + /** + * Compare this candidate to another + * + * @return negative if first priority is higher than second node + */ + @Override + public int compareTo(NodeCandidate other) { + // First always pick nodes without violation above nodes with violations + if (!this.violatesSpares && other.violatesSpares) return -1; + if (!other.violatesSpares && this.violatesSpares) return 1; + + // Choose active nodes + if (this.node.state() == Node.State.active && other.node.state() != Node.State.active) return -1; + if (other.node.state() == Node.State.active && this.node.state() != Node.State.active) return 1; + + // Choose active node that is not retired first (surplus is active but retired) + if (!this.isSurplusNode && other.isSurplusNode) return -1; + if (!other.isSurplusNode && this.isSurplusNode) return 1; + + // Choose reserved nodes from a previous allocation attempt (the exist in node repo) + if (this.isInNodeRepoAndReserved() && ! other.isInNodeRepoAndReserved()) return -1; + if (other.isInNodeRepoAndReserved() && ! this.isInNodeRepoAndReserved()) return 1; + + // Choose inactive nodes + if (this.node.state() == Node.State.inactive && other.node.state() != Node.State.inactive) return -1; + if (other.node.state() == Node.State.inactive && this.node.state() != Node.State.inactive) return 1; + + // Choose ready nodes + if (this.node.state() == Node.State.ready && other.node.state() != Node.State.ready) return -1; + if (other.node.state() == Node.State.ready && this.node.state() != Node.State.ready) return 1; + + if (this.node.state() != other.node.state()) + throw new IllegalStateException("Nodes " + this.node + " and " + other.node + " have different states"); + + if (this.parent.isPresent() && other.parent.isPresent()) { + // Prefer reserved hosts (that they are reserved to the right tenant is ensured elsewhere) + if ( this.parent.get().reservedTo().isPresent() && ! other.parent.get().reservedTo().isPresent()) return -1; + if ( ! this.parent.get().reservedTo().isPresent() && other.parent.get().reservedTo().isPresent()) return 1; + + int diskCostDifference = NodeResources.DiskSpeed.compare(this.parent.get().flavor().resources().diskSpeed(), + other.parent.get().flavor().resources().diskSpeed()); + if (diskCostDifference != 0) + return diskCostDifference; + + int storageCostDifference = NodeResources.StorageType.compare(this.parent.get().flavor().resources().storageType(), + other.parent.get().flavor().resources().storageType()); + if (storageCostDifference != 0) + return storageCostDifference; + + // Prefer hosts that are at least twice the size of this node + // (utilization is more even if one application does not dominate the host) + if ( lessThanHalfTheHost(this) && ! lessThanHalfTheHost(other)) return -1; + if ( ! lessThanHalfTheHost(this) && lessThanHalfTheHost(other)) return 1; + } + + int hostPriority = Double.compare(this.skewWithThis() - this.skewWithoutThis(), + other.skewWithThis() - other.skewWithoutThis()); + if (hostPriority != 0) return hostPriority; + + // Choose cheapest node + if (this.node.flavor().cost() < other.node.flavor().cost()) return -1; + if (other.node.flavor().cost() < this.node.flavor().cost()) return 1; + + // Choose nodes where host is in more desirable state + int thisHostStatePri = this.parent.map(host -> HOST_STATE_PRIORITY.indexOf(host.state())).orElse(-2); + int otherHostStatePri = other.parent.map(host -> HOST_STATE_PRIORITY.indexOf(host.state())).orElse(-2); + if (thisHostStatePri != otherHostStatePri) return otherHostStatePri - thisHostStatePri; + + // Prefer lower indexes to minimize redistribution + if (this.node.allocation().isPresent() && other.node.allocation().isPresent()) + return Integer.compare(this.node.allocation().get().membership().index(), + other.node.allocation().get().membership().index()); + + // All else equal choose hostname alphabetically + return this.node.hostname().compareTo(other.node.hostname()); + } + + /** Returns the allocation skew of the parent of this before adding this node to it */ + double skewWithoutThis() { return skewWith(zeroResources); } + + /** Returns the allocation skew of the parent of this after adding this node to it */ + double skewWithThis() { return skewWith(node.resources()); } + + /** Returns a copy of this with node set to given value */ + NodeCandidate withNode(Node node) { + return new NodeCandidate(node, freeParentCapacity, parent, violatesSpares, isSurplusNode, isNewNode, isResizable); + } + + private boolean lessThanHalfTheHost(NodeCandidate node) { + var n = node.node.resources(); + var h = node.parent.get().resources(); + if (h.vcpu() < n.vcpu() * 2) return false; + if (h.memoryGb() < n.memoryGb() * 2) return false; + if (h.diskGb() < n.diskGb() * 2) return false; + return true; + } + + private double skewWith(NodeResources resources) { + if (parent.isEmpty()) return 0; + + NodeResources free = freeParentCapacity.justNumbers().subtract(resources.justNumbers()); + return Node.skew(parent.get().flavor().resources(), free); + } + + private boolean isInNodeRepoAndReserved() { + if (isNewNode) return false; + return node.state().equals(Node.State.reserved); + } + + @Override + public String toString() { + return node.id(); + } + + @Override + public int hashCode() { + return node.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if ( ! (other instanceof NodeCandidate)) return false; + return this.node.equals(((NodeCandidate)other).node); + } + + static class Builder { + + public final Node node; + private NodeResources freeParentCapacity; + private Optional parent = Optional.empty(); + private boolean violatesSpares; + private boolean isSurplusNode; + private boolean isNewNode; + private boolean isResizable; + + Builder(Node node) { + this.node = node; + this.freeParentCapacity = node.flavor().resources(); + } + + /** The free capacity of the parent, before adding this node to it */ + Builder freeParentCapacity(NodeResources freeParentCapacity) { + this.freeParentCapacity = freeParentCapacity; + return this; + } + + Builder parent(Node parent) { + this.parent = Optional.of(parent); + return this; + } + + Builder violatesSpares(boolean violatesSpares) { + this.violatesSpares = violatesSpares; + return this; + } + + Builder surplusNode(boolean surplusNode) { + isSurplusNode = surplusNode; + return this; + } + + Builder newNode(boolean newNode) { + isNewNode = newNode; + return this; + } + + Builder resizable(boolean resizable) { + isResizable = resizable; + return this; + } + + NodeCandidate build() { + return new NodeCandidate(node, freeParentCapacity, parent, violatesSpares, isSurplusNode, isNewNode, isResizable); + } + } + +} 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 d5951490729..226079e43fe 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 @@ -24,9 +24,9 @@ import java.util.stream.Collectors; /** * Builds up data structures necessary for node prioritization. It wraps each node - * up in a PrioritizableNode object with attributes used in sorting. + * up in a {@link NodeCandidate} object with attributes used in sorting. * - * The actual sorting/prioritization is implemented in the PrioritizableNode class as a compare method. + * The prioritization logic is implemented by {@link NodeCandidate}. * * @author smorgrav */ @@ -34,7 +34,7 @@ public class NodePrioritizer { private final static Logger log = Logger.getLogger(NodePrioritizer.class.getName()); - private final Map nodes = new HashMap<>(); + private final Map nodes = new HashMap<>(); private final LockedNodeList allNodes; private final HostCapacity capacity; private final NodeSpec requestedNodes; @@ -80,8 +80,8 @@ public class NodePrioritizer { this.isDocker = resources(requestedNodes) != null; } - /** Returns the list of nodes sorted by {@link PrioritizableNode#compareTo(PrioritizableNode)} */ - List prioritize() { + /** Returns the list of nodes sorted by {@link NodeCandidate#compareTo(NodeCandidate)} */ + List prioritize() { return nodes.values().stream().sorted().collect(Collectors.toList()); } @@ -91,7 +91,7 @@ public class NodePrioritizer { */ void addSurplusNodes(List surplusNodes) { for (Node node : surplusNodes) { - PrioritizableNode nodePri = toPrioritizable(node, true, false); + NodeCandidate nodePri = candidateFrom(node, true, false); if (!nodePri.violatesSpares || isAllocatingForReplacement) { nodes.put(node, nodePri); } @@ -142,7 +142,7 @@ public class NodePrioritizer { resources(requestedNodes).with(host.flavor().resources().diskSpeed()) .with(host.flavor().resources().storageType()), NodeType.tenant); - PrioritizableNode nodePri = toPrioritizable(newNode, false, true); + NodeCandidate nodePri = candidateFrom(newNode, false, true); if ( ! nodePri.violatesSpares || isAllocatingForReplacement) { log.log(Level.FINE, "Adding new Docker node " + newNode); nodes.put(newNode, nodePri); @@ -158,7 +158,7 @@ public class NodePrioritizer { .filter(node -> legalStates.contains(node.state())) .filter(node -> node.allocation().isPresent()) .filter(node -> node.allocation().get().owner().equals(application)) - .map(node -> toPrioritizable(node, false, false)) + .map(node -> candidateFrom(node, false, false)) .forEach(prioritizableNode -> nodes.put(prioritizableNode.node, prioritizableNode)); } @@ -167,20 +167,17 @@ public class NodePrioritizer { allNodes.asList().stream() .filter(node -> node.type() == requestedNodes.type()) .filter(node -> node.state() == Node.State.ready) - .map(node -> toPrioritizable(node, false, false)) + .map(node -> candidateFrom(node, false, false)) .filter(n -> !n.violatesSpares || isAllocatingForReplacement) - .forEach(prioritizableNode -> nodes.put(prioritizableNode.node, prioritizableNode)); + .forEach(candidate -> nodes.put(candidate.node, candidate)); } - public List nodes() { return new ArrayList<>(nodes.values()); } + public List nodes() { return new ArrayList<>(nodes.values()); } - /** - * Convert a list of nodes to a list of node priorities. This includes finding, calculating - * parameters to the priority sorting procedure. - */ - private PrioritizableNode toPrioritizable(Node node, boolean isSurplusNode, boolean isNewNode) { - PrioritizableNode.Builder builder = new PrioritizableNode.Builder(node).surplusNode(isSurplusNode) - .newNode(isNewNode); + /** Create a candidate from given node */ + private NodeCandidate candidateFrom(Node node, boolean isSurplusNode, boolean isNewNode) { + NodeCandidate.Builder builder = new NodeCandidate.Builder(node).surplusNode(isSurplusNode) + .newNode(isNewNode); allNodes.parentOf(node).ifPresent(parent -> { NodeResources parentCapacity = capacity.freeCapacityOf(parent, false); 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 deleted file mode 100644 index dd1ecb453db..00000000000 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/PrioritizableNode.java +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright 2017 Yahoo Holdings. 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.config.provision.NodeResources; -import com.yahoo.vespa.hosted.provision.Node; - -import java.util.List; -import java.util.Optional; - -/** - * A node with additional information required to prioritize it for allocation. This is immutable. - * - * @author smorgrav - */ -class PrioritizableNode implements Comparable { - - /** List of host states ordered by preference (ascending) */ - private static final List HOST_STATE_PRIORITY = - List.of(Node.State.provisioned, Node.State.ready, Node.State.active); - - private static final NodeResources zeroResources = - new NodeResources(0, 0, 0, 0, NodeResources.DiskSpeed.any, NodeResources.StorageType.any); - - final Node node; - - /** The free capacity on the parent of this node, before adding this node to it */ - private final NodeResources freeParentCapacity; - - /** The parent host (docker or hypervisor) */ - final Optional parent; - - /** True if the node is allocated to a host that should be dedicated as a spare */ - final boolean violatesSpares; - - /** True if this node belongs to a group which will not be needed after this deployment */ - final boolean isSurplusNode; - - /** This node does not exist in the node repository yet */ - final boolean isNewNode; - - /** This node can be resized to the new NodeResources */ - final boolean isResizable; - - PrioritizableNode(Node node, NodeResources freeParentCapacity, Optional parent, boolean violatesSpares, boolean isSurplusNode, boolean isNewNode, boolean isResizeable) { - if (isResizeable && isNewNode) - throw new IllegalArgumentException("A new node cannot be resizable"); - - this.node = node; - this.freeParentCapacity = freeParentCapacity; - this.parent = parent; - this.violatesSpares = violatesSpares; - this.isSurplusNode = isSurplusNode; - this.isNewNode = isNewNode; - this.isResizable = isResizeable; - } - - /** - * Compares two prioritizable nodes - * - * @return negative if first priority is higher than second node - */ - @Override - public int compareTo(PrioritizableNode other) { - // First always pick nodes without violation above nodes with violations - if (!this.violatesSpares && other.violatesSpares) return -1; - if (!other.violatesSpares && this.violatesSpares) return 1; - - // Choose active nodes - if (this.node.state() == Node.State.active && other.node.state() != Node.State.active) return -1; - if (other.node.state() == Node.State.active && this.node.state() != Node.State.active) return 1; - - // Choose active node that is not retired first (surplus is active but retired) - if (!this.isSurplusNode && other.isSurplusNode) return -1; - if (!other.isSurplusNode && this.isSurplusNode) return 1; - - // Choose reserved nodes from a previous allocation attempt (the exist in node repo) - if (this.isInNodeRepoAndReserved() && ! other.isInNodeRepoAndReserved()) return -1; - if (other.isInNodeRepoAndReserved() && ! this.isInNodeRepoAndReserved()) return 1; - - // Choose inactive nodes - if (this.node.state() == Node.State.inactive && other.node.state() != Node.State.inactive) return -1; - if (other.node.state() == Node.State.inactive && this.node.state() != Node.State.inactive) return 1; - - // Choose ready nodes - if (this.node.state() == Node.State.ready && other.node.state() != Node.State.ready) return -1; - if (other.node.state() == Node.State.ready && this.node.state() != Node.State.ready) return 1; - - if (this.node.state() != other.node.state()) - throw new IllegalStateException("Nodes " + this.node + " and " + other.node + " have different states"); - - if (this.parent.isPresent() && other.parent.isPresent()) { - // Prefer reserved hosts (that they are reserved to the right tenant is ensured elsewhere) - if ( this.parent.get().reservedTo().isPresent() && ! other.parent.get().reservedTo().isPresent()) return -1; - if ( ! this.parent.get().reservedTo().isPresent() && other.parent.get().reservedTo().isPresent()) return 1; - - int diskCostDifference = NodeResources.DiskSpeed.compare(this.parent.get().flavor().resources().diskSpeed(), - other.parent.get().flavor().resources().diskSpeed()); - if (diskCostDifference != 0) - return diskCostDifference; - - int storageCostDifference = NodeResources.StorageType.compare(this.parent.get().flavor().resources().storageType(), - other.parent.get().flavor().resources().storageType()); - if (storageCostDifference != 0) - return storageCostDifference; - - // Prefer hosts that are at least twice the size of this node - // (utilization is more even if one application does not dominate the host) - if ( lessThanHalfTheHost(this) && ! lessThanHalfTheHost(other)) return -1; - if ( ! lessThanHalfTheHost(this) && lessThanHalfTheHost(other)) return 1; - } - - int hostPriority = Double.compare(this.skewWithThis() - this.skewWithoutThis(), - other.skewWithThis() - other.skewWithoutThis()); - if (hostPriority != 0) return hostPriority; - - // Choose cheapest node - if (this.node.flavor().cost() < other.node.flavor().cost()) return -1; - if (other.node.flavor().cost() < this.node.flavor().cost()) return 1; - - // Choose nodes where host is in more desirable state - int thisHostStatePri = this.parent.map(host -> HOST_STATE_PRIORITY.indexOf(host.state())).orElse(-2); - int otherHostStatePri = other.parent.map(host -> HOST_STATE_PRIORITY.indexOf(host.state())).orElse(-2); - if (thisHostStatePri != otherHostStatePri) return otherHostStatePri - thisHostStatePri; - - // Prefer lower indexes to minimize redistribution - if (this.node.allocation().isPresent() && other.node.allocation().isPresent()) - return Integer.compare(this.node.allocation().get().membership().index(), - other.node.allocation().get().membership().index()); - - // All else equal choose hostname alphabetically - return this.node.hostname().compareTo(other.node.hostname()); - } - - /** Returns the allocation skew of the parent of this before adding this node to it */ - double skewWithoutThis() { return skewWith(zeroResources); } - - /** Returns the allocation skew of the parent of this after adding this node to it */ - double skewWithThis() { return skewWith(node.resources()); } - - /** Returns a copy of this with node set to given value */ - PrioritizableNode withNode(Node node) { - return new PrioritizableNode(node, freeParentCapacity, parent, violatesSpares, isSurplusNode, isNewNode, isResizable); - } - - private boolean lessThanHalfTheHost(PrioritizableNode node) { - var n = node.node.resources(); - var h = node.parent.get().resources(); - if (h.vcpu() < n.vcpu() * 2) return false; - if (h.memoryGb() < n.memoryGb() * 2) return false; - if (h.diskGb() < n.diskGb() * 2) return false; - return true; - } - - private double skewWith(NodeResources resources) { - if (parent.isEmpty()) return 0; - - NodeResources free = freeParentCapacity.justNumbers().subtract(resources.justNumbers()); - return Node.skew(parent.get().flavor().resources(), free); - } - - private boolean isInNodeRepoAndReserved() { - if (isNewNode) return false; - return node.state().equals(Node.State.reserved); - } - - @Override - public String toString() { - return node.id(); - } - - @Override - public int hashCode() { - return node.hashCode(); - } - - @Override - public boolean equals(Object other) { - if (other == this) return true; - if ( ! (other instanceof PrioritizableNode)) return false; - return this.node.equals(((PrioritizableNode)other).node); - } - - static class Builder { - - public final Node node; - private NodeResources freeParentCapacity; - private Optional parent = Optional.empty(); - private boolean violatesSpares; - private boolean isSurplusNode; - private boolean isNewNode; - private boolean isResizable; - - Builder(Node node) { - this.node = node; - this.freeParentCapacity = node.flavor().resources(); - } - - /** The free capacity of the parent, before adding this node to it */ - Builder freeParentCapacity(NodeResources freeParentCapacity) { - this.freeParentCapacity = freeParentCapacity; - return this; - } - - Builder parent(Node parent) { - this.parent = Optional.of(parent); - return this; - } - - Builder violatesSpares(boolean violatesSpares) { - this.violatesSpares = violatesSpares; - return this; - } - - Builder surplusNode(boolean surplusNode) { - isSurplusNode = surplusNode; - return this; - } - - Builder newNode(boolean newNode) { - isNewNode = newNode; - return this; - } - - Builder resizable(boolean resizable) { - isResizable = resizable; - return this; - } - - PrioritizableNode build() { - return new PrioritizableNode(node, freeParentCapacity, parent, violatesSpares, isSurplusNode, isNewNode, isResizable); - } - } - -} diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidateTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidateTest.java new file mode 100644 index 00000000000..95b9f334bb4 --- /dev/null +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidateTest.java @@ -0,0 +1,149 @@ +// Copyright 2020 Oath Inc. 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.config.provision.Flavor; +import com.yahoo.config.provision.NodeResources; +import com.yahoo.config.provision.NodeType; +import com.yahoo.vespa.hosted.provision.Node; +import com.yahoo.vespa.hosted.provision.node.History; +import com.yahoo.vespa.hosted.provision.node.IP; +import com.yahoo.vespa.hosted.provision.node.Reports; +import com.yahoo.vespa.hosted.provision.node.Status; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import static org.junit.Assert.assertEquals; + +/** + * @author bratseth + */ +public class NodeCandidateTest { + + @Test + public void test_order() { + List expected = List.of( + new NodeCandidate(node("01", Node.State.ready), new NodeResources(2, 2, 2, 2), Optional.empty(), false, true, false, false), + new NodeCandidate(node("02", Node.State.active), new NodeResources(2, 2, 2, 2), Optional.empty(), true, false, false, false), + new NodeCandidate(node("04", Node.State.reserved), new NodeResources(2, 2, 2, 2), Optional.empty(), true, false, false, false), + new NodeCandidate(node("03", Node.State.inactive), new NodeResources(2, 2, 2, 2), Optional.empty(), true, false, false, false), + new NodeCandidate(node("05", Node.State.ready), new NodeResources(2, 2, 2, 2), Optional.of(node("host1", Node.State.active)), true, false, true, false), + new NodeCandidate(node("06", Node.State.ready), new NodeResources(2, 2, 2, 2), Optional.of(node("host1", Node.State.ready)), true, false, true, false), + new NodeCandidate(node("07", Node.State.ready), new NodeResources(2, 2, 2, 2), Optional.of(node("host1", Node.State.provisioned)), true, false, true, false), + new NodeCandidate(node("08", Node.State.ready), new NodeResources(2, 2, 2, 2), Optional.of(node("host1", Node.State.failed)), true, false, true, false), + new NodeCandidate(node("09", Node.State.ready), new NodeResources(1, 1, 1, 1), Optional.empty(), true, false, true, false), + new NodeCandidate(node("10", Node.State.ready), new NodeResources(2, 2, 2, 2), Optional.empty(), true, false, true, false), + new NodeCandidate(node("11", Node.State.ready), new NodeResources(2, 2, 2, 2), Optional.empty(), true, false, true, false) + ); + assertOrder(expected); + } + + @Test + public void testOrderingByAllocationSkew1() { + List expected = List.of( + node("1", node(4, 4), host(20, 20), host(40, 40)), + node("2", node(4, 4), host(21, 20), host(40, 40)), + node("3", node(4, 4), host(22, 20), host(40, 40)), + node("4", node(4, 4), host(21, 22), host(40, 40)), + node("5", node(4, 4), host(21, 21), host(40, 80)) + ); + assertOrder(expected); + } + + @Test + public void testOrderingByAllocationSkew2() { + // The same as testOrderingByAllocationSkew1, but deviating from mean (20) in the other direction. + // Since we don't choose the node with the lowest skew, but with the largest skew *reduction* + // this causes the opposite order. + List expected = List.of( + node("4", node(4, 4), host(19, 18), host(40, 40)), + node("3", node(4, 4), host(18, 20), host(40, 40)), + node("2", node(4, 4), host(19, 20), host(40, 40)), + node("1", node(4, 4), host(20, 20), host(40, 40)), + node("5", node(4, 4), host(19, 19), host(40, 80)) + ); + assertOrder(expected); + } + + @Test + public void testOrderingByAllocationSkew3() { + // The same as testOrderingByAllocationSkew1, but allocating skewed towards cpu + List expected = List.of( + node("1", node(4, 2), host(20, 20), host(40, 40)), + node("2", node(4, 2), host(21, 20), host(40, 40)), + node("4", node(4, 2), host(21, 22), host(40, 40)), + node("3", node(4, 2), host(22, 20), host(40, 40)), + node("5", node(4, 2), host(21, 21), host(40, 80)) + ); + assertOrder(expected); + } + + @Test + public void testOrderingByAllocationSkew4() { + // The same as testOrderingByAllocationSkew1, but allocating skewed towards memory + List expected = List.of( + node("5", node(2, 10), host(21, 21), host(40, 80)), + node("3", node(2, 10), host(22, 20), host(40, 40)), + node("2", node(2, 10), host(21, 20), host(40, 40)), + node("1", node(2, 10), host(20, 20), host(40, 40)), + node("4", node(2, 10), host(21, 22), host(40, 40)) + ); + assertOrder(expected); + } + + @Test + public void testOrderingByAllocationSkew5() { + // node1 is skewed towards cpu (without this allocation), allocation is skewed towards memory, therefore + // node 1 is preferred (even though it is still most skewed) + List expected = List.of( + node("1", node(1, 5), host(21, 10), host(40, 40)), + node("2", node(1, 5), host(21, 20), host(40, 40)), + node("3", node(1, 5), host(20, 20), host(40, 40)), + node("4", node(1, 5), host(20, 22), host(40, 40)) + ); + assertOrder(expected); + } + + private void assertOrder(List expected) { + List copy = new ArrayList<>(expected); + Collections.shuffle(copy); + Collections.sort(copy); + assertEquals(expected, copy); + } + + private static NodeResources node(double vcpu, double mem) { + return new NodeResources(vcpu, mem, 0, 0); + } + + private static NodeResources host(double vcpu, double mem) { + return new NodeResources(vcpu, mem, 10, 10); + } + + private static Node node(String hostname, Node.State state) { + return new Node(hostname, new IP.Config(Set.of("::1"), Set.of()), hostname, Optional.empty(), + new Flavor(new NodeResources(2, 2, 2, 2)), + Status.initial(), state, Optional.empty(), History.empty(), NodeType.tenant, new Reports(), + Optional.empty(), Optional.empty()); + } + + private static NodeCandidate node(String hostname, + NodeResources nodeResources, + NodeResources allocatedHostResources, // allocated before adding nodeResources + NodeResources totalHostResources) { + Node node = new Node(hostname, new IP.Config(Set.of("::1"), Set.of()), hostname, Optional.of(hostname + "parent"), + new Flavor(nodeResources), + Status.initial(), Node.State.ready, Optional.empty(), History.empty(), NodeType.tenant, + new Reports(), Optional.empty(), Optional.empty()); + Node parent = new Node(hostname + "parent", new IP.Config(Set.of("::1"), Set.of()), hostname, Optional.empty(), + new Flavor(totalHostResources), + Status.initial(), Node.State.ready, Optional.empty(), History.empty(), NodeType.host, + new Reports(), Optional.empty(), Optional.empty()); + return new NodeCandidate(node, totalHostResources.subtract(allocatedHostResources), Optional.of(parent), + false, false, true, false); + } + +} diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/PrioritizableNodeTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/PrioritizableNodeTest.java deleted file mode 100644 index 3865baa51c1..00000000000 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/PrioritizableNodeTest.java +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2020 Oath Inc. 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.config.provision.Flavor; -import com.yahoo.config.provision.NodeResources; -import com.yahoo.config.provision.NodeType; -import com.yahoo.vespa.hosted.provision.Node; -import com.yahoo.vespa.hosted.provision.node.History; -import com.yahoo.vespa.hosted.provision.node.IP; -import com.yahoo.vespa.hosted.provision.node.Reports; -import com.yahoo.vespa.hosted.provision.node.Status; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import static org.junit.Assert.assertEquals; - -/** - * @author bratseth - */ -public class PrioritizableNodeTest { - - @Test - public void test_order() { - List expected = List.of( - new PrioritizableNode(node("01", Node.State.ready), new NodeResources(2, 2, 2, 2), Optional.empty(), false, true, false, false), - new PrioritizableNode(node("02", Node.State.active), new NodeResources(2, 2, 2, 2), Optional.empty(), true, false, false, false), - new PrioritizableNode(node("04", Node.State.reserved), new NodeResources(2, 2, 2, 2), Optional.empty(), true, false, false, false), - new PrioritizableNode(node("03", Node.State.inactive), new NodeResources(2, 2, 2, 2), Optional.empty(), true, false, false, false), - new PrioritizableNode(node("05", Node.State.ready), new NodeResources(2, 2, 2, 2), Optional.of(node("host1", Node.State.active)), true, false, true, false), - new PrioritizableNode(node("06", Node.State.ready), new NodeResources(2, 2, 2, 2), Optional.of(node("host1", Node.State.ready)), true, false, true, false), - new PrioritizableNode(node("07", Node.State.ready), new NodeResources(2, 2, 2, 2), Optional.of(node("host1", Node.State.provisioned)), true, false, true, false), - new PrioritizableNode(node("08", Node.State.ready), new NodeResources(2, 2, 2, 2), Optional.of(node("host1", Node.State.failed)), true, false, true, false), - new PrioritizableNode(node("09", Node.State.ready), new NodeResources(1, 1, 1, 1), Optional.empty(), true, false, true, false), - new PrioritizableNode(node("10", Node.State.ready), new NodeResources(2, 2, 2, 2), Optional.empty(), true, false, true, false), - new PrioritizableNode(node("11", Node.State.ready), new NodeResources(2, 2, 2, 2), Optional.empty(), true, false, true, false) - ); - assertOrder(expected); - } - - @Test - public void testOrderingByAllocationSkew1() { - List expected = List.of( - node("1", node(4, 4), host(20, 20), host(40, 40)), - node("2", node(4, 4), host(21, 20), host(40, 40)), - node("3", node(4, 4), host(22, 20), host(40, 40)), - node("4", node(4, 4), host(21, 22), host(40, 40)), - node("5", node(4, 4), host(21, 21), host(40, 80)) - ); - assertOrder(expected); - } - - @Test - public void testOrderingByAllocationSkew2() { - // The same as testOrderingByAllocationSkew1, but deviating from mean (20) in the other direction. - // Since we don't choose the node with the lowest skew, but with the largest skew *reduction* - // this causes the opposite order. - List expected = List.of( - node("4", node(4, 4), host(19, 18), host(40, 40)), - node("3", node(4, 4), host(18, 20), host(40, 40)), - node("2", node(4, 4), host(19, 20), host(40, 40)), - node("1", node(4, 4), host(20, 20), host(40, 40)), - node("5", node(4, 4), host(19, 19), host(40, 80)) - ); - assertOrder(expected); - } - - @Test - public void testOrderingByAllocationSkew3() { - // The same as testOrderingByAllocationSkew1, but allocating skewed towards cpu - List expected = List.of( - node("1", node(4, 2), host(20, 20), host(40, 40)), - node("2", node(4, 2), host(21, 20), host(40, 40)), - node("4", node(4, 2), host(21, 22), host(40, 40)), - node("3", node(4, 2), host(22, 20), host(40, 40)), - node("5", node(4, 2), host(21, 21), host(40, 80)) - ); - assertOrder(expected); - } - - @Test - public void testOrderingByAllocationSkew4() { - // The same as testOrderingByAllocationSkew1, but allocating skewed towards memory - List expected = List.of( - node("5", node(2, 10), host(21, 21), host(40, 80)), - node("3", node(2, 10), host(22, 20), host(40, 40)), - node("2", node(2, 10), host(21, 20), host(40, 40)), - node("1", node(2, 10), host(20, 20), host(40, 40)), - node("4", node(2, 10), host(21, 22), host(40, 40)) - ); - assertOrder(expected); - } - - @Test - public void testOrderingByAllocationSkew5() { - // node1 is skewed towards cpu (without this allocation), allocation is skewed towards memory, therefore - // node 1 is preferred (even though it is still most skewed) - List expected = List.of( - node("1", node(1, 5), host(21, 10), host(40, 40)), - node("2", node(1, 5), host(21, 20), host(40, 40)), - node("3", node(1, 5), host(20, 20), host(40, 40)), - node("4", node(1, 5), host(20, 22), host(40, 40)) - ); - assertOrder(expected); - } - - private void assertOrder(List expected) { - List copy = new ArrayList<>(expected); - Collections.shuffle(copy); - Collections.sort(copy); - assertEquals(expected, copy); - } - - private static NodeResources node(double vcpu, double mem) { - return new NodeResources(vcpu, mem, 0, 0); - } - - private static NodeResources host(double vcpu, double mem) { - return new NodeResources(vcpu, mem, 10, 10); - } - - private static Node node(String hostname, Node.State state) { - return new Node(hostname, new IP.Config(Set.of("::1"), Set.of()), hostname, Optional.empty(), - new Flavor(new NodeResources(2, 2, 2, 2)), - Status.initial(), state, Optional.empty(), History.empty(), NodeType.tenant, new Reports(), - Optional.empty(), Optional.empty()); - } - - private static PrioritizableNode node(String hostname, - NodeResources nodeResources, - NodeResources allocatedHostResources, // allocated before adding nodeResources - NodeResources totalHostResources) { - Node node = new Node(hostname, new IP.Config(Set.of("::1"), Set.of()), hostname, Optional.of(hostname + "parent"), - new Flavor(nodeResources), - Status.initial(), Node.State.ready, Optional.empty(), History.empty(), NodeType.tenant, - new Reports(), Optional.empty(), Optional.empty()); - Node parent = new Node(hostname + "parent", new IP.Config(Set.of("::1"), Set.of()), hostname, Optional.empty(), - new Flavor(totalHostResources), - Status.initial(), Node.State.ready, Optional.empty(), History.empty(), NodeType.host, - new Reports(), Optional.empty(), Optional.empty()); - return new PrioritizableNode(node, totalHostResources.subtract(allocatedHostResources), Optional.of(parent), - false, false, true, false); - } - -} -- cgit v1.2.3 From cca8ef0d7b033475d2e32b211f136dbdeb8fee11 Mon Sep 17 00:00:00 2001 From: Martin Polden Date: Wed, 16 Sep 2020 12:36:39 +0200 Subject: Rename method --- .../provision/autoscale/AutoscalingTester.java | 5 +- .../provision/autoscale/NodeMetricsDbTest.java | 10 ++-- .../autoscale/NodeMetricsFetcherTest.java | 3 +- .../maintenance/AutoscalingMaintainerTest.java | 2 +- .../DynamicProvisioningMaintainerTest.java | 2 +- .../provision/maintenance/RebalancerTest.java | 10 ++-- .../ScalingSuggestionsMaintainerTest.java | 2 +- .../provisioning/AclProvisioningTest.java | 2 +- .../provision/provisioning/DockerImagesTest.java | 2 +- ...ckerProvisioningCompleteHostCalculatorTest.java | 2 +- .../provisioning/DockerProvisioningTest.java | 13 +++-- .../provisioning/DynamicDockerAllocationTest.java | 26 +++++----- .../provisioning/DynamicDockerProvisionTest.java | 14 +++--- .../provisioning/MultigroupProvisioningTest.java | 6 +-- .../provision/provisioning/ProvisioningTest.java | 58 +++++++++++----------- .../provision/provisioning/ProvisioningTester.java | 4 +- 16 files changed, 79 insertions(+), 82 deletions(-) (limited to 'node-repository') 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 0a127eacae1..19911076e69 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 @@ -32,7 +32,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import static com.yahoo.config.provision.NodeResources.StorageType.local; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -51,7 +50,7 @@ class AutoscalingTester { public AutoscalingTester(NodeResources hostResources, HostResourcesCalculator resourcesCalculator) { this(new Zone(Environment.prod, RegionName.from("us-east")), List.of(new Flavor("hostFlavor", hostResources)), resourcesCalculator); provisioningTester.makeReadyNodes(20, "hostFlavor", NodeType.host, 8); - provisioningTester.deployZoneApp(); + provisioningTester.activateTenantHosts(); } public AutoscalingTester(Zone zone, List flavors) { @@ -87,7 +86,7 @@ class AutoscalingTester { List hosts = provisioningTester.prepare(application, cluster, Capacity.from(new ClusterResources(nodes, groups, resources))); for (HostSpec host : hosts) makeReady(host.hostname()); - provisioningTester.deployZoneApp(); + provisioningTester.activateTenantHosts(); provisioningTester.activate(application, hosts); return hosts; } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricsDbTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricsDbTest.java index 976aeb2346a..eaad4526591 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricsDbTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricsDbTest.java @@ -6,10 +6,7 @@ import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.NodeResources; -import com.yahoo.config.provision.NodeType; import com.yahoo.test.ManualClock; -import com.yahoo.vespa.hosted.provision.NodeRepository; -import com.yahoo.vespa.hosted.provision.NodeRepositoryTester; import com.yahoo.vespa.hosted.provision.provisioning.ProvisioningTester; import org.junit.Test; @@ -19,14 +16,17 @@ import java.util.List; import static org.junit.Assert.assertEquals; +/** + * @author bratseth + */ public class NodeMetricsDbTest { @Test public void testNodeMetricsDb() { ProvisioningTester tester = new ProvisioningTester.Builder().build(); - tester.makeReadyHosts(10, new NodeResources(10, 100, 1000, 10)).deployZoneApp(); + tester.makeReadyHosts(10, new NodeResources(10, 100, 1000, 10)) + .activateTenantHosts(); ApplicationId app1 = tester.makeApplicationId("app1"); - tester.deployZoneApp(); var hosts = tester.activate(app1, ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test")).vespaVersion("7.0").build(), diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricsFetcherTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricsFetcherTest.java index d418d818ef3..fea3e8da70a 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricsFetcherTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/NodeMetricsFetcherTest.java @@ -5,7 +5,6 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.NodeResources; -import com.yahoo.vdslib.state.NodeState; import com.yahoo.vespa.hosted.provision.Node; import com.yahoo.vespa.hosted.provision.provisioning.ProvisioningTester; import com.yahoo.vespa.hosted.provision.testutils.OrchestratorMock; @@ -29,7 +28,7 @@ public class NodeMetricsFetcherTest { NodeMetricsFetcher fetcher = new NodeMetricsFetcher(tester.nodeRepository(), orchestrator, httpClient); tester.makeReadyNodes(4, resources); // Creates (in order) host-1.yahoo.com, host-2.yahoo.com, host-3.yahoo.com, host-4.yahoo.com - tester.deployZoneApp(); + tester.activateTenantHosts(); ApplicationId application1 = tester.makeApplicationId(); ApplicationId application2 = tester.makeApplicationId(); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java index 88195cf0ed9..833daebc37a 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/AutoscalingMaintainerTest.java @@ -65,7 +65,7 @@ public class AutoscalingMaintainerTest { assertTrue(deployer.lastDeployTime(app2).isEmpty()); tester.makeReadyNodes(20, "flt", NodeType.host, 8); - tester.deployZoneApp(); + tester.activateTenantHosts(); tester.deploy(app1, cluster1, Capacity.from(new ClusterResources(5, 1, new NodeResources(4, 4, 10, 0.1)), new ClusterResources(5, 1, new NodeResources(4, 4, 10, 0.1)), 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 45da1f1d3ee..12cf114b2d2 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 @@ -179,7 +179,7 @@ public class DynamicProvisioningMaintainerTest { tester.maintainer.maintain(); // Resume provisioning of new hosts List provisioned = tester.nodeRepository.list().state(Node.State.provisioned).asList(); tester.nodeRepository.setReady(provisioned, Agent.system, this.getClass().getSimpleName()); - tester.provisioningTester.deployZoneApp(); + tester.provisioningTester.activateTenantHosts(); // Allocating nodes to a host does not result in provisioning of additional capacity ApplicationId application = tester.provisioningTester.makeApplicationId(); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RebalancerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RebalancerTest.java index 05e5b4829e9..65b79c2df04 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RebalancerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RebalancerTest.java @@ -54,7 +54,7 @@ public class RebalancerTest { // --- Making a more suitable node configuration available causes rebalancing Node newCpuHost = tester.makeReadyNode("cpu"); - tester.deployZoneApp(); + tester.activateTenantHosts(); tester.maintain(); assertTrue("Rebalancer retired the node we wanted to move away from", tester.isNodeRetired(cpuSkewedNode)); @@ -74,7 +74,7 @@ public class RebalancerTest { // --- Adding a more suitable node reconfiguration causes no action as the system is not stable Node memSkewedNode = tester.getNode(memoryApp); Node newMemHost = tester.makeReadyNode("mem"); - tester.deployZoneApp(); + tester.activateTenantHosts(); tester.maintain(); assertFalse("No rebalancing happens because cpuSkewedNode is still retired", tester.isNodeRetired(memSkewedNode)); @@ -117,7 +117,7 @@ public class RebalancerTest { // --- Making a more suitable node configuration available causes rebalancing Node newCpuHost = tester.makeReadyNode("cpu"); - tester.deployZoneApp(); + tester.activateTenantHosts(); tester.deployApp(cpuApp, false /* skip advancing clock after deployment */); tester.maintain(); @@ -152,7 +152,7 @@ public class RebalancerTest { deployer = new MockDeployer(tester.provisioner(), tester.clock(), apps); rebalancer = new Rebalancer(deployer, tester.nodeRepository(), metric, tester.clock(), Duration.ofMinutes(1)); tester.makeReadyNodes(3, "flat", NodeType.host, 8); - tester.deployZoneApp(); + tester.activateTenantHosts(); } void maintain() { rebalancer.maintain(); } @@ -163,7 +163,7 @@ public class RebalancerTest { NodeRepository nodeRepository() { return tester.nodeRepository(); } - void deployZoneApp() { tester.deployZoneApp(); } + void activateTenantHosts() { tester.activateTenantHosts(); } void deployApp(ApplicationId id) { deployApp(id, true); } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ScalingSuggestionsMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ScalingSuggestionsMaintainerTest.java index 31a9fcb8999..bd2afb5d1c8 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ScalingSuggestionsMaintainerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/ScalingSuggestionsMaintainerTest.java @@ -47,7 +47,7 @@ public class ScalingSuggestionsMaintainerTest { NodeMetricsDb nodeMetricsDb = new NodeMetricsDb(tester.nodeRepository()); tester.makeReadyNodes(20, "flt", NodeType.host, 8); - tester.deployZoneApp(); + tester.activateTenantHosts(); tester.deploy(app1, cluster1, Capacity.from(new ClusterResources(5, 1, new NodeResources(4, 4, 10, 0.1)), new ClusterResources(5, 1, new NodeResources(4, 4, 10, 0.1)), 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 d165f865432..505e53c9195 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 @@ -165,7 +165,7 @@ public class AclProvisioningTest { public void trusted_nodes_for_application_with_load_balancer() { // Provision hosts and containers var hosts = tester.makeReadyNodes(2, "default", NodeType.host); - tester.deployZoneApp(); + tester.activateTenantHosts(); for (var host : hosts) { tester.makeReadyVirtualDockerNodes(2, new NodeResources(2, 8, 50, 1), host.hostname()); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerImagesTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerImagesTest.java index cd6ae587b04..d5437296620 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerImagesTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerImagesTest.java @@ -27,7 +27,7 @@ public class DockerImagesTest { // Host uses tenant default image (for preload purposes) var defaultImage = DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa"); var hosts = tester.makeReadyNodes(2, "default", NodeType.host); - tester.deployZoneApp(); + tester.activateTenantHosts(); for (var host : hosts) { assertEquals(defaultImage, tester.nodeRepository().dockerImages().dockerImageFor(host.type())); } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningCompleteHostCalculatorTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningCompleteHostCalculatorTest.java index 24cdc5c8fd0..2588818a9d3 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningCompleteHostCalculatorTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerProvisioningCompleteHostCalculatorTest.java @@ -30,7 +30,7 @@ public class DockerProvisioningCompleteHostCalculatorTest { .resourcesCalculator(new CompleteResourcesCalculator(hostFlavor)) .flavors(List.of(hostFlavor)) .build(); - tester.makeReadyHosts(9, hostFlavor.resources()).deployZoneApp(); + tester.makeReadyHosts(9, hostFlavor.resources()).activateTenantHosts(); ApplicationId app1 = tester.makeApplicationId("app1"); ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build(); 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 e566172b524..6ae78f9019c 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 @@ -20,7 +20,6 @@ import com.yahoo.config.provision.TenantName; 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.NodeRepository; import org.junit.Test; import java.util.HashSet; @@ -123,7 +122,7 @@ public class DockerProvisioningTest { tester.makeReadyNodes(10, resources, Optional.of(tenant1), NodeType.host, 1); tester.makeReadyNodes(10, resources, Optional.empty(), NodeType.host, 1); - tester.deployZoneApp(); + tester.activateTenantHosts(); Version wantedVespaVersion = Version.fromString("6.39"); List nodes = tester.prepare(application2_1, @@ -310,7 +309,7 @@ public class DockerProvisioningTest { .resourcesCalculator(3, 0) .flavors(List.of(hostFlavor)) .build(); - tester.makeReadyHosts(2, hostFlavor.resources()).deployZoneApp(); + tester.makeReadyHosts(2, hostFlavor.resources()).activateTenantHosts(); ApplicationId app1 = tester.makeApplicationId("app1"); ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build(); @@ -330,7 +329,7 @@ public class DockerProvisioningTest { .resourcesCalculator(3, 0) .flavors(List.of(hostFlavor)) .build(); - tester.makeReadyHosts(9, hostFlavor.resources()).deployZoneApp(); + tester.makeReadyHosts(9, hostFlavor.resources()).activateTenantHosts(); ApplicationId app1 = tester.makeApplicationId("app1"); ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build(); @@ -365,7 +364,7 @@ public class DockerProvisioningTest { .resourcesCalculator(3, 0) .flavors(List.of(hostFlavor)) .build(); - tester.makeReadyHosts(2, hostFlavor.resources()).deployZoneApp(); + tester.makeReadyHosts(2, hostFlavor.resources()).activateTenantHosts(); ApplicationId app1 = tester.makeApplicationId("app1"); ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build(); @@ -387,7 +386,7 @@ public class DockerProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))) .flavors(List.of(new Flavor(r))) .build(); - tester.makeReadyHosts(5, r).deployZoneApp(); + tester.makeReadyHosts(5, r).activateTenantHosts(); ApplicationId app1 = tester.makeApplicationId("app1"); ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.container, new ClusterSpec.Id("cluster1")).vespaVersion("7").build(); @@ -420,7 +419,7 @@ public class DockerProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))) .flavors(List.of(new Flavor(r))) .build(); - tester.makeReadyHosts(4, r).deployZoneApp(); + tester.makeReadyHosts(4, r).activateTenantHosts(); ApplicationId app1 = tester.makeApplicationId("app1"); ClusterSpec cluster1 = ClusterSpec.request(clusterType, new ClusterSpec.Id("cluster1")).vespaVersion("7").build(); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java index 29e371dd937..ab62972df30 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java @@ -66,7 +66,7 @@ public class DynamicDockerAllocationTest { .spareCount(spareCount) .build(); tester.makeReadyNodes(4, "host-small", NodeType.host, 32); - tester.deployZoneApp(); + tester.activateTenantHosts(); List dockerHosts = tester.nodeRepository().getNodes(NodeType.host, Node.State.active); NodeResources flavor = new NodeResources(1, 4, 100, 1); @@ -109,7 +109,7 @@ public class DynamicDockerAllocationTest { public void relocate_failed_nodes() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).flavorsConfig(flavorsConfig()).build(); tester.makeReadyNodes(5, "host-small", NodeType.host, 32); - tester.deployZoneApp(); + tester.activateTenantHosts(); List dockerHosts = tester.nodeRepository().getNodes(NodeType.host, Node.State.active); NodeResources resources = new NodeResources(1, 4, 100, 0.3); @@ -158,7 +158,7 @@ public class DynamicDockerAllocationTest { tester.makeReadyNodes(3, "flt", NodeType.host, 8); // cpu: 30, mem: 30 tester.makeReadyNodes(3, "cpu", NodeType.host, 8); // cpu: 40, mem: 20 tester.makeReadyNodes(3, "mem", NodeType.host, 8); // cpu: 20, mem: 40 - tester.deployZoneApp(); + tester.activateTenantHosts(); NodeResources fltResources = new NodeResources(6, 6, 10, 0.1); NodeResources cpuResources = new NodeResources(8, 4, 10, 0.1); NodeResources memResources = new NodeResources(4, 8, 10, 0.1); @@ -201,7 +201,7 @@ public class DynamicDockerAllocationTest { public void do_not_relocate_nodes_from_spare_if_no_where_to_relocate_them() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).flavorsConfig(flavorsConfig()).build(); tester.makeReadyNodes(2, "host-small", NodeType.host, 32); - tester.deployZoneApp(); + tester.activateTenantHosts(); List dockerHosts = tester.nodeRepository().getNodes(NodeType.host, Node.State.active); NodeResources flavor = new NodeResources(1, 4, 100, 1); @@ -228,7 +228,7 @@ public class DynamicDockerAllocationTest { public void multiple_groups_are_on_separate_parent_hosts() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).flavorsConfig(flavorsConfig()).build(); tester.makeReadyNodes(5, "host-small", NodeType.host, 32); - tester.deployZoneApp(); + tester.activateTenantHosts(); //Deploy an application having 6 nodes (3 nodes in 2 groups). We only have 5 docker hosts available ApplicationId application1 = tester.makeApplicationId(); @@ -249,7 +249,7 @@ public class DynamicDockerAllocationTest { // Setup test ApplicationId application1 = tester.makeApplicationId(); tester.makeReadyNodes(5, "host-small", NodeType.host, 32); - tester.deployZoneApp(); + tester.activateTenantHosts(); NodeResources flavor = new NodeResources(1, 4, 100, 1); // Deploy initial state (can max deploy 3 nodes due to redundancy requirements) @@ -278,7 +278,7 @@ public class DynamicDockerAllocationTest { public void non_prod_zones_do_not_have_spares() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.perf, RegionName.from("us-east"))).flavorsConfig(flavorsConfig()).build(); tester.makeReadyNodes(3, "host-small", NodeType.host, 32); - tester.deployZoneApp(); + tester.activateTenantHosts(); ApplicationId application1 = tester.makeApplicationId(); List hosts = tester.prepare(application1, clusterSpec("myContent.t1.a1"), 3, 1, new NodeResources(1, 4, 100, 1)); tester.activate(application1, ImmutableSet.copyOf(hosts)); @@ -291,7 +291,7 @@ public class DynamicDockerAllocationTest { public void cd_uses_slow_disk_nodes_for_docker_hosts() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(SystemName.cd, Environment.test, RegionName.from("us-east"))).flavorsConfig(flavorsConfig()).build(); tester.makeReadyNodes(4, new Flavor(new NodeResources(1, 8, 120, 1, NodeResources.DiskSpeed.slow)), NodeType.host, 10, true); - tester.deployZoneApp(); + tester.activateTenantHosts(); ApplicationId application1 = tester.makeApplicationId(); List hosts = tester.prepare(application1, clusterSpec("myContent.t1.a1"), 3, 1, new NodeResources(1, 4, 100, 1)); tester.activate(application1, ImmutableSet.copyOf(hosts)); @@ -311,7 +311,7 @@ public class DynamicDockerAllocationTest { public void provision_dual_stack_containers() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).flavorsConfig(flavorsConfig()).build(); tester.makeReadyNodes(2, "host-large", NodeType.host, 10, true); - tester.deployZoneApp(); + tester.activateTenantHosts(); ApplicationId application = tester.makeApplicationId(); List hosts = tester.prepare(application, clusterSpec("myContent.t1.a1"), 2, 1, new NodeResources(1, 4, 100, 1)); @@ -342,7 +342,7 @@ public class DynamicDockerAllocationTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).flavorsConfig(flavorsConfig()).build(); tester.makeReadyNodes(2, new Flavor(new NodeResources(1, 8, 120, 1, NodeResources.DiskSpeed.fast)), NodeType.host, 10, true); tester.makeReadyNodes(2, new Flavor(new NodeResources(1, 8, 120, 1, NodeResources.DiskSpeed.slow)), NodeType.host, 10, true); - tester.deployZoneApp(); + tester.activateTenantHosts(); ApplicationId application = tester.makeApplicationId(); ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test")).vespaVersion("1").build(); @@ -359,7 +359,7 @@ public class DynamicDockerAllocationTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).flavorsConfig(flavorsConfig()).build(); tester.makeReadyNodes(2, new Flavor(new NodeResources(1, 8, 120, 1, NodeResources.DiskSpeed.fast)), NodeType.host, 10, true); tester.makeReadyNodes(2, new Flavor(new NodeResources(1, 8, 120, 1, NodeResources.DiskSpeed.slow)), NodeType.host, 10, true); - tester.deployZoneApp(); + tester.activateTenantHosts(); ApplicationId application = tester.makeApplicationId(); ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test")).vespaVersion("1").build(); @@ -381,7 +381,7 @@ public class DynamicDockerAllocationTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.dev, RegionName.from("us-east"))).flavorsConfig(flavorsConfig()).build(); tester.makeReadyNodes(2, new Flavor(new NodeResources(1, 8, 120, 1, NodeResources.DiskSpeed.fast)), NodeType.host, 10, true); tester.makeReadyNodes(2, new Flavor(new NodeResources(1, 8, 120, 1, NodeResources.DiskSpeed.slow)), NodeType.host, 10, true); - tester.deployZoneApp(); + tester.activateTenantHosts(); ApplicationId application = tester.makeApplicationId(); ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test")).vespaVersion("1").build(); @@ -399,7 +399,7 @@ public class DynamicDockerAllocationTest { public void testSwitchingFromLegacyFlavorSyntaxToResourcesDoesNotCauseReallocation() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).flavorsConfig(flavorsConfig()).build(); tester.makeReadyNodes(2, new Flavor(new NodeResources(5, 20, 1400, 3)), NodeType.host, 10, true); - tester.deployZoneApp(); + tester.activateTenantHosts(); ApplicationId application = tester.makeApplicationId(); ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test")).vespaVersion("1").build(); 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 db6d75d724e..411283abf33 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 @@ -81,7 +81,7 @@ public class DynamicDockerProvisionTest { @Test public void does_not_allocate_to_available_empty_hosts() { tester.makeReadyNodes(3, "small", NodeType.host, 10); - tester.deployZoneApp(); + tester.activateTenantHosts(); ApplicationId application = tester.makeApplicationId(); NodeResources flavor = new NodeResources(1, 4, 10, 1); @@ -109,7 +109,7 @@ public class DynamicDockerProvisionTest { tester.nodeRepository().setReady(List.of(host), Agent.system, getClass().getSimpleName()); nameResolver.addRecord(hostname + "-2", "::" + i + ":2"); } - tester.deployZoneApp(); + tester.activateTenantHosts(); mockHostProvisioner(hostProvisioner, tester.nodeRepository().flavors().getFlavorOrThrow("small")); tester.prepare(application, clusterSpec("another-id"), 2, 1, flavor); @@ -136,7 +136,7 @@ public class DynamicDockerProvisionTest { // Allocate 10 hosts tester.makeReadyNodes(10, resources, NodeType.host, 1); - tester.deployZoneApp(); + tester.activateTenantHosts(); // Prepare & activate an application with 8 nodes and 2 groups tester.activate(app, tester.prepare(app, clusterSpec("content"), 8, 2, resources)); @@ -170,7 +170,7 @@ public class DynamicDockerProvisionTest { .resourcesCalculator(memoryTax, 0) .build(); - tester.deployZoneApp(); + tester.activateTenantHosts(); ApplicationId app1 = tester.makeApplicationId("app1"); ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build(); @@ -216,7 +216,7 @@ public class DynamicDockerProvisionTest { .resourcesCalculator(memoryTax, 0) .build(); - tester.deployZoneApp(); + tester.activateTenantHosts(); ApplicationId app1 = tester.makeApplicationId("app1"); ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build(); @@ -291,7 +291,7 @@ public class DynamicDockerProvisionTest { .resourcesCalculator(memoryTax, 0) .build(); - tester.deployZoneApp(); + tester.activateTenantHosts(); ApplicationId app1 = tester.makeApplicationId("app1"); ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build(); @@ -326,7 +326,7 @@ public class DynamicDockerProvisionTest { .resourcesCalculator(memoryTax, localDiskTax) .build(); - tester.deployZoneApp(); + tester.activateTenantHosts(); ApplicationId app1 = tester.makeApplicationId("app1"); ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build(); 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 6e609d13d3b..09d1600e1d7 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 @@ -134,7 +134,7 @@ public class MultigroupProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))) .flavors(List.of(hostFlavor)) .build(); - tester.makeReadyHosts(6, hostFlavor.resources()).deployZoneApp(); + tester.makeReadyHosts(6, hostFlavor.resources()).activateTenantHosts(); ApplicationId app1 = tester.makeApplicationId("app1"); ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build(); @@ -160,7 +160,7 @@ public class MultigroupProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))) .flavors(List.of(hostFlavor)) .build(); - tester.makeReadyHosts(6, hostFlavor.resources()).deployZoneApp(); + tester.makeReadyHosts(6, hostFlavor.resources()).activateTenantHosts(); ApplicationId app1 = tester.makeApplicationId("app1"); ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build(); @@ -190,7 +190,7 @@ public class MultigroupProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))) .flavors(List.of(hostFlavor)) .build(); - tester.makeReadyHosts(12, hostFlavor.resources()).deployZoneApp(); + tester.makeReadyHosts(12, hostFlavor.resources()).activateTenantHosts(); ApplicationId app1 = tester.makeApplicationId("app1"); ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build(); 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 ff2f0ffca96..d53a0732c89 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 @@ -62,7 +62,7 @@ public class ProvisioningTest { ApplicationId application1 = tester.makeApplicationId(); ApplicationId application2 = tester.makeApplicationId(); - tester.makeReadyHosts(21, defaultResources).deployZoneApp(); + tester.makeReadyHosts(21, defaultResources).activateTenantHosts(); // deploy SystemState state1 = prepare(application1, 2, 2, 3, 3, defaultResources, tester); @@ -196,7 +196,7 @@ public class ProvisioningTest { ApplicationId application1 = tester.makeApplicationId(); tester.makeReadyHosts(24, defaultResources); - tester.deployZoneApp(); + tester.activateTenantHosts(); // deploy SystemState state1 = prepare(application1, 2, 2, 3, 3, defaultResources, tester); @@ -267,7 +267,7 @@ public class ProvisioningTest { ApplicationId application1 = tester.makeApplicationId(); tester.makeReadyHosts(12, small); - tester.deployZoneApp(); + tester.activateTenantHosts(); // deploy SystemState state1 = prepare(application1, 2, 2, 4, 4, small, tester); @@ -278,7 +278,7 @@ public class ProvisioningTest { tester.activate(application1, state2.allHosts); tester.makeReadyHosts(16, large); - tester.deployZoneApp(); + tester.activateTenantHosts(); // redeploy with increased sizes and new flavor SystemState state3 = prepare(application1, 3, 4, 4, 5, large, tester); @@ -303,7 +303,7 @@ public class ProvisioningTest { tester.makeReadyHosts(12, small); tester.makeReadyHosts(12, large); - tester.deployZoneApp(); + tester.activateTenantHosts(); // deploy SystemState state1 = prepare(application1, 2, 2, 4, 4, small, tester); @@ -319,7 +319,7 @@ public class ProvisioningTest { ApplicationId application1 = tester.makeApplicationId(); - tester.makeReadyHosts(5, defaultResources).deployZoneApp(); + tester.makeReadyHosts(5, defaultResources).activateTenantHosts(); // deploy SystemState state1 = prepare(application1, 2, 0, 3, 0, defaultResources, tester); @@ -354,7 +354,7 @@ public class ProvisioningTest { @Test public void requested_resources_info_is_retained() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); - tester.makeReadyHosts(13, defaultResources).deployZoneApp(); + tester.makeReadyHosts(13, defaultResources).activateTenantHosts(); tester.prepareAndActivateInfraApplication(tester.makeApplicationId(), NodeType.host); ApplicationId application = tester.makeApplicationId(); @@ -398,7 +398,7 @@ public class ProvisioningTest { public void deploy_specific_vespa_version() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.dev, RegionName.from("us-east"))).build(); - tester.makeReadyHosts(4, defaultResources).deployZoneApp(); + tester.makeReadyHosts(4, defaultResources).activateTenantHosts(); tester.prepareAndActivateInfraApplication(tester.makeApplicationId(), NodeType.host); ApplicationId application = tester.makeApplicationId(); @@ -411,7 +411,7 @@ public class ProvisioningTest { public void deploy_specific_vespa_version_and_docker_image() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.dev, RegionName.from("us-east"))).build(); - tester.makeReadyHosts(4, defaultResources).deployZoneApp(); + tester.makeReadyHosts(4, defaultResources).activateTenantHosts(); tester.prepareAndActivateInfraApplication(tester.makeApplicationId(), NodeType.host); ApplicationId application = tester.makeApplicationId(); @@ -426,7 +426,7 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.test, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - tester.makeReadyHosts(4, defaultResources).deployZoneApp(); + tester.makeReadyHosts(4, defaultResources).activateTenantHosts(); SystemState state = prepare(application, 2, 2, 3, 3, defaultResources, tester); assertEquals(4, state.allHosts.size()); tester.activate(application, state.allHosts); @@ -438,7 +438,7 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))) .flavors(List.of(hostFlavor)) .build(); - tester.makeReadyHosts(4, hostFlavor.resources()).deployZoneApp(); + tester.makeReadyHosts(4, hostFlavor.resources()).activateTenantHosts(); ApplicationId app1 = tester.makeApplicationId("app1"); ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build(); @@ -456,7 +456,7 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))) .flavors(List.of(hostFlavor)) .build(); - tester.makeReadyHosts(31, hostFlavor.resources()).deployZoneApp(); + tester.makeReadyHosts(31, hostFlavor.resources()).activateTenantHosts(); ApplicationId app1 = tester.makeApplicationId("app1"); ClusterSpec cluster1 = ClusterSpec.request(ClusterSpec.Type.content, new ClusterSpec.Id("cluster1")).vespaVersion("7").build(); @@ -516,7 +516,7 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - tester.makeReadyHosts(10, defaultResources).deployZoneApp(); + tester.makeReadyHosts(10, defaultResources).activateTenantHosts(); prepare(application, 1, 2, 3, 3, defaultResources, tester); } @@ -525,7 +525,7 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - tester.makeReadyHosts(10, defaultResources).deployZoneApp(); + tester.makeReadyHosts(10, defaultResources).activateTenantHosts(); try { prepare(application, 2, 2, 3, 3, new NodeResources(2, 2, 10, 2), tester); @@ -540,7 +540,7 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - tester.makeReadyHosts(10, defaultResources).deployZoneApp(); + tester.makeReadyHosts(10, defaultResources).activateTenantHosts(); try { prepare(application, 2, 2, 3, 3, new NodeResources(0.4, 4, 10, 2), tester); @@ -573,7 +573,7 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.test, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - tester.makeReadyHosts(4, large).deployZoneApp(); + tester.makeReadyHosts(4, large).activateTenantHosts(); SystemState state = prepare(application, 2, 2, 3, 3, large, tester); assertEquals(4, state.allHosts.size()); tester.activate(application, state.allHosts); @@ -584,7 +584,7 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.staging, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - tester.makeReadyHosts(14, defaultResources).deployZoneApp(); + tester.makeReadyHosts(14, defaultResources).activateTenantHosts(); SystemState state = prepare(application, 1, 1, 1, 64, defaultResources, tester); // becomes 1, 1, 1, 1, 6 assertEquals(9, state.allHosts.size()); tester.activate(application, state.allHosts); @@ -594,7 +594,7 @@ public class ProvisioningTest { public void activate_after_reservation_timeout() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); - tester.makeReadyHosts(10, defaultResources).deployZoneApp(); + tester.makeReadyHosts(10, defaultResources).activateTenantHosts(); ApplicationId application = tester.makeApplicationId(); SystemState state = prepare(application, 2, 2, 3, 3, defaultResources, tester); @@ -616,7 +616,7 @@ public class ProvisioningTest { public void out_of_capacity() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); - tester.makeReadyHosts(9, defaultResources).deployZoneApp(); // need 2+2+3+3=10 + tester.makeReadyHosts(9, defaultResources).activateTenantHosts(); // need 2+2+3+3=10 ApplicationId application = tester.makeApplicationId(); try { prepare(application, 2, 2, 3, 3, defaultResources, tester); @@ -633,7 +633,7 @@ public class ProvisioningTest { Environment.prod, RegionName.from("us-east"))).build(); - tester.makeReadyHosts(13, defaultResources).deployZoneApp(); + tester.makeReadyHosts(13, defaultResources).activateTenantHosts(); ApplicationId application = tester.makeApplicationId(); try { prepare(application, 2, 2, 6, 3, defaultResources, tester); @@ -651,7 +651,7 @@ public class ProvisioningTest { Environment.prod, RegionName.from("us-east"))).build(); - tester.makeReadyHosts(13, defaultResources).deployZoneApp(); + tester.makeReadyHosts(13, defaultResources).activateTenantHosts(); ApplicationId application = tester.makeApplicationId(); prepare(application, 2, 2, 6, 3, defaultResources, tester); } @@ -659,7 +659,7 @@ public class ProvisioningTest { @Test public void out_of_capacity_but_cannot_fail() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); - tester.makeReadyHosts(4, defaultResources).deployZoneApp(); + tester.makeReadyHosts(4, defaultResources).activateTenantHosts(); ApplicationId application = tester.makeApplicationId(); ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("music")).vespaVersion("4.5.6").build(); tester.prepare(application, cluster, Capacity.from(new ClusterResources(5, 1, NodeResources.unspecified()), false, false)); @@ -693,7 +693,7 @@ public class ProvisioningTest { ApplicationId application = tester.makeApplicationId(); // Create 10 nodes - tester.makeReadyHosts(10, defaultResources).deployZoneApp(); + tester.makeReadyHosts(10, defaultResources).activateTenantHosts(); // Allocate 5 nodes ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("music")).vespaVersion("4.5.6").build(); tester.activate(application, tester.prepare(application, cluster, capacity)); @@ -724,7 +724,7 @@ public class ProvisioningTest { ApplicationId application1 = tester.makeApplicationId(); - tester.makeReadyHosts(14, defaultResources).deployZoneApp(); + tester.makeReadyHosts(14, defaultResources).activateTenantHosts(); // deploy SystemState state1 = prepare(application1, 3, 3, 4, 4, defaultResources, tester); @@ -751,7 +751,7 @@ public class ProvisioningTest { public void node_on_spare_host_retired_first() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))) .spareCount(1).build(); - tester.makeReadyHosts(7, defaultResources).deployZoneApp(); + tester.makeReadyHosts(7, defaultResources).activateTenantHosts(); ApplicationId application = tester.makeApplicationId(); ClusterSpec spec = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("content1")).vespaVersion("7.1.2").build(); @@ -774,7 +774,7 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - tester.makeReadyHosts(10, defaultResources).deployZoneApp(); + tester.makeReadyHosts(10, defaultResources).activateTenantHosts(); // Deploy application { @@ -802,7 +802,7 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - tester.makeReadyHosts(2, defaultResources).deployZoneApp(); + tester.makeReadyHosts(2, defaultResources).activateTenantHosts(); // Deploy fails with out of capacity try { @@ -813,7 +813,7 @@ public class ProvisioningTest { tester.getNodes(application, Node.State.reserved).size()); // Enough nodes become available - tester.makeReadyHosts(2, defaultResources).deployZoneApp(); + tester.makeReadyHosts(2, defaultResources).activateTenantHosts(); // Deploy is retried after a few minutes tester.clock().advance(Duration.ofMinutes(2)); @@ -883,7 +883,7 @@ public class ProvisioningTest { var tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); var application = tester.makeApplicationId(); - tester.makeReadyHosts(4, defaultResources).deployZoneApp(); + tester.makeReadyHosts(4, defaultResources).activateTenantHosts(); // Application allocates two content nodes initially, with cluster type content ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("music")).vespaVersion("1.2.3").build(); 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 d56cae799b2..0a3c85d3702 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 @@ -320,7 +320,7 @@ public class ProvisioningTester { return makeReadyNodes(n, flavor, NodeType.tenant); } - /** Call deployZoneApp() after this before deploying applications */ + /** Call {@link this#activateTenantHosts()} after this before deploying applications */ public ProvisioningTester makeReadyHosts(int n, NodeResources resources) { makeReadyNodes(n, resources, NodeType.host, 5); return this; @@ -494,7 +494,7 @@ public class ProvisioningTester { return nodes; } - public void deployZoneApp() { + public void activateTenantHosts() { ApplicationId applicationId = makeApplicationId(); List list = prepare(applicationId, ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("node-admin")).vespaVersion("6.42").build(), -- cgit v1.2.3 From 655690784b67f9d7e547d068d3386900df9283ac Mon Sep 17 00:00:00 2001 From: Martin Polden Date: Fri, 18 Sep 2020 11:57:00 +0200 Subject: Add switch hostname to node --- .../com/yahoo/vespa/hosted/provision/Node.java | 62 +++++++++++++++------- .../persistence/CuratorDatabaseClient.java | 3 +- .../provision/persistence/NodeSerializer.java | 10 +++- .../DynamicProvisioningMaintainerTest.java | 3 +- .../provision/persistence/NodeSerializerTest.java | 23 +++++--- .../provisioning/AllocationSimulator.java | 3 +- .../provisioning/DynamicDockerAllocationTest.java | 2 +- .../provision/provisioning/NodeCandidateTest.java | 6 +-- 8 files changed, 77 insertions(+), 35 deletions(-) (limited to 'node-repository') diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java index b08dc6bbaf2..9b86c57df55 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java @@ -43,6 +43,7 @@ public final class Node { private final Reports reports; private final Optional modelName; private final Optional reservedTo; + private final Optional switchHostname; /** Record of the last event of each type happening to this node */ private final History history; @@ -54,20 +55,21 @@ public final class Node { public static Node createDockerNode(Set ipAddresses, String hostname, String parentHostname, NodeResources resources, NodeType type) { return new Node("fake-" + hostname, new IP.Config(ipAddresses, Set.of()), hostname, Optional.of(parentHostname), new Flavor(resources), Status.initial(), State.reserved, - Optional.empty(), History.empty(), type, new Reports(), Optional.empty(), Optional.empty()); + Optional.empty(), History.empty(), type, new Reports(), Optional.empty(), Optional.empty(), + Optional.empty()); } /** Creates a node in the initial state (provisioned) */ public static Node create(String openStackId, IP.Config ipConfig, String hostname, Optional parentHostname, Optional modelName, Flavor flavor, Optional reservedTo, NodeType type) { return new Node(openStackId, ipConfig, hostname, parentHostname, flavor, Status.initial(), State.provisioned, - Optional.empty(), History.empty(), type, new Reports(), modelName, reservedTo); + Optional.empty(), History.empty(), type, new Reports(), modelName, reservedTo, Optional.empty()); } /** Creates a node. See also the {@code create} helper methods. */ public Node(String id, IP.Config ipConfig, String hostname, Optional parentHostname, Flavor flavor, Status status, State state, Optional allocation, History history, NodeType type, - Reports reports, Optional modelName, Optional reservedTo) { + Reports reports, Optional modelName, Optional reservedTo, Optional switchHostname) { this.id = Objects.requireNonNull(id, "A node must have an ID"); this.hostname = requireNonEmptyString(hostname, "A node must have a hostname"); this.ipConfig = Objects.requireNonNull(ipConfig, "A node must a have an IP config"); @@ -81,6 +83,7 @@ public final class Node { this.reports = Objects.requireNonNull(reports, "A null reports is not permitted"); this.modelName = Objects.requireNonNull(modelName, "A null modelName is not permitted"); this.reservedTo = Objects.requireNonNull(reservedTo, "reservedTo cannot be null"); + this.switchHostname = requireNonEmptyString(switchHostname, "switchHostname cannot be null"); if (state == State.active) requireNonEmpty(ipConfig.primary(), "Active node " + hostname + " must have at least one valid IP address"); @@ -88,6 +91,7 @@ public final class Node { if (parentHostname.isPresent()) { if (!ipConfig.pool().asSet().isEmpty()) throw new IllegalArgumentException("A child node cannot have an IP address pool"); if (modelName.isPresent()) throw new IllegalArgumentException("A child node cannot have model name set"); + if (switchHostname.isPresent()) throw new IllegalArgumentException("A child node cannot have switch hostname set"); } if (type != NodeType.host && reservedTo.isPresent()) @@ -151,7 +155,7 @@ public final class Node { /** Returns all the reports on this node. */ public Reports reports() { return reports; } - /** Returns the hardware model of this node */ + /** Returns the hardware model of this node, if any */ public Optional modelName() { return modelName; } /** @@ -160,6 +164,11 @@ public final class Node { */ public Optional reservedTo() { return reservedTo; } + /** Returns the hostname of the switch this node is connected to, if any */ + public Optional switchHostname() { + return switchHostname; + } + /** * Returns a copy of this node with wantToRetire and wantToDeprovision set to the given values and updated history. * @@ -217,42 +226,44 @@ public final class Node { /** Returns a node with the status assigned to the given value */ public Node with(Status status) { - return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history, type, reports, modelName, reservedTo); + return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history, type, + reports, modelName, reservedTo, switchHostname); } /** Returns a node with the type assigned to the given value */ public Node with(NodeType type) { - return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history, type, reports, modelName, reservedTo); + return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history, type, + reports, modelName, reservedTo, switchHostname); } /** Returns a node with the flavor assigned to the given value */ public Node with(Flavor flavor) { - return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, - allocation, history, type, reports, modelName, reservedTo); + return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history, type, + reports, modelName, reservedTo, switchHostname); } /** Returns a copy of this with the reboot generation set to generation */ public Node withReboot(Generation generation) { return new Node(id, ipConfig, hostname, parentHostname, flavor, status.withReboot(generation), state, - allocation, history, type, reports, modelName, reservedTo); + allocation, history, type, reports, modelName, reservedTo, switchHostname); } /** Returns a copy of this with the openStackId set */ public Node withOpenStackId(String openStackId) { return new Node(openStackId, ipConfig, hostname, parentHostname, flavor, status, state, - allocation, history, type, reports, modelName, reservedTo); + allocation, history, type, reports, modelName, reservedTo, switchHostname); } /** Returns a copy of this with model name set to given value */ public Node withModelName(String modelName) { return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, - allocation, history, type, reports, Optional.of(modelName), reservedTo); + allocation, history, type, reports, Optional.of(modelName), reservedTo, switchHostname); } /** Returns a copy of this with model name cleared */ public Node withoutModelName() { return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, - allocation, history, type, reports, Optional.empty(), reservedTo); + allocation, history, type, reports, Optional.empty(), reservedTo, switchHostname); } /** Returns a copy of this with a history record saying it was detected to be down at this instant */ @@ -278,39 +289,50 @@ public final class Node { */ public Node with(Allocation allocation) { return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, - Optional.of(allocation), history, type, reports, modelName, reservedTo); + Optional.of(allocation), history, type, reports, modelName, reservedTo, switchHostname); } /** Returns a new Node without an allocation. */ public Node withoutAllocation() { return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, - Optional.empty(), history, type, reports, modelName, reservedTo); + Optional.empty(), history, type, reports, modelName, reservedTo, switchHostname); } /** Returns a copy of this node with IP config set to the given value. */ public Node with(IP.Config ipConfig) { return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, - allocation, history, type, reports, modelName, reservedTo); + allocation, history, type, reports, modelName, reservedTo, switchHostname); } /** Returns a copy of this node with the parent hostname assigned to the given value. */ public Node withParentHostname(String parentHostname) { return new Node(id, ipConfig, hostname, Optional.of(parentHostname), flavor, status, state, - allocation, history, type, reports, modelName, reservedTo); + allocation, history, type, reports, modelName, reservedTo, switchHostname); } public Node withReservedTo(TenantName tenant) { if (type != NodeType.host) throw new IllegalArgumentException("Only host nodes can be reserved, " + hostname + " has type " + type); return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, - allocation, history, type, reports, modelName, Optional.of(tenant)); + allocation, history, type, reports, modelName, Optional.of(tenant), switchHostname); } /** Returns a copy of this node which is not reserved to a tenant */ public Node withoutReservedTo() { return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, - allocation, history, type, reports, modelName, Optional.empty()); + allocation, history, type, reports, modelName, Optional.empty(), switchHostname); + } + + /** Returns a copy of this node with switch hostname set to given value */ + public Node withSwitchHostname(String switchHostname) { + return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, + allocation, history, type, reports, modelName, reservedTo, Optional.ofNullable(switchHostname)); + } + + /** Returns a copy of this node with switch hostname unset */ + public Node withoutSwitchHostname() { + return withSwitchHostname(null); } /** Returns a copy of this node with the current reboot generation set to the given number at the given instant */ @@ -343,12 +365,12 @@ public final class Node { /** Returns a copy of this node with the given history. */ public Node with(History history) { return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, - allocation, history, type, reports, modelName, reservedTo); + allocation, history, type, reports, modelName, reservedTo, switchHostname); } public Node with(Reports reports) { return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, - allocation, history, type, reports, modelName, reservedTo); + allocation, history, type, reports, modelName, reservedTo, switchHostname); } private static Optional requireNonEmptyString(Optional value, String message) { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java index cc62ae67e84..504854108b6 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java @@ -216,7 +216,8 @@ public class CuratorDatabaseClient { toState, toState.isAllocated() ? node.allocation() : Optional.empty(), node.history().recordStateTransition(node.state(), toState, agent, clock.instant()), - node.type(), node.reports(), node.modelName(), node.reservedTo()); + node.type(), node.reports(), node.modelName(), node.reservedTo(), + node.switchHostname()); writeNode(toState, curatorTransaction, node, newNode); writtenNodes.add(newNode); } 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 5a3584b6ff4..0ce81e49bda 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 @@ -79,6 +79,7 @@ public class NodeSerializer { private static final String reportsKey = "reports"; private static final String modelNameKey = "modelName"; private static final String reservedToKey = "reservedTo"; + private static final String switchHostnameKey = "switchHostname"; // Node resource fields private static final String flavorKey = "flavor"; @@ -143,6 +144,7 @@ public class NodeSerializer { node.status().osVersion().current().ifPresent(version -> object.setString(osVersionKey, version.toString())); node.status().osVersion().wanted().ifPresent(version -> object.setString(wantedOsVersionKey, version.toFullString())); node.status().firmwareVerifiedAt().ifPresent(instant -> object.setLong(firmwareCheckKey, instant.toEpochMilli())); + node.switchHostname().ifPresent(switchHostname -> object.setString(switchHostnameKey, switchHostname)); node.reports().toSlime(object, reportsKey); node.modelName().ifPresent(modelName -> object.setString(modelNameKey, modelName)); node.reservedTo().ifPresent(tenant -> object.setString(reservedToKey, tenant.value())); @@ -212,7 +214,8 @@ public class NodeSerializer { nodeTypeFromString(object.field(nodeTypeKey).asString()), Reports.fromSlime(object.field(reportsKey)), modelNameFromSlime(object), - reservedToFromSlime(object.field(reservedToKey))); + reservedToFromSlime(object.field(reservedToKey)), + switchHostnameFromSlime(object.field(switchHostnameKey))); } private Status statusFromSlime(Inspector object) { @@ -227,6 +230,11 @@ public class NodeSerializer { instantFromSlime(object.field(firmwareCheckKey))); } + private Optional switchHostnameFromSlime(Inspector field) { + if (!field.valid()) return Optional.empty(); + return Optional.of(field.asString()); + } + private Flavor flavorFromSlime(Inspector object) { Inspector resources = object.field(resourcesKey); 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 12cf114b2d2..9978f37e835 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 @@ -273,7 +273,8 @@ public class DynamicProvisioningMaintainerTest { false)); var ipConfig = new IP.Config(state == Node.State.active ? Set.of("::1") : Set.of(), Set.of()); return new Node("fake-id-" + hostname, ipConfig, hostname, parentHostname, flavor, Status.initial(), - state, allocation, History.empty(), nodeType, new Reports(), Optional.empty(), Optional.empty()); + state, allocation, History.empty(), nodeType, new Reports(), Optional.empty(), Optional.empty(), + Optional.empty()); } private long provisionedHostsMatching(NodeResources resources) { diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializerTest.java index dbbad0b8982..e9910157592 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializerTest.java @@ -57,7 +57,7 @@ public class NodeSerializerTest { private final ManualClock clock = new ManualClock(); @Test - public void testProvisionedNodeSerialization() { + public void provisioned_node_serialization() { Node node = createNode(); Node copy = nodeSerializer.fromJson(Node.State.provisioned, nodeSerializer.toJson(node)); @@ -69,7 +69,7 @@ public class NodeSerializerTest { } @Test - public void testReservedNodeSerialization() { + public void reserved_node_serialization() { Node node = createNode(); NodeResources requestedResources = new NodeResources(1.2, 3.4, 5.6, 7.8, NodeResources.DiskSpeed.any); @@ -110,7 +110,7 @@ public class NodeSerializerTest { } @Test - public void testRebootAndRestartAndTypeNoCurrentValuesSerialization() { + public void reboot_and_restart_and_type_no_current_values_serialization() { String nodeData = "{\n" + " \"type\" : \"tenant\",\n" + @@ -158,7 +158,7 @@ public class NodeSerializerTest { } @Test - public void testRetiredNodeSerialization() { + public void retired_node_serialization() { Node node = createNode(); clock.advance(Duration.ofMinutes(3)); @@ -185,7 +185,7 @@ public class NodeSerializerTest { } @Test - public void testAssimilatedDeserialization() { + public void assimilated_node_deserialization() { Node node = nodeSerializer.fromJson(Node.State.active, ("{\n" + " \"type\": \"tenant\",\n" + " \"hostname\": \"assimilate2.vespahosted.yahoo.tld\",\n" + @@ -211,7 +211,7 @@ public class NodeSerializerTest { } @Test - public void testSetFailCount() { + public void fail_count() { Node node = createNode(); node = node.allocate(ApplicationId.from(TenantName.from("myTenant"), ApplicationName.from("myApplication"), @@ -392,7 +392,7 @@ public class NodeSerializerTest { } @Test - public void testNodeWithNetworkPorts() { + public void network_ports_serialization() { Node node = createNode(); List list = new ArrayList<>(); list.add(new NetworkPorts.Allocation(8080, "container", "default/0", "http")); @@ -416,6 +416,15 @@ public class NodeSerializerTest { assertEquals(list, listCopy); } + @Test + public void switch_hostname_serialization() { + Node node = nodeSerializer.fromJson(State.active, nodeSerializer.toJson(createNode())); + assertFalse(node.switchHostname().isPresent()); + String switchHostname = "switch0.example.com"; + node = node.withSwitchHostname(switchHostname); + node = nodeSerializer.fromJson(State.active, nodeSerializer.toJson(node)); + assertEquals(switchHostname, node.switchHostname().get()); + } private byte[] createNodeJson(String hostname, String... ipAddress) { String ipAddressJsonPart = ""; 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 b113c281289..051420d694d 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 @@ -80,7 +80,8 @@ public class AllocationSimulator { var ipConfig = new IP.Config(Set.of("127.0.0.1"), parent.isPresent() ? Set.of() : getAdditionalIP()); return new Node("fake", ipConfig, hostname, parent, flavor, Status.initial(), parent.isPresent() ? Node.State.ready : Node.State.active, allocation(tenant, flavor), History.empty(), - parent.isPresent() ? NodeType.tenant : NodeType.host, new Reports(), Optional.empty(), Optional.empty()); + parent.isPresent() ? NodeType.tenant : NodeType.host, new Reports(), Optional.empty(), Optional.empty(), + Optional.empty()); } private Set getAdditionalIP() { diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java index ab62972df30..7e416bfc397 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerAllocationTest.java @@ -425,7 +425,7 @@ public class DynamicDockerAllocationTest { private void addAndAssignNode(ApplicationId id, String hostname, String parentHostname, ClusterSpec clusterSpec, NodeResources flavor, int index, ProvisioningTester tester) { Node node1a = Node.create("open1", new IP.Config(Set.of("127.0.233." + index), Set.of()), hostname, - Optional.of(parentHostname), Optional.empty(), new Flavor(flavor), Optional.empty(), NodeType.tenant + Optional.of(parentHostname), Optional.empty(), new Flavor(flavor), Optional.empty(), NodeType.tenant, Optional.empty() ); ClusterMembership clusterMembership1 = ClusterMembership.from( clusterSpec.with(Optional.of(ClusterSpec.Group.from(0))), index); // Need to add group here so that group is serialized in node allocation diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidateTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidateTest.java index 95b9f334bb4..ec001556f58 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidateTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidateTest.java @@ -127,7 +127,7 @@ public class NodeCandidateTest { return new Node(hostname, new IP.Config(Set.of("::1"), Set.of()), hostname, Optional.empty(), new Flavor(new NodeResources(2, 2, 2, 2)), Status.initial(), state, Optional.empty(), History.empty(), NodeType.tenant, new Reports(), - Optional.empty(), Optional.empty()); + Optional.empty(), Optional.empty(), Optional.empty()); } private static NodeCandidate node(String hostname, @@ -137,11 +137,11 @@ public class NodeCandidateTest { Node node = new Node(hostname, new IP.Config(Set.of("::1"), Set.of()), hostname, Optional.of(hostname + "parent"), new Flavor(nodeResources), Status.initial(), Node.State.ready, Optional.empty(), History.empty(), NodeType.tenant, - new Reports(), Optional.empty(), Optional.empty()); + new Reports(), Optional.empty(), Optional.empty(), Optional.empty()); Node parent = new Node(hostname + "parent", new IP.Config(Set.of("::1"), Set.of()), hostname, Optional.empty(), new Flavor(totalHostResources), Status.initial(), Node.State.ready, Optional.empty(), History.empty(), NodeType.host, - new Reports(), Optional.empty(), Optional.empty()); + new Reports(), Optional.empty(), Optional.empty(), Optional.empty()); return new NodeCandidate(node, totalHostResources.subtract(allocatedHostResources), Optional.of(parent), false, false, true, false); } -- cgit v1.2.3 From 48b32b454f9c72029dee804ce76aa4ff66ef09a4 Mon Sep 17 00:00:00 2001 From: Martin Polden Date: Fri, 18 Sep 2020 12:16:46 +0200 Subject: Add switch hostname to REST API --- .../IdentityDocumentGeneratorTest.java | 2 +- .../InstanceValidatorTest.java | 2 +- .../com/yahoo/vespa/hosted/provision/Node.java | 4 +-- .../vespa/hosted/provision/NodeRepository.java | 2 +- .../provision/provisioning/ProvisionedHost.java | 2 +- .../hosted/provision/restapi/NodePatcher.java | 2 ++ .../hosted/provision/restapi/NodesResponse.java | 1 + .../provision/restapi/NodesV2ApiHandler.java | 9 ++++-- .../provision/maintenance/MetricsReporterTest.java | 2 +- .../yahoo/vespa/hosted/provision/node/IPTest.java | 2 +- .../provision/persistence/NodeSerializerTest.java | 6 ++-- .../provision/provisioning/HostCapacityTest.java | 8 ++--- .../hosted/provision/restapi/NodesV2ApiTest.java | 37 ++++++++++++++++++---- 13 files changed, 56 insertions(+), 23 deletions(-) (limited to 'node-repository') diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java index 8e5622e6c2f..f92f02f7908 100644 --- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java +++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java @@ -63,7 +63,7 @@ public class IdentityDocumentGeneratorTest { Optional.empty(), new MockNodeFlavors().getFlavorOrThrow("default"), Optional.empty(), - NodeType.host); + NodeType.host, Optional.empty()); Node containerNode = Node.createDockerNode(Set.of("::1"), containerHostname, parentHostname, diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java index ab81cb8eda5..5e965761874 100644 --- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java +++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java @@ -231,7 +231,7 @@ public class InstanceValidatorTest { Optional.empty(), flavors.getFlavorOrThrow("default"), Optional.empty(), - NodeType.tenant); + NodeType.tenant, Optional.empty()); nodeList.add(node); } return nodeList; diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java index 9b86c57df55..3b3711d1081 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java @@ -61,9 +61,9 @@ public final class Node { /** Creates a node in the initial state (provisioned) */ public static Node create(String openStackId, IP.Config ipConfig, String hostname, Optional parentHostname, - Optional modelName, Flavor flavor, Optional reservedTo, NodeType type) { + Optional modelName, Flavor flavor, Optional reservedTo, NodeType type, Optional switchHostname) { return new Node(openStackId, ipConfig, hostname, parentHostname, flavor, Status.initial(), State.provisioned, - Optional.empty(), History.empty(), type, new Reports(), modelName, reservedTo, Optional.empty()); + Optional.empty(), History.empty(), type, new Reports(), modelName, reservedTo, switchHostname); } /** Creates a node. See also the {@code create} helper methods. */ diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java index 4ec7ddd04c4..8523db7a970 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeRepository.java @@ -407,7 +407,7 @@ public class NodeRepository extends AbstractComponent { Flavor flavor, Optional reservedTo, NodeType type) { if (ipConfig.primary().isEmpty()) // TODO: Remove this. Only test code hits this path ipConfig = ipConfig.with(nameResolver.getAllByNameOrThrow(hostname)); - return Node.create(openStackId, ipConfig, hostname, parentHostname, Optional.empty(), flavor, reservedTo, type); + return Node.create(openStackId, ipConfig, hostname, parentHostname, Optional.empty(), flavor, reservedTo, type, Optional.empty()); } public Node createNode(String openStackId, String hostname, Optional parentHostname, Flavor flavor, NodeType type) { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisionedHost.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisionedHost.java index 151fcb4233f..298404a4cb4 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisionedHost.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisionedHost.java @@ -38,7 +38,7 @@ public class ProvisionedHost { /** Generate {@link Node} instance representing the provisioned physical host */ public Node generateHost() { - var node = Node.create(id, IP.Config.EMPTY, hostHostname, Optional.empty(), Optional.empty(), hostFlavor, Optional.empty(), NodeType.host); + var node = Node.create(id, IP.Config.EMPTY, hostHostname, Optional.empty(), Optional.empty(), hostFlavor, Optional.empty(), NodeType.host, Optional.empty()); return node.with(node.status().withOsVersion(OsVersion.EMPTY.withCurrent(Optional.of(osVersion)))); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java index 19276a81ef8..834bed00bd0 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java @@ -149,6 +149,8 @@ public class NodePatcher { return patchRequiredDiskSpeed(node, asString(value)); case "reservedTo": return value.type() == Type.NIX ? node.withoutReservedTo() : node.withReservedTo(TenantName.from(value.asString())); + case "switchHostname": + return value.type() == Type.NIX ? node.withoutSwitchHostname() : node.withSwitchHostname(value.asString()); default : throw new IllegalArgumentException("Could not apply field '" + name + "' on a node: No such modifiable field"); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java index 8e3ae6358df..0137bee5fbd 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java @@ -189,6 +189,7 @@ class NodesResponse extends HttpResponse { ipAddressesToSlime(node.ipConfig().pool().asSet(), object.setArray("additionalIpAddresses")); node.reports().toSlime(object, "reports"); node.modelName().ifPresent(modelName -> object.setString("modelName", modelName)); + node.switchHostname().ifPresent(switchHostname -> object.setString("switchHostname", switchHostname)); } private void toSlime(ApplicationId id, Cursor object) { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java index 5080dafe2a5..1891b9d2f82 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java @@ -261,8 +261,13 @@ public class NodesV2ApiHandler extends LoggingRequestHandler { modelName, flavorFromSlime(inspector), reservedToFromSlime(inspector.field("reservedTo")), - nodeTypeFromSlime(inspector.field("type")) - ); + nodeTypeFromSlime(inspector.field("type")), + switchHostnameFromSlime(inspector.field("switchHostname"))); + } + + private Optional switchHostnameFromSlime(Inspector field) { + if (!field.valid()) return Optional.empty(); + return Optional.of(field.asString()); } private Flavor flavorFromSlime(Inspector inspector) { diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporterTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporterTest.java index 20c9d24d1b6..6897a293525 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporterTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporterTest.java @@ -163,7 +163,7 @@ public class MetricsReporterTest { Set ipAddressPool = Set.of("::2", "::3", "::4", "::5"); Node dockerHost = Node.create("openStackId1", new IP.Config(Set.of("::1"), ipAddressPool), "dockerHost", - Optional.empty(), Optional.empty(), nodeFlavors.getFlavorOrThrow("host"), Optional.empty(), NodeType.host); + Optional.empty(), Optional.empty(), nodeFlavors.getFlavorOrThrow("host"), Optional.empty(), NodeType.host, Optional.empty()); nodeRepository.addNodes(List.of(dockerHost), Agent.system); nodeRepository.dirtyRecursively("dockerHost", Agent.system, getClass().getSimpleName()); nodeRepository.setReady("dockerHost", Agent.system, getClass().getSimpleName()); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/node/IPTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/node/IPTest.java index fcf2ab5a52d..4602a8f3560 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/node/IPTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/node/IPTest.java @@ -179,7 +179,7 @@ public class IPTest { private static Node createNode(Set ipAddresses) { return Node.create("id1", new IP.Config(Set.of("127.0.0.1"), ipAddresses), "host1", Optional.empty(), Optional.empty(), nodeFlavors.getFlavorOrThrow("default"), - Optional.empty(), NodeType.host); + Optional.empty(), NodeType.host, Optional.empty()); } } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializerTest.java index e9910157592..97c9bef0dd0 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializerTest.java @@ -229,7 +229,7 @@ public class NodeSerializerTest { @Test public void serialize_parentHostname() { final String parentHostname = "parent.yahoo.com"; - Node node = Node.create("myId", new IP.Config(Set.of("127.0.0.1"), Set.of()), "myHostname", Optional.of(parentHostname), Optional.empty(), nodeFlavors.getFlavorOrThrow("default"), Optional.empty(), NodeType.tenant); + Node node = Node.create("myId", new IP.Config(Set.of("127.0.0.1"), Set.of()), "myHostname", Optional.of(parentHostname), Optional.empty(), nodeFlavors.getFlavorOrThrow("default"), Optional.empty(), NodeType.tenant, Optional.empty()); Node deserializedNode = nodeSerializer.fromJson(State.provisioned, nodeSerializer.toJson(node)); assertEquals(parentHostname, deserializedNode.parentHostname().get()); @@ -450,8 +450,8 @@ public class NodeSerializerTest { Optional.empty(), Optional.empty(), nodeFlavors.getFlavorOrThrow("default"), - Optional.empty(), NodeType.tenant - ); + Optional.empty(), NodeType.tenant, + Optional.empty()); } } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacityTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacityTest.java index da78aff493e..f831f2d501b 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacityTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacityTest.java @@ -44,9 +44,9 @@ public class HostCapacityTest { NodeFlavors nodeFlavors = FlavorConfigBuilder.createDummies("host", "docker", "docker2"); // Create three docker hosts - host1 = Node.create("host1", new IP.Config(Set.of("::1"), generateIPs(2, 4)), "host1", Optional.empty(), Optional.empty(), nodeFlavors.getFlavorOrThrow("host"), Optional.empty(), NodeType.host); - host2 = Node.create("host2", new IP.Config(Set.of("::11"), generateIPs(12, 3)), "host2", Optional.empty(), Optional.empty(), nodeFlavors.getFlavorOrThrow("host"), Optional.empty(), NodeType.host); - host3 = Node.create("host3", new IP.Config(Set.of("::21"), generateIPs(22, 1)), "host3", Optional.empty(), Optional.empty(), nodeFlavors.getFlavorOrThrow("host"), Optional.empty(), NodeType.host); + host1 = Node.create("host1", new IP.Config(Set.of("::1"), generateIPs(2, 4)), "host1", Optional.empty(), Optional.empty(), nodeFlavors.getFlavorOrThrow("host"), Optional.empty(), NodeType.host, Optional.empty()); + host2 = Node.create("host2", new IP.Config(Set.of("::11"), generateIPs(12, 3)), "host2", Optional.empty(), Optional.empty(), nodeFlavors.getFlavorOrThrow("host"), Optional.empty(), NodeType.host, Optional.empty()); + host3 = Node.create("host3", new IP.Config(Set.of("::21"), generateIPs(22, 1)), "host3", Optional.empty(), Optional.empty(), nodeFlavors.getFlavorOrThrow("host"), Optional.empty(), NodeType.host, Optional.empty()); // Add two containers to host1 var nodeA = Node.createDockerNode(Set.of("::2"), "nodeA", "host1", resources1, NodeType.tenant); @@ -111,7 +111,7 @@ public class HostCapacityTest { // Dev host can assign both configserver and tenant containers. var nodeFlavors = FlavorConfigBuilder.createDummies("devhost", "container"); - var devHost = Node.create("devhost", new IP.Config(Set.of("::1"), generateIPs(2, 10)), "devhost", Optional.empty(), Optional.empty(), nodeFlavors.getFlavorOrThrow("devhost"), Optional.empty(), NodeType.devhost); + var devHost = Node.create("devhost", new IP.Config(Set.of("::1"), generateIPs(2, 10)), "devhost", Optional.empty(), Optional.empty(), nodeFlavors.getFlavorOrThrow("devhost"), Optional.empty(), NodeType.devhost, Optional.empty()); var cfg = Node.createDockerNode(Set.of("::2"), "cfg", "devhost", resources1, NodeType.config); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java index 2cd2fe1fc28..1a354686ae4 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiTest.java @@ -333,7 +333,7 @@ public class NodesV2ApiTest { // Node types running a single container can share their IP address with child node tester.assertResponse(new Request("http://localhost:8080/nodes/v2/node", - "[" + asNodeJson("cfghost42.yahoo.com", NodeType.confighost, "default", Optional.empty(), "127.0.42.1") + "]", + "[" + asNodeJson("cfghost42.yahoo.com", NodeType.confighost, "default", Optional.empty(), Optional.empty(), "127.0.42.1") + "]", Request.Method.POST), 200, "{\"message\":\"Added 1 nodes to the provisioned state\"}"); tester.assertResponse(new Request("http://localhost:8080/nodes/v2/node", @@ -349,7 +349,7 @@ public class NodesV2ApiTest { // ... nor with child node on different host tester.assertResponse(new Request("http://localhost:8080/nodes/v2/node", - "[" + asNodeJson("cfghost43.yahoo.com", NodeType.confighost, "default", Optional.empty(), "127.0.43.1") + "]", + "[" + asNodeJson("cfghost43.yahoo.com", NodeType.confighost, "default", Optional.empty(), Optional.empty(), "127.0.43.1") + "]", Request.Method.POST), 200, "{\"message\":\"Added 1 nodes to the provisioned state\"}"); tester.assertResponse(new Request("http://localhost:8080/nodes/v2/node/cfg42.yahoo.com", @@ -939,6 +939,30 @@ public class NodesV2ApiTest { "\"resources\":{\"vcpu\":56.0,\"memoryGb\":34.0,\"diskGb\":12.0,\"bandwidthGbps\":78.0,\"diskSpeed\":\"fast\",\"storageType\":\"remote\"}"); } + @Test + public void test_node_switch_hostname() throws Exception { + String hostname = "host42.yahoo.com"; + // Add host with switch hostname + String json = asNodeJson(hostname, NodeType.host, "default", Optional.empty(), Optional.of("switch0"), "127.0.42.1", "::42:1"); + assertResponse(new Request("http://localhost:8080/nodes/v2/node", + ("[" + json + "]").getBytes(StandardCharsets.UTF_8), + Request.Method.POST), + "{\"message\":\"Added 1 nodes to the provisioned state\"}"); + tester.assertResponseContains(new Request("http://localhost:8080/nodes/v2/node/" + hostname), "\"switchHostname\":\"switch0\""); + + // Update switch hostname + json = "{\"switchHostname\":\"switch1\"}"; + assertResponse(new Request("http://localhost:8080/nodes/v2/node/" + hostname, json.getBytes(StandardCharsets.UTF_8), Request.Method.PATCH), + "{\"message\":\"Updated host42.yahoo.com\"}"); + tester.assertResponseContains(new Request("http://localhost:8080/nodes/v2/node/" + hostname), "\"switchHostname\":\"switch1\""); + + // Clear switch hostname + json = "{\"switchHostname\":null}"; + assertResponse(new Request("http://localhost:8080/nodes/v2/node/" + hostname, json.getBytes(StandardCharsets.UTF_8), Request.Method.PATCH), + "{\"message\":\"Updated host42.yahoo.com\"}"); + tester.assertPartialResponse(new Request("http://localhost:8080/nodes/v2/node/" + hostname), "switchHostname", false); + } + private static String asDockerNodeJson(String hostname, String parentHostname, String... ipAddress) { return asDockerNodeJson(hostname, NodeType.tenant, parentHostname, ipAddress); } @@ -958,14 +982,15 @@ public class NodesV2ApiTest { } private static String asHostJson(String hostname, String flavor, Optional reservedTo, String... ipAddress) { - return asNodeJson(hostname, NodeType.host, flavor, reservedTo, ipAddress); + return asNodeJson(hostname, NodeType.host, flavor, reservedTo, Optional.empty(), ipAddress); } - private static String asNodeJson(String hostname, NodeType nodeType, String flavor, Optional reservedTo, String... ipAddress) { + private static String asNodeJson(String hostname, NodeType nodeType, String flavor, Optional reservedTo, Optional switchHostname, String... ipAddress) { return "{\"hostname\":\"" + hostname + "\", \"openStackId\":\"" + hostname + "\"," + createIpAddresses(ipAddress) + "\"flavor\":\"" + flavor + "\"" + (reservedTo.isPresent() ? ", \"reservedTo\":\"" + reservedTo.get().value() + "\"" : "") + + (switchHostname.isPresent() ? ", \"switchHostname\":\"" + switchHostname.get() + "\"" : "") + ", \"type\":\"" + nodeType + "\"}"; } @@ -989,8 +1014,8 @@ public class NodesV2ApiTest { tester.assertFile(request, file); } - private void assertResponse(Request request, String file) throws IOException { - tester.assertResponse(request, file); + private void assertResponse(Request request, String response) throws IOException { + tester.assertResponse(request, response); } } -- cgit v1.2.3 From 14c1f947b2906e8cfa38d98ec48ea44bf5fe9e22 Mon Sep 17 00:00:00 2001 From: Martin Polden Date: Fri, 18 Sep 2020 13:18:18 +0200 Subject: Unify node patch methods in tests --- .../hosted/provision/autoscale/AutoscalingTester.java | 2 +- .../maintenance/InactiveAndFailedExpirerTest.java | 6 ++---- .../provision/maintenance/OsUpgradeActivatorTest.java | 8 +------- .../vespa/hosted/provision/os/OsVersionsTest.java | 17 ++++------------- .../provisioning/DynamicDockerProvisionTest.java | 3 +-- .../provision/provisioning/ProvisioningTest.java | 6 +++--- .../provision/provisioning/ProvisioningTester.java | 18 +++++++++++++++++- .../provisioning/VirtualNodeProvisioningTest.java | 14 +++++--------- 8 files changed, 34 insertions(+), 40 deletions(-) (limited to 'node-repository') 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 19911076e69..cc755c01405 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 @@ -93,7 +93,7 @@ class AutoscalingTester { public void makeReady(String hostname) { Node node = nodeRepository().getNode(hostname).get(); - nodeRepository().write(node.with(new IP.Config(Set.of("::" + 0 + ":0"), Set.of())), nodeRepository().lock(node)); + provisioningTester.patchNode(node, (n) -> n.with(new IP.Config(Set.of("::" + 0 + ":0"), Set.of()))); Node host = nodeRepository().getNode(node.parentHostname().get()).get(); host = host.with(new IP.Config(Set.of("::" + 0 + ":0"), Set.of("::" + 0 + ":2"))); if (host.state() == Node.State.provisioned) 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 3d17cbf0217..056fe041377 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 @@ -133,7 +133,7 @@ public class InactiveAndFailedExpirerTest { // Flag one node for retirement and redeploy { Node toRetire = tester.getNodes(applicationId, Node.State.active).asList().get(0); - tester.patchNode(toRetire.withWantToRetire(true, Agent.operator, tester.clock().instant())); + tester.patchNode(toRetire, (node) -> node.withWantToRetire(true, Agent.operator, tester.clock().instant())); List hostSpecs = tester.prepare(applicationId, cluster, Capacity.from(new ClusterResources(2, 1, nodeResources))); tester.activate(applicationId, new HashSet<>(hostSpecs)); } @@ -203,9 +203,7 @@ public class InactiveAndFailedExpirerTest { assertEquals(2, inactiveNodes.size()); // Nodes marked for deprovisioning are moved to parked - tester.nodeRepository().write(inactiveNodes.stream() - .map(node -> node.withWantToRetire(true, true, Agent.system, tester.clock().instant())) - .collect(Collectors.toList()), () -> {}); + tester.patchNodes(inactiveNodes, (node) -> node.withWantToRetire(true, true, Agent.system, tester.clock().instant())); tester.advanceTime(Duration.ofMinutes(11)); new InactiveExpirer(tester.nodeRepository(), tester.clock(), Duration.ofMinutes(10), new TestMetric()).run(); assertEquals(2, tester.nodeRepository().getNodes(Node.State.parked).size()); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OsUpgradeActivatorTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OsUpgradeActivatorTest.java index 218812f9a3d..f795dbaaa1c 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OsUpgradeActivatorTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/OsUpgradeActivatorTest.java @@ -97,13 +97,7 @@ public class OsUpgradeActivatorTest { } private void completeUpgradeOf(List nodes) { - for (var node : nodes) { - try (var lock = tester.nodeRepository().lock(node)) { - node = tester.nodeRepository().getNode(node.hostname()).get(); - node = node.with(node.status().withVespaVersion(node.allocation().get().membership().cluster().vespaVersion())); - tester.nodeRepository().write(node, lock); - } - } + tester.patchNodes(nodes, (node) -> node.with(node.status().withVespaVersion(node.allocation().get().membership().cluster().vespaVersion()))); } private Stream streamUpdatedNodes(List nodes) { diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/os/OsVersionsTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/os/OsVersionsTest.java index 6a41e766ace..715ecdb5949 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/os/OsVersionsTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/os/OsVersionsTest.java @@ -21,7 +21,6 @@ import java.util.List; import java.util.Optional; import java.util.function.Function; import java.util.function.Supplier; -import java.util.function.UnaryOperator; import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; @@ -273,20 +272,11 @@ public class OsVersionsTest { } private void setWantedVersion(List nodes, Version wantedVersion) { - writeNode(nodes, node -> node.with(node.status().withOsVersion(node.status().osVersion().withWanted(Optional.of(wantedVersion))))); + tester.patchNodes(nodes, node -> node.with(node.status().withOsVersion(node.status().osVersion().withWanted(Optional.of(wantedVersion))))); } private void setCurrentVersion(List nodes, Version currentVersion) { - writeNode(nodes, node -> node.with(node.status().withOsVersion(node.status().osVersion().withCurrent(Optional.of(currentVersion))))); - } - - private void writeNode(List nodes, UnaryOperator updateFunc) { - for (var node : nodes) { - try (var lock = tester.nodeRepository().lock(node)) { - node = tester.nodeRepository().getNode(node.hostname()).get(); - tester.nodeRepository().write(updateFunc.apply(node), lock); - } - } + tester.patchNodes(nodes, node -> node.with(node.status().withOsVersion(node.status().osVersion().withCurrent(Optional.of(currentVersion))))); } private void completeUpgradeOf(List nodes) { @@ -294,7 +284,8 @@ public class OsVersionsTest { } private void completeUpgradeOf(List nodes, NodeType nodeType) { - writeNode(nodes, (node) -> { + // Complete upgrade by deprovisioning stale hosts and provisioning new ones + tester.patchNodes(nodes, (node) -> { Optional wantedOsVersion = node.status().osVersion().wanted(); if (node.status().wantToDeprovision()) { // Complete upgrade by deprovisioning stale hosts and provisioning new ones 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 411283abf33..57c7c46c2d9 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 @@ -128,8 +128,7 @@ public class DynamicDockerProvisionTest { NodeResources resources = new NodeResources(10, 10, 10, 10); ApplicationId app = tester.makeApplicationId(); - Function retireNode = node -> - tester.nodeRepository().write(node.withWantToRetire(true, Agent.system, Instant.now()), () -> {}); + Function retireNode = node -> tester.patchNode(node, (n) -> n.withWantToRetire(true, Agent.system, Instant.now())); Function getNodeInGroup = group -> tester.nodeRepository().getNodes(app).stream() .filter(node -> node.allocation().get().membership().cluster().group().get().index() == group) .findAny().orElseThrow(); 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 d53a0732c89..6cf5a2c8342 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 @@ -673,7 +673,7 @@ public class ProvisioningTest { ApplicationId application = tester.makeApplicationId(); // Flag all nodes for retirement List readyNodes = tester.makeReadyNodes(5, defaultResources); - readyNodes.forEach(node -> tester.patchNode(node.withWantToRetire(true, Agent.system, tester.clock().instant()))); + tester.patchNodes(readyNodes, (node) -> node.withWantToRetire(true, Agent.system, tester.clock().instant())); try { prepare(application, 2, 0, 2, 0, defaultResources, tester); @@ -701,7 +701,7 @@ public class ProvisioningTest { assertEquals(0, NodeList.copyOf(tester.nodeRepository().getNodes(application, Node.State.active)).retired().size()); // Mark the nodes as want to retire - tester.nodeRepository().getNodes(application, Node.State.active).forEach(node -> tester.patchNode(node.withWantToRetire(true, Agent.system, tester.clock().instant()))); + tester.nodeRepository().getNodes(application, Node.State.active).forEach(node -> tester.patchNode(node, (n) -> n.withWantToRetire(true, Agent.system, tester.clock().instant()))); // redeploy without allow failing tester.activate(application, tester.prepare(application, cluster, capacityFORCED)); @@ -786,7 +786,7 @@ public class ProvisioningTest { // Retire some nodes and redeploy { List nodesToRetire = tester.getNodes(application, Node.State.active).asList().subList(0, 2); - nodesToRetire.forEach(node -> tester.patchNode(node.withWantToRetire(true, Agent.system, tester.clock().instant()))); + tester.patchNodes(nodesToRetire, (node) -> node.withWantToRetire(true, Agent.system, tester.clock().instant())); SystemState state = prepare(application, 2, 0, 2, 0, defaultResources, tester); tester.activate(application, state.allHosts); 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 0a3c85d3702..4c8d5caad43 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 @@ -53,6 +53,7 @@ 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; @@ -139,7 +140,22 @@ public class ProvisioningTester { public CapacityPolicies capacityPolicies() { return capacityPolicies; } public NodeList getNodes(ApplicationId id, Node.State ... inState) { return NodeList.copyOf(nodeRepository.getNodes(id, inState)); } - public void patchNode(Node node) { nodeRepository.write(node, () -> {}); } + public Node patchNode(Node node, UnaryOperator patcher) { + return patchNodes(List.of(node), patcher).get(0); + } + + public List patchNodes(List nodes, UnaryOperator patcher) { + List updated = new ArrayList<>(); + for (var node : nodes) { + try (var lock = nodeRepository.lock(node)) { + node = nodeRepository.getNode(node.hostname()).get(); + node = patcher.apply(node); + nodeRepository.write(node, lock); + updated.add(node); + } + } + return updated; + } public List prepare(ApplicationId application, ClusterSpec cluster, int nodeCount, int groups, NodeResources resources) { return prepare(application, cluster, nodeCount, groups, false, resources); 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 51261c29a71..0ef7071b095 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 @@ -129,9 +129,7 @@ public class VirtualNodeProvisioningTest { assertDistinctParentHosts(nodes, ClusterSpec.Type.container, containerNodeCount); assertDistinctParentHosts(nodes, ClusterSpec.Type.content, contentNodeCount); - for (Node n : nodes) { - tester.patchNode(n.withParentHostname("clashing")); - } + tester.patchNodes(nodes, (n) -> n.withParentHostname("clashing")); containerHosts = prepare(containerClusterSpec, containerNodeCount, groups); contentHosts = prepare(contentClusterSpec, contentNodeCount, groups); activate(containerHosts, contentHosts); @@ -160,9 +158,7 @@ public class VirtualNodeProvisioningTest { assertDistinctParentHosts(nodes, ClusterSpec.Type.container, containerNodeCount); assertDistinctParentHosts(nodes, ClusterSpec.Type.content, contentNodeCount); - for (Node n : nodes) { - tester.patchNode(n.withParentHostname("clashing")); - } + tester.patchNodes(nodes, (n) -> n.withParentHostname("clashing")); OutOfCapacityException expected = null; try { containerHosts = prepare(containerClusterSpec, containerNodeCount, groups); @@ -216,9 +212,9 @@ public class VirtualNodeProvisioningTest { assertEquals(3, nodes.size()); // Set indistinct parents - tester.patchNode(nodes.get(0).withParentHostname("parentHost1")); - tester.patchNode(nodes.get(1).withParentHostname("parentHost1")); - tester.patchNode(nodes.get(2).withParentHostname("parentHost2")); + tester.patchNode(nodes.get(0), (n) -> n.withParentHostname("parentHost1")); + tester.patchNode(nodes.get(1), (n) -> n.withParentHostname("parentHost1")); + tester.patchNode(nodes.get(2), (n) -> n.withParentHostname("parentHost2")); nodes = getNodes(applicationId); assertEquals(3, nodes.stream().filter(n -> n.parentHostname().isPresent()).count()); -- cgit v1.2.3