diff options
author | Valerij Fredriksen <valerijf@verizonmedia.com> | 2019-05-07 09:06:21 +0200 |
---|---|---|
committer | Valerij Fredriksen <valerijf@verizonmedia.com> | 2019-05-07 09:06:21 +0200 |
commit | 316f1e4a66d6e29e91f24a8e6a79a810f4aad430 (patch) | |
tree | c3775892085a3749e95f541bb3dc26743ab7af75 /node-repository | |
parent | f6cf93af903ad78f505714a307ab11068616d3ab (diff) | |
parent | 231477602105f19e232fdb7fce3f7d0912028b64 (diff) |
Merge branch 'master' into freva/move-job-controller-to-node-repo
Diffstat (limited to 'node-repository')
43 files changed, 614 insertions, 468 deletions
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 bfccfdf4224..196aea3e4a5 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 @@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableSet; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterMembership; 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.Agent; import com.yahoo.vespa.hosted.provision.node.Allocation; @@ -48,9 +49,9 @@ public final class Node { private final Optional<Allocation> allocation; /** Temporary method until we can merge it with the other create method */ - public static Node createDockerNode(Set<String> ipAddresses, Set<String> ipAddressPool, String hostname, Optional<String> parentHostname, Flavor flavor, NodeType type) { - return new Node("fake-" + hostname, ipAddresses, ipAddressPool, hostname, parentHostname, flavor, Status.initial(), State.reserved, - Optional.empty(), History.empty(), type, new Reports(), Optional.empty()); + public static Node createDockerNode(Set<String> ipAddresses, Set<String> ipAddressPool, String hostname, Optional<String> parentHostname, NodeResources resources, NodeType type) { + return new Node("fake-" + hostname, ipAddresses, ipAddressPool, hostname, parentHostname, new Flavor(resources), Status.initial(), State.reserved, + Optional.empty(), History.empty(), type, new Reports(), Optional.empty()); } /** Creates a node in the initial state (provisioned) */ diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java index bfb7bfe9dae..7058917d351 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.provision; import com.google.common.collect.ImmutableList; 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.Collection; @@ -44,6 +45,9 @@ public class NodeList implements Iterable<Node> { return filter(node -> ! node.allocation().get().membership().retired()); } + /** Returns the subset of nodes having exactly the given resources */ + public NodeList resources(NodeResources resources) { return filter(node -> node.flavor().resources().equals(resources)); } + /** Returns the subset of nodes of the given flavor */ public NodeList flavor(String flavor) { return filter(node -> node.flavor().name().equals(flavor)); diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java index 2f1cadf5964..3d8639a43be 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirer.java @@ -6,6 +6,7 @@ import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.Zone; +import com.yahoo.document.datatypes.Array; import com.yahoo.vespa.hosted.provision.Node; import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.node.Agent; @@ -16,6 +17,7 @@ import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.function.Predicate; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -75,23 +77,21 @@ public class FailedExpirer extends Maintainer { @Override protected void maintain() { - List<Node> containerNodes = getExpiredNodes(containerExpiry) - .stream() - .filter(node -> node.allocation().isPresent() && - node.allocation().get().membership().cluster().type() == ClusterSpec.Type.container) - .collect(Collectors.toList()); - List<Node> remainingNodes = getExpiredNodes(defaultExpiry); - remainingNodes.removeAll(containerNodes); - recycle(containerNodes); - recycle(remainingNodes); + List<Node> remainingNodes = new ArrayList<>(nodeRepository.getNodes(Node.State.failed)); + + recycleIf(remainingNodes, node -> node.allocation().isEmpty()); + recycleIf(remainingNodes, node -> + node.allocation().get().membership().cluster().type() == ClusterSpec.Type.container && + node.history().hasEventBefore(History.Event.Type.failed, clock.instant().minus(containerExpiry))); + recycleIf(remainingNodes, node -> + node.history().hasEventBefore(History.Event.Type.failed, clock.instant().minus(defaultExpiry))); } - /** Get failed nodes that have expired according to given expiry */ - private List<Node> getExpiredNodes(Duration expiry) { - return nodeRepository.getNodes(Node.State.failed).stream() - .filter(node -> node.history() - .hasEventBefore(History.Event.Type.failed, clock.instant().minus(expiry))) - .collect(Collectors.toList()); + /** Recycle the nodes matching condition, and remove those nodes from the nodes list. */ + private void recycleIf(List<Node> nodes, Predicate<Node> recycleCondition) { + List<Node> nodesToRecycle = nodes.stream().filter(recycleCondition).collect(Collectors.toList()); + nodes.removeAll(nodesToRecycle); + recycle(nodesToRecycle); } /** Move eligible nodes to dirty. This may be a subset of the given nodes */ diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporter.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporter.java index 69e5ae32817..87c69d6a124 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporter.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporter.java @@ -215,12 +215,12 @@ public class MetricsReporter extends Maintainer { private void updateDockerMetrics(List<Node> nodes) { // Capacity flavors for docker DockerHostCapacity capacity = new DockerHostCapacity(nodes); - metric.set("hostedVespa.docker.totalCapacityCpu", capacity.getCapacityTotal().getCpu(), null); - metric.set("hostedVespa.docker.totalCapacityMem", capacity.getCapacityTotal().getMemory(), null); - metric.set("hostedVespa.docker.totalCapacityDisk", capacity.getCapacityTotal().getDisk(), null); - metric.set("hostedVespa.docker.freeCapacityCpu", capacity.getFreeCapacityTotal().getCpu(), null); - metric.set("hostedVespa.docker.freeCapacityMem", capacity.getFreeCapacityTotal().getMemory(), null); - metric.set("hostedVespa.docker.freeCapacityDisk", capacity.getFreeCapacityTotal().getDisk(), null); + metric.set("hostedVespa.docker.totalCapacityCpu", capacity.getCapacityTotal().vcpu(), null); + metric.set("hostedVespa.docker.totalCapacityMem", capacity.getCapacityTotal().memoryGb(), null); + metric.set("hostedVespa.docker.totalCapacityDisk", capacity.getCapacityTotal().diskGb(), null); + metric.set("hostedVespa.docker.freeCapacityCpu", capacity.getFreeCapacityTotal().vcpu(), null); + metric.set("hostedVespa.docker.freeCapacityMem", capacity.getFreeCapacityTotal().memoryGb(), null); + metric.set("hostedVespa.docker.freeCapacityDisk", capacity.getFreeCapacityTotal().diskGb(), null); List<Flavor> dockerFlavors = nodeRepository().getAvailableFlavors().getFlavors().stream() .filter(f -> f.getType().equals(Flavor.Type.DOCKER_CONTAINER)) 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 5ba56dfa8ed..bd2ce3a5f5e 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 @@ -11,6 +11,7 @@ import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.NetworkPortsSerializer; import com.yahoo.config.provision.NodeFlavors; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.TenantName; import com.yahoo.slime.ArrayTraverser; @@ -69,8 +70,14 @@ public class NodeSerializer { private static final String reportsKey = "reports"; private static final String modelNameKey = "modelName"; - // Configuration fields + // Node resource fields + // ...for hosts and nodes allocated by legacy flavor specs private static final String flavorKey = "flavor"; + // ...for nodes allocated by resources + private static final String resourcesKey = "resources"; + private static final String vcpuKey = "vcpu"; + private static final String memoryKey = "memory"; + private static final String diskKey = "disk"; // Allocation fields private static final String tenantIdKey = "tenantId"; @@ -114,7 +121,7 @@ public class NodeSerializer { toSlime(node.ipAddressPool().asSet(), object.setArray(ipAddressPoolKey), IP::requireAddressPool); object.setString(idKey, node.id()); node.parentHostname().ifPresent(hostname -> object.setString(parentHostnameKey, hostname)); - object.setString(flavorKey, node.flavor().name()); + toSlime(node.flavor(), object); object.setLong(rebootGenerationKey, node.status().reboot().wanted()); object.setLong(currentRebootGenerationKey, node.status().reboot().current()); node.status().vespaVersion().ifPresent(version -> object.setString(vespaVersionKey, version.toString())); @@ -134,6 +141,19 @@ public class NodeSerializer { node.modelName().ifPresent(modelName -> object.setString(modelNameKey, modelName)); } + private void toSlime(Flavor flavor, Cursor object) { + if (flavor.isConfigured()) { + object.setString(flavorKey, flavor.name()); + } + else { + NodeResources resources = flavor.resources(); + Cursor resourcesObject = object.setObject(resourcesKey); + resourcesObject.setDouble(vcpuKey, resources.vcpu()); + resourcesObject.setDouble(memoryKey, resources.memoryGb()); + resourcesObject.setDouble(diskKey, resources.diskGb()); + } + } + private void toSlime(Allocation allocation, Cursor object) { object.setString(tenantIdKey, allocation.owner().tenant().value()); object.setString(applicationIdKey, allocation.owner().application().value()); @@ -198,7 +218,15 @@ public class NodeSerializer { } private Flavor flavorFromSlime(Inspector object) { - return flavors.getFlavorOrThrow(object.field(flavorKey).asString()); + if (object.field(flavorKey).valid()) { + return flavors.getFlavorOrThrow(object.field(flavorKey).asString()); + } + else { + Inspector resources = object.field(resourcesKey); + return new Flavor(new NodeResources(resources.field(vcpuKey).asDouble(), + resources.field(memoryKey).asDouble(), + resources.field(diskKey).asDouble())); + } } private Optional<Allocation> allocationFromSlime(Inspector object) { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java index e437badf0dc..dbeed0c3e76 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java @@ -4,10 +4,10 @@ package com.yahoo.vespa.hosted.provision.provisioning; import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.Zone; -import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.NodeFlavors; import java.util.Arrays; @@ -41,19 +41,29 @@ public class CapacityPolicies { } } - public Flavor decideFlavor(Capacity requestedCapacity, ClusterSpec cluster) { - // for now, always use the requested flavor if a docker flavor is requested - Optional<String> requestedFlavor = requestedCapacity.flavor(); - if (requestedFlavor.isPresent() && - flavors.getFlavorOrThrow(requestedFlavor.get()).getType() == Flavor.Type.DOCKER_CONTAINER) - return flavors.getFlavorOrThrow(requestedFlavor.get()); + public NodeResources decideFlavor(Capacity requestedCapacity, ClusterSpec cluster) { + Optional<NodeResources> requestedFlavor = requestedCapacity.nodeResources(); + if (requestedFlavor.isPresent() && ! requestedFlavor.get().allocateByLegacyName()) + return requestedFlavor.get(); - String defaultFlavorName = zone.defaultFlavor(cluster.type()); + NodeResources defaultFlavor = NodeResources.fromLegacyName(zone.defaultFlavor(cluster.type())); + if (requestedFlavor.isEmpty()) + return defaultFlavor; + + // Flavor is specified and is allocateByLegacyName: Handle legacy flavor specs if (zone.system() == SystemName.cd) - return flavors.getFlavorOrThrow(requestedFlavor.orElse(defaultFlavorName)); - switch(zone.environment()) { - case dev : case test : case staging : return flavors.getFlavorOrThrow(defaultFlavorName); - default : return flavors.getFlavorOrThrow(requestedFlavor.orElse(defaultFlavorName)); + return flavors.exists(requestedFlavor.get().legacyName().get()) ? requestedFlavor.get() : defaultFlavor; + else { + switch (zone.environment()) { + case dev: case test: case staging: return defaultFlavor; + default: + // Check existence of the legacy specified flavor + flavors.getFlavorOrThrow(requestedFlavor.get().legacyName().get()); + // Return this spec containing the legacy flavor name, not the flavor's capacity object + // which describes the flavors capacity, as the point of legacy allocation is to match + // by name, not by resources + return requestedFlavor.get(); + } } } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/DockerHostCapacity.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/DockerHostCapacity.java index 95b8bd24ba5..4c557c60802 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/DockerHostCapacity.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/DockerHostCapacity.java @@ -33,13 +33,14 @@ public class DockerHostCapacity { /** * Compare hosts on free capacity. - * <p> * Used in prioritizing hosts for allocation in <b>descending</b> order. */ int compare(Node hostA, Node hostB) { - int comp = freeCapacityOf(hostB, false).compare(freeCapacityOf(hostA, false)); + int comp = compare(freeCapacityOf(hostB, false), + freeCapacityOf(hostA, false)); if (comp == 0) { - comp = freeCapacityOf(hostB, false).compare(freeCapacityOf(hostA, false)); + comp = compare(freeCapacityOf(hostB, false), + freeCapacityOf(hostA, false)); if (comp == 0) { // If resources are equal - we want to assign to the one with the most IPaddresses free comp = freeIPs(hostB) - freeIPs(hostA); @@ -49,9 +50,11 @@ public class DockerHostCapacity { } int compareWithoutInactive(Node hostA, Node hostB) { - int comp = freeCapacityOf(hostB, true).compare(freeCapacityOf(hostA, true)); + int comp = compare(freeCapacityOf(hostB, true), + freeCapacityOf(hostA, true)); if (comp == 0) { - comp = freeCapacityOf(hostB, true).compare(freeCapacityOf(hostA, true)); + comp = compare(freeCapacityOf(hostB, true), + freeCapacityOf(hostA, true)); if (comp == 0) { // If resources are equal - we want to assign to the one with the most IPaddresses free comp = freeIPs(hostB) - freeIPs(hostA); @@ -60,6 +63,10 @@ public class DockerHostCapacity { return comp; } + private int compare(ResourceCapacity a, ResourceCapacity b) { + return ResourceCapacityComparator.defaultOrder().compare(a, b); + } + /** * Checks the node capacity and free ip addresses to see * if we could allocate a flavor on the docker host. @@ -105,11 +112,22 @@ public class DockerHostCapacity { } private int canFitNumberOf(Node node, Flavor flavor) { - int capacityFactor = freeCapacityOf(node, false).freeCapacityInFlavorEquivalence(flavor); + ResourceCapacity freeCapacity = freeCapacityOf(node, false); + int capacityFactor = freeCapacityInFlavorEquivalence(freeCapacity, flavor); int ips = freeIPs(node); return Math.min(capacityFactor, ips); } + int freeCapacityInFlavorEquivalence(ResourceCapacity freeCapacity, Flavor flavor) { + if ( ! freeCapacity.hasCapacityFor(ResourceCapacity.of(flavor))) return 0; + + double cpuFactor = Math.floor(freeCapacity.vcpu() / flavor.getMinCpuCores()); + double memoryFactor = Math.floor(freeCapacity.memoryGb() / flavor.getMinMainMemoryAvailableGb()); + double diskFactor = Math.floor(freeCapacity.diskGb() / flavor.getMinDiskAvailableGb()); + + return (int) Math.min(Math.min(memoryFactor, cpuFactor), diskFactor); + } + /** * Calculate the remaining capacity for the dockerHost. * @param dockerHost The host to find free capacity of. diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/FlavorConfigBuilder.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/FlavorConfigBuilder.java index cff62508ec6..77872fc1435 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/FlavorConfigBuilder.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/FlavorConfigBuilder.java @@ -31,19 +31,6 @@ public class FlavorConfigBuilder { return flavor; } - public FlavorsConfig.Flavor.Builder addNonStockFlavor(String flavorName, double cpu, double mem, double disk, Flavor.Type type) { - FlavorsConfig.Flavor.Builder flavor = new FlavorsConfig.Flavor.Builder(); - flavor.name(flavorName); - flavor.description("Flavor-name-is-" + flavorName); - flavor.minDiskAvailableGb(disk); - flavor.minCpuCores(cpu); - flavor.minMainMemoryAvailableGb(mem); - flavor.stock(false); - flavor.environment(type.name()); - builder.flavor(flavor); - return flavor; - } - public void addReplaces(String replaces, FlavorsConfig.Flavor.Builder flavor) { FlavorsConfig.Flavor.Replaces.Builder flavorReplaces = new FlavorsConfig.Flavor.Replaces.Builder(); flavorReplaces.name(replaces); 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 a8b48751d23..5db9eaf3d08 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 @@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.provision.provisioning; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.NodeFlavors; import com.yahoo.config.provision.OutOfCapacityException; import com.yahoo.lang.MutableInteger; import com.yahoo.transaction.Mutex; @@ -64,7 +65,8 @@ public class GroupPreparer { // Create a prioritized set of nodes NodeList nodeList = nodeRepository.list(); NodePrioritizer prioritizer = new NodePrioritizer( - nodeList, application, cluster, requestedNodes, spareCount, nodeRepository.nameResolver()); + nodeList, application, cluster, requestedNodes, spareCount, nodeRepository.nameResolver(), + nodeRepository.getAvailableFlavors()); prioritizer.addApplicationNodes(); prioritizer.addSurplusNodes(surplusActiveNodes); @@ -73,14 +75,14 @@ public class GroupPreparer { // Allocate from the prioritized list NodeAllocation allocation = new NodeAllocation(nodeList, application, cluster, requestedNodes, - highestIndex, nodeRepository.zone(), nodeRepository.clock()); + highestIndex, nodeRepository.getAvailableFlavors(), + nodeRepository.zone(), nodeRepository.clock()); allocation.offer(prioritizer.prioritize()); if (dynamicProvisioningEnabled) { List<ProvisionedHost> provisionedHosts = allocation.getFulfilledDockerDeficit() - .map(deficit -> hostProvisioner.get().provisionHosts( - nodeRepository.database().getProvisionIndexes(deficit.getCount()), - deficit.getFlavor())) + .map(deficit -> hostProvisioner.get().provisionHosts(nodeRepository.database().getProvisionIndexes(deficit.getCount()), + deficit.getFlavor())) .orElseGet(List::of); // At this point we have started provisioning of the hosts, the first priority is to make sure that diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostProvisioner.java index 79296e58045..af933d0a8f0 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostProvisioner.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostProvisioner.java @@ -1,7 +1,7 @@ // Copyright 2019 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.vespa.hosted.provision.Node; import java.util.List; @@ -17,13 +17,12 @@ public interface HostProvisioner { /** * Schedule provisioning of a given number of hosts. * - * @param provisionIndexes List of unique provision indexes which will be used to generate the host hostnames + * @param provisionIndexes List of unique provision indexes which will be used to generate the node hostnames * on the form of <code>[prefix][index].[domain]</code> - * @param nodeFlavor Vespa flavor of the node that will run on this host. The resulting provisioned host - * will be of a flavor that is at least as big or bigger than this. - * @return list of {@link ProvisionedHost} describing the provisioned hosts and nodes on them. + * @param resources the resources needed per node + * @return list of {@link ProvisionedHost} describing the provisioned nodes */ - List<ProvisionedHost> provisionHosts(List<Integer> provisionIndexes, Flavor nodeFlavor); + List<ProvisionedHost> provisionHosts(List<Integer> provisionIndexes, NodeResources resources); /** * Continue provisioning of given list of Nodes. @@ -47,4 +46,5 @@ public interface HostProvisioner { * @param host host to deprovision. */ void deprovision(Node host); + } 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 dcc3c4a0ef8..d4527452e9c 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 @@ -4,7 +4,8 @@ package com.yahoo.vespa.hosted.provision.provisioning; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.ClusterSpec; -import com.yahoo.config.provision.Flavor; +import com.yahoo.config.provision.NodeResources; +import com.yahoo.config.provision.NodeFlavors; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.TenantName; import com.yahoo.config.provision.Zone; @@ -67,17 +68,18 @@ class NodeAllocation { /** The next membership index to assign to a new node */ private final MutableInteger highestIndex; + private final NodeFlavors flavors; private final Zone zone; - private final Clock clock; NodeAllocation(NodeList allNodes, ApplicationId application, ClusterSpec cluster, NodeSpec requestedNodes, - MutableInteger highestIndex, Zone zone, Clock clock) { + MutableInteger highestIndex, NodeFlavors flavors, Zone zone, Clock clock) { this.allNodes = allNodes; this.application = application; this.cluster = cluster; this.requestedNodes = requestedNodes; this.highestIndex = highestIndex; + this.flavors = flavors; this.zone = zone; this.clock = clock; } @@ -217,7 +219,7 @@ class NodeAllocation { } private boolean hasCompatibleFlavor(Node node) { - return requestedNodes.isCompatible(node.flavor()); + return requestedNodes.isCompatible(node.flavor(), flavors); } private Node acceptNode(PrioritizableNode prioritizableNode, boolean wantToRetire) { @@ -284,8 +286,8 @@ class NodeAllocation { return Optional.of(requestedNodes) .filter(NodeSpec.CountNodeSpec.class::isInstance) .map(NodeSpec.CountNodeSpec.class::cast) - .map(spec -> new FlavorCount(spec.getFlavor(), spec.fulfilledDeficitCount(acceptedOfRequestedFlavor))) - .filter(flavorCount -> flavorCount.getFlavor().getType() == Flavor.Type.DOCKER_CONTAINER) + .map(spec -> new FlavorCount(spec.resources(), spec.fulfilledDeficitCount(acceptedOfRequestedFlavor))) + .filter(flavorCount -> ! flavorCount.getFlavor().allocateByLegacyName()) .filter(flavorCount -> flavorCount.getCount() > 0); } @@ -364,15 +366,16 @@ class NodeAllocation { } static class FlavorCount { - private final Flavor flavor; + + private final NodeResources flavor; private final int count; - private FlavorCount(Flavor flavor, int count) { + private FlavorCount(NodeResources flavor, int count) { this.flavor = flavor; this.count = count; } - Flavor getFlavor() { + NodeResources getFlavor() { return flavor; } 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 5a3f0380fa2..288a24a0d89 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 @@ -4,6 +4,8 @@ package com.yahoo.vespa.hosted.provision.provisioning; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Flavor; +import com.yahoo.config.provision.NodeResources; +import com.yahoo.config.provision.NodeFlavors; import com.yahoo.config.provision.NodeType; import com.yahoo.log.LogLevel; import com.yahoo.transaction.Mutex; @@ -44,21 +46,22 @@ class NodePrioritizer { private final ApplicationId appId; private final ClusterSpec clusterSpec; private final NameResolver nameResolver; + private final NodeFlavors flavors; private final boolean isDocker; private final boolean isAllocatingForReplacement; private final Set<Node> spareHosts; NodePrioritizer(NodeList allNodes, ApplicationId appId, ClusterSpec clusterSpec, NodeSpec nodeSpec, - int spares, NameResolver nameResolver) { + int spares, NameResolver nameResolver, NodeFlavors flavors) { this.allNodes = allNodes; this.capacity = new DockerHostCapacity(allNodes); this.requestedNodes = nodeSpec; this.clusterSpec = clusterSpec; this.appId = appId; this.nameResolver = nameResolver; + this.flavors = flavors; this.spareHosts = findSpareHosts(allNodes, capacity, spares); - int nofFailedNodes = (int) allNodes.asList().stream() .filter(node -> node.state().equals(Node.State.failed)) .filter(node -> node.allocation().isPresent()) @@ -143,8 +146,8 @@ class NodePrioritizer { } void addNewDockerNodesOn(Mutex allocationLock, NodeList candidates) { - if (!isDocker) return; - ResourceCapacity wantedResourceCapacity = ResourceCapacity.of(getFlavor(requestedNodes)); + if ( ! isDocker) return; + ResourceCapacity wantedResourceCapacity = ResourceCapacity.of(resources(requestedNodes)); for (Node node : candidates) { if (node.type() != NodeType.host) continue; @@ -171,10 +174,10 @@ class NodePrioritizer { Collections.emptySet(), allocation.get().hostname(), Optional.of(node.hostname()), - getFlavor(requestedNodes), + resources(requestedNodes), NodeType.tenant); PrioritizableNode nodePri = toNodePriority(newNode, false, true); - if (!nodePri.violatesSpares || isAllocatingForReplacement) { + if ( ! nodePri.violatesSpares || isAllocatingForReplacement) { log.log(LogLevel.DEBUG, "Adding new Docker node " + newNode); nodes.put(newNode, nodePri); } @@ -215,8 +218,7 @@ class NodePrioritizer { PrioritizableNode.Builder builder = new PrioritizableNode.Builder(node) .withSurplusNode(isSurplusNode) .withNewNode(isNewNode) - .withPreferredOnFlavor( - requestedNodes.specifiesNonStockFlavor() && node.flavor().equals(getFlavor(requestedNodes))); + .withPreferredOnFlavor(preferredOnLegacyFlavor(node)); allNodes.parentOf(node).ifPresent(parent -> { builder.withParent(parent).withFreeParentCapacity(capacity.freeCapacityOf(parent, false)); @@ -229,6 +231,18 @@ class NodePrioritizer { return builder.build(); } + /** Needed to handle requests for legacy non-docker nodes only */ + private boolean preferredOnLegacyFlavor(Node node) { + if (requestedNodes instanceof NodeSpec.CountNodeSpec) { + NodeResources requestedNodeResources = ((NodeSpec.CountNodeSpec)requestedNodes).resources(); + if (requestedNodeResources.allocateByLegacyName()) { + Flavor requestedFlavor = flavors.getFlavorOrThrow(requestedNodeResources.legacyName().get()); + return ! requestedFlavor.isStock() && node.flavor().equals(requestedFlavor); + } + } + return false; + } + static boolean isPreferredNodeToBeRelocated(List<Node> nodes, Node node, Node parent) { NodeList list = new NodeList(nodes); return list.childrenOf(parent).asList().stream() @@ -243,22 +257,23 @@ class NodePrioritizer { return requestedNodes.fulfilledBy(nofNodesInCluster - nodeFailedNodes); } - private static Flavor getFlavor(NodeSpec requestedNodes) { + private static NodeResources resources(NodeSpec requestedNodes) { if (requestedNodes instanceof NodeSpec.CountNodeSpec) { NodeSpec.CountNodeSpec countSpec = (NodeSpec.CountNodeSpec) requestedNodes; - return countSpec.getFlavor(); + return countSpec.resources(); } return null; } private boolean isDocker() { - Flavor flavor = getFlavor(requestedNodes); - return (flavor != null) && flavor.getType().equals(Flavor.Type.DOCKER_CONTAINER); + NodeResources flavor = resources(requestedNodes); + return (flavor != null) && ! flavor.allocateByLegacyName(); } private static int compareForRelocation(Node a, Node b) { // Choose smallest node - int capacity = ResourceCapacity.of(a).compare(ResourceCapacity.of(b)); + int capacity = ResourceCapacityComparator.defaultOrder().compare(ResourceCapacity.of(a), + ResourceCapacity.of(b)); if (capacity != 0) return capacity; // Choose unallocated over allocated (this case is when we have ready docker nodes) diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java index 8faabd268d4..f9d712e767e 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java @@ -6,7 +6,7 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; -import com.yahoo.config.provision.Flavor; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.HostFilter; import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.NodeFlavors; @@ -86,7 +86,7 @@ public class NodeRepositoryProvisioner implements Provisioner { log.log(zone.system() == SystemName.cd ? Level.INFO : LogLevel.DEBUG, () -> "Received deploy prepare request for " + requestedCapacity + " in " + - wantedGroups + " groups for application " + application + ", cluster " + cluster); + wantedGroups + " groups for application " + application + ", cluster " + cluster); int effectiveGroups; NodeSpec requestedNodes; @@ -96,7 +96,7 @@ public class NodeRepositoryProvisioner implements Provisioner { if (zone.environment().isManuallyDeployed() && nodeCount < requestedCapacity.nodeCount()) logger.log(Level.INFO, "Requested " + requestedCapacity.nodeCount() + " nodes for " + cluster + ", downscaling to " + nodeCount + " nodes in " + zone.environment()); - Flavor flavor = capacityPolicies.decideFlavor(requestedCapacity, cluster); + NodeResources flavor = capacityPolicies.decideFlavor(requestedCapacity, cluster); log.log(LogLevel.DEBUG, () -> "Decided flavor for requested tenant nodes: " + flavor); boolean exclusive = capacityPolicies.decideExclusivity(cluster.isExclusive()); effectiveGroups = wantedGroups > nodeCount ? nodeCount : wantedGroups; // cannot have more groups than nodes diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java index e033d994f24..ab48e82fbed 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java @@ -1,6 +1,8 @@ // 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.config.provision.NodeFlavors; import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.Flavor; import com.yahoo.vespa.hosted.provision.Node; @@ -26,13 +28,7 @@ public interface NodeSpec { boolean isExclusive(); /** Returns whether the given flavor is compatible with this spec */ - boolean isCompatible(Flavor flavor); - - /** Returns whether the given flavor is exactly specified by this node spec */ - boolean matchesExactly(Flavor flavor); - - /** Returns whether this requests a non-stock flavor */ - boolean specifiesNonStockFlavor(); + boolean isCompatible(Flavor flavor, NodeFlavors flavors); /** Returns whether the given node count is sufficient to consider this spec fulfilled to the maximum amount */ boolean saturatedBy(int count); @@ -61,7 +57,7 @@ public interface NodeSpec { */ Node assignRequestedFlavor(Node node); - static NodeSpec from(int nodeCount, Flavor flavor, boolean exclusive, boolean canFail) { + static NodeSpec from(int nodeCount, NodeResources flavor, boolean exclusive, boolean canFail) { return new CountNodeSpec(nodeCount, flavor, exclusive, canFail); } @@ -73,20 +69,19 @@ public interface NodeSpec { class CountNodeSpec implements NodeSpec { private final int count; - private final Flavor requestedFlavor; + private final NodeResources requestedNodeResources; private final boolean exclusive; private final boolean canFail; - CountNodeSpec(int count, Flavor flavor, boolean exclusive, boolean canFail) { + CountNodeSpec(int count, NodeResources flavor, boolean exclusive, boolean canFail) { this.count = count; - this.requestedFlavor = Objects.requireNonNull(flavor, "A flavor must be specified"); + this.requestedNodeResources = Objects.requireNonNull(flavor, "A flavor must be specified"); this.exclusive = exclusive; this.canFail = canFail; } - // TODO: Remove usage of this - public Flavor getFlavor() { - return requestedFlavor; + public NodeResources resources() { + return requestedNodeResources; } @Override @@ -96,18 +91,19 @@ public interface NodeSpec { public NodeType type() { return NodeType.tenant; } @Override - public boolean isCompatible(Flavor flavor) { - if (flavor.satisfies(requestedFlavor)) return true; + public boolean isCompatible(Flavor flavor, NodeFlavors flavors) { + if (requestedNodeResources.allocateByLegacyName() && flavor.isConfigured()) { + if (flavor.satisfies(flavors.getFlavorOrThrow(requestedNodeResources.legacyName().get()))) + return true; + } + else { + if (requestedNodeResources.equals(flavor.resources())) + return true; + } return requestedFlavorCanBeAchievedByResizing(flavor); } @Override - public boolean matchesExactly(Flavor flavor) { return flavor.equals(this.requestedFlavor); } - - @Override - public boolean specifiesNonStockFlavor() { return ! requestedFlavor.isStock(); } - - @Override public boolean saturatedBy(int count) { return fulfilledBy(count); } // min=max for count specs @Override @@ -123,20 +119,19 @@ public interface NodeSpec { @Override public NodeSpec fraction(int divisor) { - return new CountNodeSpec(count/divisor, requestedFlavor, exclusive, canFail); + return new CountNodeSpec(count/divisor, requestedNodeResources, exclusive, canFail); } @Override public Node assignRequestedFlavor(Node node) { - // Docker nodes can change flavor in place - if (requestedFlavorCanBeAchievedByResizing(node.flavor())) - return node.with(requestedFlavor); - + // Docker nodes can change flavor in place - disabled - see below + // if (requestedFlavorCanBeAchievedByResizing(node.flavor())) + // return node.with(requestedFlavor); return node; } @Override - public String toString() { return "request for " + count + " nodes of " + requestedFlavor; } + public String toString() { return "request for " + count + " nodes with " + requestedNodeResources; } /** Docker nodes can be downsized in place */ private boolean requestedFlavorCanBeAchievedByResizing(Flavor flavor) { @@ -164,13 +159,7 @@ public interface NodeSpec { public boolean isExclusive() { return false; } @Override - public boolean isCompatible(Flavor flavor) { return true; } - - @Override - public boolean matchesExactly(Flavor flavor) { return false; } - - @Override - public boolean specifiesNonStockFlavor() { return false; } + public boolean isCompatible(Flavor flavor, NodeFlavors flavors) { return true; } @Override public boolean saturatedBy(int count) { return false; } @@ -180,11 +169,9 @@ public interface NodeSpec { @Override public int idealRetiredCount(int acceptedCount, int currentRetiredCount) { - /* - * All nodes marked with wantToRetire get marked as retired just before this function is called, - * the job of this function is to throttle the retired count. If no nodes are marked as retired - * then continue this way, otherwise allow only 1 node to be retired - */ + // All nodes marked with wantToRetire get marked as retired just before this function is called, + // the job of this function is to throttle the retired count. If no nodes are marked as retired + // then continue this way, otherwise allow only 1 node to be retired return Math.min(1, currentRetiredCount); } 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 f98de7ac579..7abb43374bf 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 @@ -90,7 +90,7 @@ class PrioritizableNode implements Comparable<PrioritizableNode> { if (other.parent.isPresent() && !this.parent.isPresent()) return 1; // Choose the node with parent node with the least capacity (TODO parameterize this as this is pretty much the core of the algorithm) - int freeCapacity = this.freeParentCapacity.compare(other.freeParentCapacity); + int freeCapacity = ResourceCapacityComparator.defaultOrder().compare(this.freeParentCapacity, other.freeParentCapacity); if (freeCapacity != 0) return freeCapacity; // Choose cheapest node 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 e0cf882fffa..c2b56c757fa 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 @@ -2,6 +2,7 @@ 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; @@ -15,18 +16,19 @@ import java.util.Set; * @author freva */ public class ProvisionedHost { + private final String id; private final String hostHostname; private final Flavor hostFlavor; private final String nodeHostname; - private final Flavor nodeFlavor; + private final NodeResources nodeResources; - public ProvisionedHost(String id, String hostHostname, Flavor hostFlavor, String nodeHostname, Flavor nodeFlavor) { + public ProvisionedHost(String id, String hostHostname, Flavor hostFlavor, String nodeHostname, NodeResources nodeResources) { this.id = Objects.requireNonNull(id, "Host id must be set"); this.hostHostname = Objects.requireNonNull(hostHostname, "Host hostname must be set"); this.hostFlavor = Objects.requireNonNull(hostFlavor, "Host flavor must be set"); this.nodeHostname = Objects.requireNonNull(nodeHostname, "Node hostname must be set"); - this.nodeFlavor = Objects.requireNonNull(nodeFlavor, "Node flavor must be set"); + this.nodeResources = Objects.requireNonNull(nodeResources, "Node resources must be set"); } /** Generate {@link Node} instance representing the provisioned physical host */ @@ -36,28 +38,26 @@ public class ProvisionedHost { /** Generate {@link Node} instance representing the node running on this physical host */ public Node generateNode() { - return Node.createDockerNode(Set.of(), Set.of(), nodeHostname, Optional.of(hostHostname), nodeFlavor, NodeType.tenant); + return Node.createDockerNode(Set.of(), Set.of(), nodeHostname, Optional.of(hostHostname), nodeResources, NodeType.tenant); } public String getId() { return id; } - public String getHostHostname() { + public String hostHostname() { return hostHostname; } - public Flavor getHostFlavor() { + public Flavor hostFlavor() { return hostFlavor; } - public String getNodeHostname() { + public String nodeHostname() { return nodeHostname; } - public Flavor getNodeFlavor() { - return nodeFlavor; - } + public NodeResources nodeResources() { return nodeResources; } @Override public boolean equals(Object o) { @@ -68,12 +68,12 @@ public class ProvisionedHost { hostHostname.equals(that.hostHostname) && hostFlavor.equals(that.hostFlavor) && nodeHostname.equals(that.nodeHostname) && - nodeFlavor.equals(that.nodeFlavor); + nodeResources.equals(that.nodeResources); } @Override public int hashCode() { - return Objects.hash(id, hostHostname, hostFlavor, nodeHostname, nodeFlavor); + return Objects.hash(id, hostHostname, hostFlavor, nodeHostname, nodeResources); } @Override @@ -83,7 +83,8 @@ public class ProvisionedHost { ", hostHostname='" + hostHostname + '\'' + ", hostFlavor=" + hostFlavor + ", nodeHostname='" + nodeHostname + '\'' + - ", nodeFlavor=" + nodeFlavor + + ", nodeResources=" + nodeResources + '}'; } + } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ResourceCapacity.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ResourceCapacity.java index 903951ef93b..3839d66c8de 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ResourceCapacity.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ResourceCapacity.java @@ -2,6 +2,7 @@ package com.yahoo.vespa.hosted.provision.provisioning; import com.yahoo.config.provision.Flavor; +import com.yahoo.config.provision.NodeResources; import com.yahoo.vespa.hosted.provision.Node; /** @@ -16,14 +17,14 @@ public class ResourceCapacity { public static final ResourceCapacity NONE = new ResourceCapacity(0, 0, 0); - private final double memory; - private final double cpu; - private final double disk; + private final double memoryGb; + private final double vcpu; + private final double diskGb; - private ResourceCapacity(double memory, double cpu, double disk) { - this.memory = memory; - this.cpu = cpu; - this.disk = disk; + private ResourceCapacity(double memoryGb, double vcpu, double diskGb) { + this.memoryGb = memoryGb; + this.vcpu = vcpu; + this.diskGb = diskGb; } static ResourceCapacity of(Flavor flavor) { @@ -31,64 +32,46 @@ public class ResourceCapacity { flavor.getMinMainMemoryAvailableGb(), flavor.getMinCpuCores(), flavor.getMinDiskAvailableGb()); } + static ResourceCapacity of(NodeResources resources) { + return new ResourceCapacity(resources.memoryGb(), resources.vcpu(), resources.diskGb()); + } + static ResourceCapacity of(Node node) { return ResourceCapacity.of(node.flavor()); } - public double getMemory() { - return memory; + public double memoryGb() { + return memoryGb; } - public double getCpu() { - return cpu; + public double vcpu() { + return vcpu; } - public double getDisk() { - return disk; + public double diskGb() { + return diskGb; } public ResourceCapacity subtract(ResourceCapacity other) { - return new ResourceCapacity(memory - other.memory, - cpu - other.cpu, - disk - other.disk); + return new ResourceCapacity(memoryGb - other.memoryGb, + vcpu - other.vcpu, + diskGb - other.diskGb); } public ResourceCapacity add(ResourceCapacity other) { - return new ResourceCapacity(memory + other.memory, - cpu + other.cpu, - disk + other.disk); + return new ResourceCapacity(memoryGb + other.memoryGb, + vcpu + other.vcpu, + diskGb + other.diskGb); } boolean hasCapacityFor(ResourceCapacity capacity) { - return memory >= capacity.memory && - cpu >= capacity.cpu && - disk >= capacity.disk; + return memoryGb >= capacity.memoryGb && + vcpu >= capacity.vcpu && + diskGb >= capacity.diskGb; } boolean hasCapacityFor(Flavor flavor) { return hasCapacityFor(ResourceCapacity.of(flavor)); } - int freeCapacityInFlavorEquivalence(Flavor flavor) { - if (!hasCapacityFor(ResourceCapacity.of(flavor))) return 0; - - double memoryFactor = Math.floor(memory/flavor.getMinMainMemoryAvailableGb()); - double cpuFactor = Math.floor(cpu/flavor.getMinCpuCores()); - double diskFactor = Math.floor(disk/flavor.getMinDiskAvailableGb()); - - return (int) Math.min(Math.min(memoryFactor, cpuFactor), diskFactor); - } - - /** - * Normal compare implementation where -1 if this is less than that. - */ - int compare(ResourceCapacity that) { - if (memory > that.memory) return 1; - if (memory < that.memory) return -1; - if (disk > that.disk) return 1; - if (disk < that.disk) return -1; - if (cpu > that.cpu) return 1; - if (cpu < that.cpu) return -1; - return 0; - } } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ResourceCapacityComparator.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ResourceCapacityComparator.java new file mode 100644 index 00000000000..c4f605bb9fc --- /dev/null +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ResourceCapacityComparator.java @@ -0,0 +1,36 @@ +// Copyright 2019 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 java.util.Comparator; + +/** + * Resource comparator + * + * @author bratseth + */ +public class ResourceCapacityComparator { + + private static final MemoryDiskCpu memoryDiskCpuComparator = new MemoryDiskCpu(); + + /** Returns the default ordering */ + public static Comparator<ResourceCapacity> defaultOrder() { return memoryDiskCpuOrder(); } + + /** Returns a comparator comparing by memory, disk, vcpu */ + public static Comparator<ResourceCapacity> memoryDiskCpuOrder() { return memoryDiskCpuComparator; } + + private static class MemoryDiskCpu implements Comparator<ResourceCapacity> { + + @Override + public int compare(ResourceCapacity a, ResourceCapacity b) { + if (a.memoryGb() > b.memoryGb()) return 1; + if (a.memoryGb() < b.memoryGb()) return -1; + if (a.diskGb() > b.diskGb()) return 1; + if (a.diskGb() < b.diskGb()) return -1; + if (a.vcpu() > b.vcpu()) return 1; + if (a.vcpu() < b.vcpu()) return -1; + return 0; + } + + } + +} diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java index b9fb88e900e..9fccdfda9fe 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockNodeRepository.java @@ -8,6 +8,8 @@ import com.yahoo.config.provision.ApplicationName; import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.DockerImage; +import com.yahoo.config.provision.Flavor; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.NodeFlavors; @@ -35,7 +37,7 @@ import java.util.Set; /** * A mock repository prepopulated with some applications. - * Instantiated by DI from application package above. + * Instantiated by DI. */ public class MockNodeRepository extends NodeRepository { @@ -59,36 +61,47 @@ public class MockNodeRepository extends NodeRepository { } private void populate() { - NodeRepositoryProvisioner provisioner = new NodeRepositoryProvisioner(this, flavors, Zone.defaultZone(), - new MockProvisionServiceProvider(), new InMemoryFlagSource()); + NodeRepositoryProvisioner provisioner = new NodeRepositoryProvisioner(this, + flavors, + Zone.defaultZone(), + new MockProvisionServiceProvider(), + new InMemoryFlagSource()); List<Node> nodes = new ArrayList<>(); // Regular nodes Set<String> ipAddresses = ImmutableSet.of("::1", "127.0.0.1"); Set<String> ipAddressPool = ImmutableSet.of("::2", "::3", "::4"); - nodes.add(createNode("node1", "host1.yahoo.com", ipAddresses, Optional.empty(), flavors.getFlavorOrThrow("default"), NodeType.tenant)); - nodes.add(createNode("node2", "host2.yahoo.com", ipAddresses, Optional.empty(), flavors.getFlavorOrThrow("default"), NodeType.tenant)); - nodes.add(createNode("node3", "host3.yahoo.com", ipAddresses, Optional.empty(), flavors.getFlavorOrThrow("expensive"), NodeType.tenant)); + nodes.add(createNode("node1", "host1.yahoo.com", ipAddresses, Optional.empty(), + flavors.getFlavorOrThrow("default"), NodeType.tenant)); + nodes.add(createNode("node2", "host2.yahoo.com", ipAddresses, Optional.empty(), + flavors.getFlavorOrThrow("default"), NodeType.tenant)); + nodes.add(createNode("node3", "host3.yahoo.com", ipAddresses, Optional.empty(), + flavors.getFlavorOrThrow("expensive"), NodeType.tenant)); - Node node4 = createNode("node4", "host4.yahoo.com", ipAddresses, Optional.of("dockerhost1.yahoo.com"), flavors.getFlavorOrThrow("docker"), NodeType.tenant); + Node node4 = createNode("node4", "host4.yahoo.com", ipAddresses, Optional.of("dockerhost1.yahoo.com"), + new Flavor(new NodeResources(1, 1, 100)), NodeType.tenant); node4 = node4.with(node4.status() .withVespaVersion(new Version("6.41.0")) .withDockerImage(DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa:6.41.0"))); nodes.add(node4); - Node node5 = createNode("node5", "host5.yahoo.com", ipAddresses, Optional.of("dockerhost2.yahoo.com"), flavors.getFlavorOrThrow("docker"), NodeType.tenant); + Node node5 = createNode("node5", "host5.yahoo.com", ipAddresses, Optional.of("dockerhost2.yahoo.com"), + new Flavor(new NodeResources(1, 1, 100)), NodeType.tenant); nodes.add(node5.with(node5.status() .withVespaVersion(new Version("1.2.3")) .withDockerImage(DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa:1.2.3")))); - nodes.add(createNode("node6", "host6.yahoo.com", ipAddresses, Optional.empty(), flavors.getFlavorOrThrow("default"), NodeType.tenant)); - Node node7 = createNode("node7", "host7.yahoo.com", ipAddresses, Optional.empty(), flavors.getFlavorOrThrow("default"), NodeType.tenant); + nodes.add(createNode("node6", "host6.yahoo.com", ipAddresses, Optional.empty(), + flavors.getFlavorOrThrow("default"), NodeType.tenant)); + Node node7 = createNode("node7", "host7.yahoo.com", ipAddresses, Optional.empty(), + flavors.getFlavorOrThrow("default"), NodeType.tenant); nodes.add(node7); // 8, 9, 11 and 12 are added by web service calls - Node node10 = createNode("node10", "host10.yahoo.com", ipAddresses, Optional.of("parent1.yahoo.com"), flavors.getFlavorOrThrow("default"), NodeType.tenant); + Node node10 = createNode("node10", "host10.yahoo.com", ipAddresses, Optional.of("parent1.yahoo.com"), + flavors.getFlavorOrThrow("default"), NodeType.tenant); Status node10newStatus = node10.status(); node10newStatus = node10newStatus .withVespaVersion(Version.fromString("5.104.142")) @@ -96,24 +109,34 @@ public class MockNodeRepository extends NodeRepository { node10 = node10.with(node10newStatus); nodes.add(node10); - Node node13 = createNode("node13", "host13.yahoo.com", ipAddresses, Optional.empty(), flavors.getFlavorOrThrow("large"), NodeType.tenant); - Node node14 = createNode("node14", "host14.yahoo.com", ipAddresses, Optional.empty(), flavors.getFlavorOrThrow("large"), NodeType.tenant); + Node node13 = createNode("node13", "host13.yahoo.com", ipAddresses, Optional.empty(), + flavors.getFlavorOrThrow("large"), NodeType.tenant); + Node node14 = createNode("node14", "host14.yahoo.com", ipAddresses, Optional.empty(), + flavors.getFlavorOrThrow("large"), NodeType.tenant); nodes.add(node13); nodes.add(node14); - Node node55 = createNode("node55", "host55.yahoo.com", ipAddresses, Optional.empty(), flavors.getFlavorOrThrow("default"), NodeType.tenant); + Node node55 = createNode("node55", "host55.yahoo.com", ipAddresses, Optional.empty(), + flavors.getFlavorOrThrow("default"), NodeType.tenant); nodes.add(node55.with(node55.status().withWantToRetire(true).withWantToDeprovision(true))); /* Setup docker hosts (two of these will be reserved for spares */ - nodes.add(createNode("dockerhost1", "dockerhost1.yahoo.com", ipAddresses, ipAddressPool, Optional.empty(), Optional.empty(), flavors.getFlavorOrThrow("large"), NodeType.host)); - nodes.add(createNode("dockerhost2", "dockerhost2.yahoo.com", ipAddresses, ipAddressPool, Optional.empty(), Optional.empty(), flavors.getFlavorOrThrow("large"), NodeType.host)); - nodes.add(createNode("dockerhost3", "dockerhost3.yahoo.com", ipAddresses, ipAddressPool, Optional.empty(), Optional.empty(), flavors.getFlavorOrThrow("large"), NodeType.host)); - nodes.add(createNode("dockerhost4", "dockerhost4.yahoo.com", ipAddresses, ipAddressPool, Optional.empty(), Optional.empty(), flavors.getFlavorOrThrow("large"), NodeType.host)); - nodes.add(createNode("dockerhost5", "dockerhost5.yahoo.com", ipAddresses, ipAddressPool, Optional.empty(), Optional.empty(), flavors.getFlavorOrThrow("large"), NodeType.host)); + nodes.add(createNode("dockerhost1", "dockerhost1.yahoo.com", ipAddresses, ipAddressPool, Optional.empty(), Optional.empty(), + flavors.getFlavorOrThrow("large"), NodeType.host)); + nodes.add(createNode("dockerhost2", "dockerhost2.yahoo.com", ipAddresses, ipAddressPool, Optional.empty(), Optional.empty(), + flavors.getFlavorOrThrow("large"), NodeType.host)); + nodes.add(createNode("dockerhost3", "dockerhost3.yahoo.com", ipAddresses, ipAddressPool, Optional.empty(), Optional.empty(), + flavors.getFlavorOrThrow("large"), NodeType.host)); + nodes.add(createNode("dockerhost4", "dockerhost4.yahoo.com", ipAddresses, ipAddressPool, Optional.empty(), Optional.empty(), + flavors.getFlavorOrThrow("large"), NodeType.host)); + nodes.add(createNode("dockerhost5", "dockerhost5.yahoo.com", ipAddresses, ipAddressPool, Optional.empty(), Optional.empty(), + flavors.getFlavorOrThrow("large"), NodeType.host)); // Config servers - nodes.add(createNode("cfg1", "cfg1.yahoo.com", Collections.singleton("127.0.1.1"), Optional.empty(), flavors.getFlavorOrThrow("default"), NodeType.config)); - nodes.add(createNode("cfg2", "cfg2.yahoo.com", Collections.singleton("127.0.1.2"), Optional.empty(), flavors.getFlavorOrThrow("default"), NodeType.config)); + nodes.add(createNode("cfg1", "cfg1.yahoo.com", Collections.singleton("127.0.1.1"), Optional.empty(), + flavors.getFlavorOrThrow("default"), NodeType.config)); + nodes.add(createNode("cfg2", "cfg2.yahoo.com", Collections.singleton("127.0.1.2"), Optional.empty(), + flavors.getFlavorOrThrow("default"), NodeType.config)); // Ready all nodes, except 7 and 55 nodes = addNodes(nodes); @@ -152,7 +175,7 @@ public class MockNodeRepository extends NodeRepository { ClusterSpec.Id.from("id3"), Version.fromString("6.42"), false, Collections.emptySet()); - activate(provisioner.prepare(app3, cluster3, Capacity.fromNodeCount(2, Optional.of("docker"), false, true), 1, null), app3, provisioner); + activate(provisioner.prepare(app3, cluster3, Capacity.fromCount(2, new NodeResources(1, 1, 100), false, true), 1, null), app3, provisioner); ApplicationId app4 = ApplicationId.from(TenantName.from("tenant4"), ApplicationName.from("application4"), InstanceName.from("instance4")); ClusterSpec cluster4 = ClusterSpec.request(ClusterSpec.Type.container, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java index c5c1e9dae16..23b71cc8b6e 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/FailedExpirerTest.java @@ -9,6 +9,7 @@ import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.Flavor; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.NodeFlavors; @@ -89,7 +90,7 @@ public class FailedExpirerTest { scenario.clock().advance(Duration.ofDays(2)); scenario.expirer().run(); - scenario.assertNodesIn(Node.State.failed, "node1"); + scenario.assertNodesIn(Node.State.dirty, "node1"); scenario.assertNodesIn(Node.State.parked, "node2", "node3"); } @@ -125,7 +126,7 @@ public class FailedExpirerTest { scenario.clock().advance(Duration.ofHours(2)); scenario.expirer().run(); - scenario.assertNodesIn(Node.State.failed, "node1"); + scenario.assertNodesIn(Node.State.dirty, "node1"); scenario.assertNodesIn(Node.State.parked, "node2", "node3"); } @@ -186,20 +187,24 @@ public class FailedExpirerTest { .withNode(NodeType.tenant, FailureScenario.dockerFlavor, "node1", "parent1") .withNode(NodeType.tenant, FailureScenario.dockerFlavor, "node2", "parent2") .withNode(NodeType.tenant, FailureScenario.dockerFlavor, "node3", "parent3") - .withNode(NodeType.tenant, FailureScenario.dockerFlavor, "node4", "parent1") - .withNode(NodeType.tenant, FailureScenario.dockerFlavor, "node5", "parent1") + .withNode(NodeType.tenant, FailureScenario.dockerFlavor, "node4", "parent3") + .withNode(NodeType.tenant, FailureScenario.dockerFlavor, "node5", "parent3") .setReady("node1", "node2", "node3") .allocate(ClusterSpec.Type.content, FailureScenario.dockerFlavor, "node1", "node2", "node3") - .failWithHardwareFailure("parent1"); + .failNode(1, "node3") + .setReady("node4") + .allocate(ClusterSpec.Type.content, FailureScenario.dockerFlavor, "node1", "node2", "node4") + .failNode(1, "node4") + .setReady("node5") + .allocate(ClusterSpec.Type.content, FailureScenario.dockerFlavor, "node1", "node2", "node5") + .failWithHardwareFailure("parent3"); - scenario.clock().advance(Duration.ofDays(2)); - scenario.failNode(1, "node4", "node5"); scenario.clock().advance(Duration.ofDays(3)); scenario.expirer().run(); // Run twice because parent can only be parked after the child scenario.expirer().run(); - scenario.assertNodesIn(Node.State.failed, "parent1", "node4", "node5"); + scenario.assertNodesIn(Node.State.failed, "parent3", "node3", "node4"); } @Test @@ -232,8 +237,8 @@ public class FailedExpirerTest { private static class FailureScenario { private static final NodeFlavors nodeFlavors = FlavorConfigBuilder.createDummies("default", "docker"); - public static final Flavor defaultFlavor = nodeFlavors.getFlavorOrThrow("default"); - public static final Flavor dockerFlavor = nodeFlavors.getFlavorOrThrow("docker"); + public static final NodeResources defaultFlavor = new NodeResources(2, 2, 2); + public static final NodeResources dockerFlavor = new NodeResources(1, 1, 1); private final MockCurator curator = new MockCurator(); private final ManualClock clock = new ManualClock(); @@ -268,15 +273,15 @@ public class FailedExpirerTest { .orElseThrow(() -> new IllegalArgumentException("No such node: " + hostname)); } - public FailureScenario withNode(NodeType type, Flavor flavor, String hostname, String parentHostname) { + public FailureScenario withNode(NodeType type, NodeResources flavor, String hostname, String parentHostname) { nodeRepository.addNodes(Collections.singletonList( nodeRepository.createNode(UUID.randomUUID().toString(), hostname, - Optional.ofNullable(parentHostname), flavor, type) + Optional.ofNullable(parentHostname), new Flavor(flavor), type) )); return this; } - public FailureScenario withNode(NodeType type, Flavor flavor, String hostname) { + public FailureScenario withNode(NodeType type, NodeResources flavor, String hostname) { return withNode(type, flavor, hostname, null); } @@ -316,13 +321,13 @@ public class FailedExpirerTest { return allocate(clusterType, defaultFlavor, hostname); } - public FailureScenario allocate(ClusterSpec.Type clusterType, Flavor flavor, String... hostname) { + public FailureScenario allocate(ClusterSpec.Type clusterType, NodeResources flavor, String... hostname) { ClusterSpec clusterSpec = ClusterSpec.request(clusterType, ClusterSpec.Id.from("test"), Version.fromString("6.42"), false, Collections.emptySet()); - Capacity capacity = Capacity.fromNodeCount(hostname.length, Optional.of(flavor.name()), false, true); + Capacity capacity = Capacity.fromCount(hostname.length, Optional.of(flavor), false, true); return allocate(applicationId, clusterSpec, capacity); } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirerTest.java index 9f251eea9bf..d7942cdb6e7 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/LoadBalancerExpirerTest.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.provision.maintenance; import com.yahoo.component.Vtag; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.HostSpec; import com.yahoo.transaction.NestedTransaction; import com.yahoo.vespa.hosted.provision.lb.LoadBalancer; @@ -76,11 +77,11 @@ public class LoadBalancerExpirerTest { } private void deployApplication(ApplicationId application, ClusterSpec.Id cluster) { - tester.makeReadyNodes(10, "default"); + tester.makeReadyNodes(10, "d-1-1-1"); List<HostSpec> hosts = tester.prepare(application, ClusterSpec.request(ClusterSpec.Type.container, cluster, Vtag.currentVersion, false, Collections.emptySet()), 2, 1, - "default"); + new NodeResources(1, 1, 1)); tester.activate(application, hosts); } 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 883da687d8d..1b109d5b080 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 @@ -7,6 +7,7 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.NodeFlavors; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.Zone; import com.yahoo.jdisc.Metric; @@ -118,16 +119,19 @@ public class MetricsReporterTest { // Allow 4 containers Set<String> ipAddressPool = ImmutableSet.of("::2", "::3", "::4", "::5"); - Node dockerHost = Node.create("openStackId1", Collections.singleton("::1"), ipAddressPool, "dockerHost", Optional.empty(), Optional.empty(), nodeFlavors.getFlavorOrThrow("host"), NodeType.host); + Node dockerHost = Node.create("openStackId1", Collections.singleton("::1"), ipAddressPool, "dockerHost", + Optional.empty(), Optional.empty(), nodeFlavors.getFlavorOrThrow("host"), NodeType.host); nodeRepository.addNodes(Collections.singletonList(dockerHost)); nodeRepository.dirtyRecursively("dockerHost", Agent.system, getClass().getSimpleName()); nodeRepository.setReady("dockerHost", Agent.system, getClass().getSimpleName()); - Node container1 = Node.createDockerNode(Collections.singleton("::2"), Collections.emptySet(), "container1", Optional.of("dockerHost"), nodeFlavors.getFlavorOrThrow("docker"), NodeType.tenant); + Node container1 = Node.createDockerNode(Collections.singleton("::2"), Collections.emptySet(), "container1", + Optional.of("dockerHost"), new NodeResources(1, 3, 2), NodeType.tenant); container1 = container1.with(allocation(Optional.of("app1")).get()); nodeRepository.addDockerNodes(Collections.singletonList(container1), nodeRepository.lockAllocation()); - Node container2 = Node.createDockerNode(Collections.singleton("::3"), Collections.emptySet(), "container2", Optional.of("dockerHost"), nodeFlavors.getFlavorOrThrow("docker2"), NodeType.tenant); + Node container2 = Node.createDockerNode(Collections.singleton("::3"), Collections.emptySet(), "container2", + Optional.of("dockerHost"), new NodeResources(2, 4, 4), NodeType.tenant); container2 = container2.with(allocation(Optional.of("app2")).get()); nodeRepository.addDockerNodes(Collections.singletonList(container2), nodeRepository.lockAllocation()); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java index 0ead179e0ea..5e76ebff9b2 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java @@ -10,6 +10,7 @@ import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.Flavor; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.NodeFlavors; @@ -128,7 +129,7 @@ public class NodeFailTester { List<Node> hosts = tester.createHostNodes(numberOfHosts); for (int i = 0; i < hosts.size(); i++) { tester.createReadyNodes(nodesPerHost, i * nodesPerHost, Optional.of("parent" + i), - nodeFlavors.getFlavorOrThrow("docker"), NodeType.tenant); + nodeFlavors.getFlavorOrThrow("d-1-1-1"), NodeType.tenant); } // Create applications @@ -136,8 +137,8 @@ public class NodeFailTester { ClusterSpec clusterApp1 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("test"), Version.fromString("6.75.0"), false, Collections.emptySet()); ClusterSpec clusterApp2 = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("test"), Version.fromString("6.75.0"), false, Collections.emptySet()); Capacity allHosts = Capacity.fromRequiredNodeType(NodeType.host); - Capacity capacity1 = Capacity.fromNodeCount(3, Optional.of("docker"), false, true); - Capacity capacity2 = Capacity.fromNodeCount(5, Optional.of("docker"), false, true); + Capacity capacity1 = Capacity.fromCount(3, new NodeResources(1, 1, 1), false, true); + Capacity capacity2 = Capacity.fromCount(5, new NodeResources(1, 1, 1), false, true); tester.activate(nodeAdminApp, clusterNodeAdminApp, allHosts); tester.activate(app1, clusterApp1, capacity1); tester.activate(app2, clusterApp2, capacity2); 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 a55211a112a..9d61f5414d6 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 @@ -6,6 +6,7 @@ import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.NodeType; import com.yahoo.vespa.hosted.provision.Node; @@ -15,6 +16,7 @@ import org.junit.Test; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -36,26 +38,26 @@ public class AclProvisioningTest { @Test public void trusted_nodes_for_allocated_node() { - List<Node> configServers = tester.makeConfigServers(3, "default", Version.fromString("6.123.456")); + List<Node> configServers = tester.makeConfigServers(3, "d-1-1-1", Version.fromString("6.123.456")); // Populate repo - tester.makeReadyNodes(10, "default"); - List<Node> dockerHost = tester.makeReadyNodes(1, "default", NodeType.host); + tester.makeReadyNodes(10, "d-1-1-1"); + List<Node> dockerHost = tester.makeReadyNodes(1, "d-1-1-1", NodeType.host); ApplicationId zoneApplication = tester.makeApplicationId(); deploy(zoneApplication, Capacity.fromRequiredNodeType(NodeType.host)); - tester.makeReadyVirtualDockerNodes(1, "default", dockerHost.get(0).hostname()); - List<Node> proxyNodes = tester.makeReadyNodes(3, "default", NodeType.proxy); + tester.makeReadyVirtualDockerNodes(1, NodeResources.fromLegacyName("d-1-1-1"), dockerHost.get(0).hostname()); + List<Node> proxyNodes = tester.makeReadyNodes(3, "d-1-1-1", NodeType.proxy); // Allocate 2 nodes ApplicationId application = tester.makeApplicationId(); - List<Node> activeNodes = deploy(application, 2); + List<Node> activeNodes = deploy(application, Capacity.fromCount(2, NodeResources.fromLegacyName("d-1-1-1"), false, true)); assertEquals(2, activeNodes.size()); // Get trusted nodes for the first active node Node node = activeNodes.get(0); Supplier<List<NodeAcl>> nodeAcls = () -> tester.nodeRepository().getNodeAcls(node, false); - // Trusted nodes is active nodes in same application, proxy nodes and config servers + // Trusted nodes are active nodes in same application, proxy nodes and config servers assertAcls(Arrays.asList(activeNodes, proxyNodes, configServers, dockerHost), ImmutableSet.of("10.2.3.0/24", "10.4.5.0/24"), nodeAcls.get()); @@ -130,7 +132,7 @@ public class AclProvisioningTest { // Populate repo List<Node> dockerHostNodes = tester.makeReadyNodes(2, "default", NodeType.host); Node dockerHostNodeUnderTest = dockerHostNodes.get(0); - List<Node> dockerNodes = tester.makeReadyVirtualDockerNodes(5, "dockerSmall", + List<Node> dockerNodes = tester.makeReadyVirtualDockerNodes(5, new NodeResources(1, 1, 1), dockerHostNodeUnderTest.hostname()); List<NodeAcl> acls = tester.nodeRepository().getNodeAcls(dockerHostNodeUnderTest, true); @@ -211,12 +213,16 @@ public class AclProvisioningTest { } private static void assertAcls(List<List<Node>> expectedNodes, Set<String> expectedNetworks, List<NodeAcl> actual) { - Set<Node> expectedTrustedNodes = expectedNodes.stream() + List<Node> expectedTrustedNodes = expectedNodes.stream() .flatMap(Collection::stream) - .collect(Collectors.toSet()); - Set<Node> actualTrustedNodes = actual.stream() + .distinct() + .sorted(Comparator.comparing(Node::hostname)) + .collect(Collectors.toList()); + List<Node> actualTrustedNodes = actual.stream() .flatMap(acl -> acl.trustedNodes().stream()) - .collect(Collectors.toSet()); + .distinct() + .sorted(Comparator.comparing(Node::hostname)) + .collect(Collectors.toList()); assertEquals(expectedTrustedNodes, actualTrustedNodes); Set<String> actualTrustedNetworks = actual.stream() 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 78fbca554f0..242bb7df146 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 @@ -118,7 +118,6 @@ public class AllocationSimulator { public void addCluster(String task, int count, Flavor flavor, String id) { // TODO: Implement - NodeSpec.CountNodeSpec nodeSpec = new NodeSpec.CountNodeSpec(count, flavor, false, true); nodes = new NodeList(nodes.asList()); } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerHostCapacityTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerHostCapacityTest.java index 4ab9beacf15..9e61cbe98ab 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerHostCapacityTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DockerHostCapacityTest.java @@ -97,17 +97,17 @@ public class DockerHostCapacityTest { @Test public void getCapacityTotal() { ResourceCapacity total = capacity.getCapacityTotal(); - assertEquals(21.0, total.getCpu(), 0.1); - assertEquals(30.0, total.getMemory(), 0.1); - assertEquals(36.0, total.getDisk(), 0.1); + assertEquals(21.0, total.vcpu(), 0.1); + assertEquals(30.0, total.memoryGb(), 0.1); + assertEquals(36.0, total.diskGb(), 0.1); } @Test public void getFreeCapacityTotal() { ResourceCapacity totalFree = capacity.getFreeCapacityTotal(); - assertEquals(15.0, totalFree.getCpu(), 0.1); - assertEquals(14.0, totalFree.getMemory(), 0.1); - assertEquals(24.0, totalFree.getDisk(), 0.1); + assertEquals(15.0, totalFree.vcpu(), 0.1); + assertEquals(14.0, totalFree.memoryGb(), 0.1); + assertEquals(24.0, totalFree.diskGb(), 0.1); } @Test 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 0fdf857e97f..003593a7d1d 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 @@ -7,6 +7,7 @@ import com.yahoo.config.provision.ApplicationName; import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.NodeType; @@ -36,7 +37,7 @@ import static org.junit.Assert.fail; */ public class DockerProvisioningTest { - private static final String dockerFlavor = "dockerSmall"; + private static final NodeResources dockerFlavor = new NodeResources(1, 1, 1); @Test public void docker_application_deployment() { @@ -55,7 +56,7 @@ public class DockerProvisioningTest { NodeList nodes = tester.getNodes(application1, Node.State.active); assertEquals(nodeCount, nodes.size()); - assertEquals(dockerFlavor, nodes.asList().get(0).flavor().canonicalName()); + assertEquals(dockerFlavor, nodes.asList().get(0).flavor().resources()); // Upgrade Vespa version on nodes Version upgradedWantedVespaVersion = Version.fromString("6.40"); @@ -65,7 +66,7 @@ public class DockerProvisioningTest { tester.activate(application1, new HashSet<>(upgradedHosts)); NodeList upgradedNodes = tester.getNodes(application1, Node.State.active); assertEquals(nodeCount, upgradedNodes.size()); - assertEquals(dockerFlavor, upgradedNodes.asList().get(0).flavor().canonicalName()); + assertEquals(dockerFlavor, upgradedNodes.asList().get(0).flavor().resources()); assertEquals(hosts, upgradedHosts); } @@ -74,7 +75,7 @@ public class DockerProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); ApplicationId zoneApplication = tester.makeApplicationId(); - List<Node> parents = tester.makeReadyVirtualDockerHosts(10, "large"); + List<Node> parents = tester.makeReadyVirtualDockerHosts(10, new NodeResources(2, 2, 2)); for (Node parent : parents) tester.makeReadyVirtualDockerNodes(1, dockerFlavor, parent.hostname()); @@ -204,7 +205,7 @@ public class DockerProvisioningTest { } catch (Exception e) { assertEquals("No room for 3 nodes as 2 of 4 hosts are exclusive", - "Could not satisfy request for 3 nodes of flavor 'dockerSmall' for container cluster 'myContainer' group 0 6.39 in tenant1.app1: Not enough nodes available due to host exclusivity constraints.", + "Could not satisfy request for 3 nodes with [vcpu: 1.0, memory: 1.0 Gb, disk 1.0 Gb] for container cluster 'myContainer' group 0 6.39 in tenant1.app1: Not enough nodes available due to host exclusivity constraints.", e.getMessage()); } @@ -225,7 +226,7 @@ public class DockerProvisioningTest { NodeList nodes = tester.getNodes(application1, Node.State.active); assertEquals(1, nodes.size()); - assertEquals(dockerFlavor, nodes.asList().get(0).flavor().canonicalName()); + assertEquals("[vcpu: 1.0, memory: 1.0 Gb, disk 1.0 Gb]", nodes.asList().get(0).flavor().canonicalName()); } private Set<String> hostsOf(NodeList nodes) { @@ -235,7 +236,7 @@ public class DockerProvisioningTest { private void prepareAndActivate(ApplicationId application, int nodeCount, boolean exclusive, ProvisioningTester tester) { Set<HostSpec> hosts = new HashSet<>(tester.prepare(application, ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContainer"), Version.fromString("6.39"), exclusive, Collections.emptySet()), - Capacity.fromNodeCount(nodeCount, Optional.of(dockerFlavor), false, true), + Capacity.fromCount(nodeCount, Optional.of(dockerFlavor), false, true), 1)); tester.activate(application, hosts); } 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 74541677714..76425fb5d4a 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 @@ -9,6 +9,7 @@ import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.Flavor; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.OutOfCapacityException; @@ -64,7 +65,7 @@ public class DynamicDockerAllocationTest { tester.makeReadyNodes(4, "host-small", NodeType.host, 32); deployZoneApp(tester); List<Node> dockerHosts = tester.nodeRepository().getNodes(NodeType.host, Node.State.active); - Flavor flavor = tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("d-1"); + NodeResources flavor = new NodeResources(1, 1, 1); // Application 1 ApplicationId application1 = makeApplicationId("t1", "a1"); @@ -107,7 +108,7 @@ public class DynamicDockerAllocationTest { tester.makeReadyNodes(5, "host-small", NodeType.host, 32); deployZoneApp(tester); List<Node> dockerHosts = tester.nodeRepository().getNodes(NodeType.host, Node.State.active); - Flavor flavor = tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("d-1"); + NodeResources flavor = new NodeResources(1, 1, 1); // Application 1 ApplicationId application1 = makeApplicationId("t1", "a1"); @@ -164,7 +165,7 @@ public class DynamicDockerAllocationTest { tester.makeReadyNodes(2, "host-small", NodeType.host, 32); deployZoneApp(tester); List<Node> dockerHosts = tester.nodeRepository().getNodes(NodeType.host, Node.State.active); - Flavor flavor = tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("d-1"); + NodeResources flavor = new NodeResources(1, 1, 1); // Application 1 ApplicationId application1 = makeApplicationId("t1", "a1"); @@ -190,11 +191,10 @@ public class DynamicDockerAllocationTest { 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); deployZoneApp(tester); - Flavor flavor = tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("d-1"); //Deploy an application having 6 nodes (3 nodes in 2 groups). We only have 5 docker hosts available ApplicationId application1 = tester.makeApplicationId(); - tester.prepare(application1, clusterSpec("myContent.t1.a1"), 6, 2, flavor.canonicalName()); + tester.prepare(application1, clusterSpec("myContent.t1.a1"), 6, 2, new NodeResources(1, 1, 1)); fail("Two groups have been allocated to the same parent host"); } @@ -212,27 +212,27 @@ public class DynamicDockerAllocationTest { ApplicationId application1 = tester.makeApplicationId(); tester.makeReadyNodes(5, "host-small", NodeType.host, 32); deployZoneApp(tester); - Flavor flavor = tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("d-3"); + NodeResources flavor = new NodeResources(1, 1, 1); // Deploy initial state (can max deploy 3 nodes due to redundancy requirements) ClusterSpec clusterSpec = clusterSpec("myContent.t1.a1"); - List<HostSpec> hosts = tester.prepare(application1, clusterSpec, 3, 1, flavor.canonicalName()); + List<HostSpec> hosts = tester.prepare(application1, clusterSpec, 3, 1, flavor); tester.activate(application1, ImmutableSet.copyOf(hosts)); DockerHostCapacity capacity = new DockerHostCapacity(tester.nodeRepository().getNodes(Node.State.values())); - assertThat(capacity.freeCapacityInFlavorEquivalence(flavor), greaterThan(0)); + assertThat(capacity.freeCapacityInFlavorEquivalence(new Flavor(flavor)), greaterThan(0)); List<Node> initialSpareCapacity = findSpareCapacity(tester); assertThat(initialSpareCapacity.size(), is(2)); try { - hosts = tester.prepare(application1, clusterSpec, 4, 1, flavor.canonicalName()); + hosts = tester.prepare(application1, clusterSpec, 4, 1, flavor); fail("Was able to deploy with 4 nodes, should not be able to use spare capacity"); } catch (OutOfCapacityException e) { } tester.fail(hosts.get(0)); - hosts = tester.prepare(application1, clusterSpec, 3, 1, flavor.canonicalName()); + hosts = tester.prepare(application1, clusterSpec, 3, 1, flavor); tester.activate(application1, ImmutableSet.copyOf(hosts)); List<Node> finalSpareCapacity = findSpareCapacity(tester); @@ -245,10 +245,8 @@ public class DynamicDockerAllocationTest { 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); deployZoneApp(tester); - Flavor flavor = tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("d-3"); - ApplicationId application1 = tester.makeApplicationId(); - List<HostSpec> hosts = tester.prepare(application1, clusterSpec("myContent.t1.a1"), 3, 1, flavor.canonicalName()); + List<HostSpec> hosts = tester.prepare(application1, clusterSpec("myContent.t1.a1"), 3, 1, new NodeResources(1, 1, 1)); tester.activate(application1, ImmutableSet.copyOf(hosts)); List<Node> initialSpareCapacity = findSpareCapacity(tester); @@ -262,8 +260,7 @@ public class DynamicDockerAllocationTest { deployZoneApp(tester); ApplicationId application = tester.makeApplicationId(); - Flavor flavor = tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("d-3"); - tester.prepare(application, clusterSpec("myContent.t2.a2"), 2, 1, flavor.canonicalName()); + tester.prepare(application, clusterSpec("myContent.t2.a2"), 2, 1, new NodeResources(1, 1, 1)); } @Test @@ -273,8 +270,7 @@ public class DynamicDockerAllocationTest { deployZoneApp(tester); ApplicationId application = tester.makeApplicationId(); - Flavor flavor = tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("d-3"); - List<HostSpec> hosts = tester.prepare(application, clusterSpec("myContent.t1.a1"), 2, 1, flavor.canonicalName()); + List<HostSpec> hosts = tester.prepare(application, clusterSpec("myContent.t1.a1"), 2, 1, new NodeResources(1, 1, 1)); tester.activate(application, hosts); List<Node> activeNodes = tester.nodeRepository().getNodes(application); @@ -286,13 +282,13 @@ public class DynamicDockerAllocationTest { return ApplicationId.from(tenant, appName, "default"); } - private void deployApp(ApplicationId id, ClusterSpec spec, Flavor flavor, ProvisioningTester tester, int nodeCount) { - List<HostSpec> hostSpec = tester.prepare(id, spec, nodeCount, 1, flavor.canonicalName()); + private void deployApp(ApplicationId id, ClusterSpec spec, NodeResources flavor, ProvisioningTester tester, int nodeCount) { + List<HostSpec> hostSpec = tester.prepare(id, spec, nodeCount, 1, flavor); tester.activate(id, new HashSet<>(hostSpec)); } - private void addAndAssignNode(ApplicationId id, String hostname, String parentHostname, ClusterSpec clusterSpec, Flavor flavor, int index, ProvisioningTester tester) { - Node node1a = Node.create("open1", Collections.singleton("127.0.0.100"), new HashSet<>(), hostname, Optional.of(parentHostname), Optional.empty(), flavor, NodeType.tenant); + private void addAndAssignNode(ApplicationId id, String hostname, String parentHostname, ClusterSpec clusterSpec, NodeResources flavor, int index, ProvisioningTester tester) { + Node node1a = Node.create("open1", Collections.singleton("127.0.0.100"), new HashSet<>(), hostname, Optional.of(parentHostname), Optional.empty(), new Flavor(flavor), NodeType.tenant); 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 Node node1aAllocation = node1a.allocate(id, clusterMembership1, Instant.now()); 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 60e9289b9bf..121819700ea 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 @@ -7,6 +7,7 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Flavor; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.NodeType; import com.yahoo.vespa.flags.Flags; @@ -44,10 +45,10 @@ public class DynamicDockerProvisionTest { assertEquals(0, tester.nodeRepository().list().size()); ApplicationId application1 = tester.makeApplicationId(); - Flavor flavor = tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("dockerSmall"); + NodeResources flavor = new NodeResources(1, 1, 1); mockHostProvisioner(hostProvisioner, tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("small")); - List<HostSpec> hostSpec = tester.prepare(application1, clusterSpec("myContent.t1.a1"), 4, 1, flavor.canonicalName()); + List<HostSpec> hostSpec = tester.prepare(application1, clusterSpec("myContent.t1.a1"), 4, 1, flavor); verify(hostProvisioner).provisionHosts(List.of(100, 101, 102, 103), flavor); // Total of 8 nodes should now be in node-repo, 4 hosts in state provisioned, and 4 reserved nodes @@ -64,21 +65,21 @@ public class DynamicDockerProvisionTest { deployZoneApp(tester); ApplicationId application = tester.makeApplicationId(); - Flavor flavor = tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("dockerSmall"); + NodeResources flavor = new NodeResources(1, 1, 1); mockHostProvisioner(hostProvisioner, tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("small")); - tester.prepare(application, clusterSpec("myContent.t2.a2"), 2, 1, flavor.canonicalName()); + tester.prepare(application, clusterSpec("myContent.t2.a2"), 2, 1, flavor); verify(hostProvisioner).provisionHosts(List.of(100, 101), flavor); } @Test public void allocates_to_hosts_already_hosting_nodes_by_this_tenant() { ApplicationId application = tester.makeApplicationId(); - Flavor flavor = tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("dockerSmall"); + NodeResources flavor = new NodeResources(1, 1, 1); List<Integer> expectedProvisionIndexes = List.of(100, 101); mockHostProvisioner(hostProvisioner, tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("large")); - tester.prepare(application, clusterSpec("myContent.t2.a2"), 2, 1, flavor.canonicalName()); + tester.prepare(application, clusterSpec("myContent.t2.a2"), 2, 1, flavor); verify(hostProvisioner).provisionHosts(expectedProvisionIndexes, flavor); // Ready the provisioned hosts, add an IP addreses to pool and activate them @@ -92,7 +93,7 @@ public class DynamicDockerProvisionTest { deployZoneApp(tester); mockHostProvisioner(hostProvisioner, tester.nodeRepository().getAvailableFlavors().getFlavorOrThrow("small")); - tester.prepare(application, clusterSpec("another-id"), 2, 1, flavor.canonicalName()); + tester.prepare(application, clusterSpec("another-id"), 2, 1, flavor); // Verify there was only 1 call to provision hosts (during the first prepare) verify(hostProvisioner).provisionHosts(any(), any()); @@ -124,9 +125,9 @@ public class DynamicDockerProvisionTest { private static void mockHostProvisioner(HostProvisioner hostProvisioner, Flavor hostFlavor) { doAnswer(invocation -> { List<Integer> provisionIndexes = (List<Integer>) invocation.getArguments()[0]; - Flavor nodeFlavor = (Flavor) invocation.getArguments()[1]; + NodeResources nodeResources = (NodeResources) invocation.getArguments()[1]; return provisionIndexes.stream() - .map(i -> new ProvisionedHost("id-" + i, "host-" + i, hostFlavor, "host-" + i + "-1", nodeFlavor)) + .map(i -> new ProvisionedHost("id-" + i, "host-" + i, hostFlavor, "host-" + i + "-1", nodeResources)) .collect(Collectors.toList()); }).when(hostProvisioner).provisionHosts(any(), any()); } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java index 4f7e09d0bd7..58c0b3ed9cc 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/LoadBalancerProvisionerTest.java @@ -5,6 +5,7 @@ import com.google.common.collect.Iterators; import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.HostName; import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.RotationName; @@ -136,10 +137,10 @@ public class LoadBalancerProvisionerTest { } private Set<HostSpec> prepare(ApplicationId application, ClusterSpec... specs) { - tester.makeReadyNodes(specs.length * 2, "default"); + tester.makeReadyNodes(specs.length * 2, "d-1-1-1"); Set<HostSpec> allNodes = new LinkedHashSet<>(); for (ClusterSpec spec : specs) { - allNodes.addAll(tester.prepare(application, spec, 2, 1, "default")); + allNodes.addAll(tester.prepare(application, spec, 2, 1, new NodeResources(1, 1, 1))); } return allNodes; } 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 b266f546b49..14720d4215c 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 @@ -149,6 +149,8 @@ public class MultigroupProvisioningTest { private void deploy(ApplicationId application, int nodeCount, int groupCount, ProvisioningTester tester) { deploy(application, Capacity.fromNodeCount(nodeCount, Optional.of("default"), false, true), groupCount, tester); } + + @SuppressWarnings("deprecation") // TODO: Remove private void deploy(ApplicationId application, Capacity capacity, int wantedGroups, ProvisioningTester tester) { int nodeCount = capacity.nodeCount(); String flavor = capacity.flavor().get(); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizerTest.java index f7caea060ad..b2b80c6f4e4 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizerTest.java @@ -7,6 +7,7 @@ import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Flavor; import com.yahoo.config.provision.NodeFlavors; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.NodeType; import com.yahoo.config.provisioning.FlavorsConfig; import com.yahoo.vespa.hosted.provision.Node; @@ -31,20 +32,20 @@ public class NodePrioritizerTest { public void relocated_nodes_are_preferred() { List<Node> nodes = new ArrayList<>(); Node parent = createParent("parent"); - Node b = createNode(parent, "b", "d2"); + Node b = createNode(parent, "b", new NodeResources(2, 2, 2)); nodes.add(b); // Only one node - should be obvious what to prefer Assert.assertTrue(NodePrioritizer.isPreferredNodeToBeRelocated(nodes, b, parent)); // Two equal nodes - choose lexically - Node a = createNode(parent, "a", "d2"); + Node a = createNode(parent, "a", new NodeResources(2, 2, 2)); nodes.add(a); Assert.assertTrue(NodePrioritizer.isPreferredNodeToBeRelocated(nodes, a, parent)); Assert.assertFalse(NodePrioritizer.isPreferredNodeToBeRelocated(nodes, b, parent)); // Smallest node should be preferred - Node c = createNode(parent, "c", "d1"); + Node c = createNode(parent, "c", new NodeResources(1, 1, 1)); nodes.add(c); Assert.assertTrue(NodePrioritizer.isPreferredNodeToBeRelocated(nodes, c, parent)); @@ -53,7 +54,7 @@ public class NodePrioritizerTest { c = c.allocate(ApplicationId.defaultId(), ClusterMembership.from(spec, 0), Instant.now()); nodes.remove(c); nodes.add(c); - Node d = createNode(parent, "d", "d1"); + Node d = createNode(parent, "d", new NodeResources(1, 1, 1)); nodes.add(d); Assert.assertTrue(NodePrioritizer.isPreferredNodeToBeRelocated(nodes, d, parent)); Assert.assertFalse(NodePrioritizer.isPreferredNodeToBeRelocated(nodes, c, parent)); @@ -67,9 +68,9 @@ public class NodePrioritizerTest { Assert.assertTrue(NodePrioritizer.isPreferredNodeToBeRelocated(nodes, d, parent)); } - private static Node createNode(Node parent, String hostname, String flavor) { + private static Node createNode(Node parent, String hostname, NodeResources resources) { return Node.createDockerNode(Collections.singleton("127.0.0.1"), new HashSet<>(), hostname, Optional.of(parent.hostname()), - flavors.getFlavorOrThrow(flavor), NodeType.tenant); + resources, NodeType.tenant); } private static Node createParent(String hostname) { @@ -80,8 +81,6 @@ public class NodePrioritizerTest { private static FlavorsConfig flavorsConfig() { FlavorConfigBuilder b = new FlavorConfigBuilder(); b.addFlavor("host-large", 6., 6., 6, Flavor.Type.BARE_METAL); - b.addFlavor("d1", 1, 1., 1, Flavor.Type.DOCKER_CONTAINER); - b.addFlavor("d2", 2, 2., 2, Flavor.Type.DOCKER_CONTAINER); return b.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 f1caa52a1b5..2c237d94715 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 @@ -10,10 +10,12 @@ import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.Flavor; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.HostFilter; import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.NodeFlavors; +import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.OutOfCapacityException; import com.yahoo.config.provision.RegionName; import com.yahoo.config.provision.TenantName; @@ -65,37 +67,37 @@ public class ProvisioningTest { ApplicationId application1 = tester.makeApplicationId(); ApplicationId application2 = tester.makeApplicationId(); - tester.makeReadyNodes(21, "default"); + tester.makeReadyNodes(21, "d-1-1-1"); // deploy - SystemState state1 = prepare(application1, 2, 2, 3, 3, "default", tester); + SystemState state1 = prepare(application1, 2, 2, 3, 3, new NodeResources(1, 1, 1), tester); tester.activate(application1, state1.allHosts); // redeploy - SystemState state2 = prepare(application1, 2, 2, 3, 3, "default", tester); + SystemState state2 = prepare(application1, 2, 2, 3, 3, new NodeResources(1, 1, 1), tester); state2.assertEquals(state1); tester.activate(application1, state2.allHosts); // deploy another application - SystemState state1App2 = prepare(application2, 2, 2, 3, 3, "default", tester); + SystemState state1App2 = prepare(application2, 2, 2, 3, 3, new NodeResources(1, 1, 1), tester); assertFalse("Hosts to different apps are disjunct", state1App2.allHosts.removeAll(state1.allHosts)); tester.activate(application2, state1App2.allHosts); // prepare twice - SystemState state3 = prepare(application1, 2, 2, 3, 3, "default", tester); - SystemState state4 = prepare(application1, 2, 2, 3, 3, "default", tester); + SystemState state3 = prepare(application1, 2, 2, 3, 3, new NodeResources(1, 1, 1), tester); + SystemState state4 = prepare(application1, 2, 2, 3, 3, new NodeResources(1, 1, 1), tester); state3.assertEquals(state2); state4.assertEquals(state3); tester.activate(application1, state4.allHosts); // remove nodes before deploying - SystemState state5 = prepare(application1, 2, 2, 3, 3, "default", tester); + SystemState state5 = prepare(application1, 2, 2, 3, 3, new NodeResources(1, 1, 1), tester); HostSpec removed = tester.removeOne(state5.allHosts); tester.activate(application1, state5.allHosts); assertEquals(removed.hostname(), tester.nodeRepository().getNodes(application1, Node.State.inactive).get(0).hostname()); // remove some of the clusters - SystemState state6 = prepare(application1, 0, 2, 0, 3, "default", tester); + SystemState state6 = prepare(application1, 0, 2, 0, 3, new NodeResources(1, 1, 1), tester); tester.activate(application1, state6.allHosts); assertEquals(5, tester.getNodes(application1, Node.State.active).size()); assertEquals(5, tester.getNodes(application1, Node.State.inactive).size()); @@ -114,14 +116,14 @@ public class ProvisioningTest { HostSpec failed = tester.removeOne(state1App2.allHosts); tester.fail(failed); assertEquals(9, tester.getNodes(application2, Node.State.active).size()); - SystemState state2App2 = prepare(application2, 2, 2, 3, 3, "default", tester); + SystemState state2App2 = prepare(application2, 2, 2, 3, 3, new NodeResources(1, 1, 1), tester); assertFalse("Hosts to different apps are disjunct", state2App2.allHosts.removeAll(state1.allHosts)); assertEquals("A new node was reserved to replace the failed one", 10, state2App2.allHosts.size()); assertFalse("The new host is not the failed one", state2App2.allHosts.contains(failed)); tester.activate(application2, state2App2.allHosts); // deploy first app again - SystemState state7 = prepare(application1, 2, 2, 3, 3, "default", tester); + SystemState state7 = prepare(application1, 2, 2, 3, 3, new NodeResources(1, 1, 1), tester); state7.assertEquals(state1); tester.activate(application1, state7.allHosts); assertEquals(0, tester.getNodes(application1, Node.State.inactive).size()); @@ -145,10 +147,10 @@ public class ProvisioningTest { ApplicationId application1 = tester.makeApplicationId(); - tester.makeReadyNodes(4, "default"); + tester.makeReadyNodes(4, "d-1-1-1"); // deploy - SystemState state1 = prepare(application1, 1, 1, 1, 1, "default", tester); + SystemState state1 = prepare(application1, 1, 1, 1, 1, new NodeResources(1, 1, 1), tester); tester.activate(application1, state1.allHosts); HostSpec host1 = state1.container0.iterator().next(); @@ -157,7 +159,7 @@ public class ProvisioningTest { tester.nodeRepository().write(node1.with(node1.status().withVespaVersion(Version.fromString("1.2.3")))); // redeploy - SystemState state2 = prepare(application1, 1, 1, 1, 1, "default", tester); + SystemState state2 = prepare(application1, 1, 1, 1, 1, new NodeResources(1, 1, 1), tester); tester.activate(application1, state2.allHosts); host1 = state2.container0.iterator().next(); @@ -170,20 +172,20 @@ public class ProvisioningTest { ApplicationId application1 = tester.makeApplicationId(); - tester.makeReadyNodes(24, "default"); + tester.makeReadyNodes(24, "d-1-1-1"); // deploy - SystemState state1 = prepare(application1, 2, 2, 3, 3, "default", tester); + SystemState state1 = prepare(application1, 2, 2, 3, 3, new NodeResources(1, 1, 1), tester); tester.activate(application1, state1.allHosts); // redeploy with increased sizes - SystemState state2 = prepare(application1, 3, 4, 4, 5, "default", tester); + SystemState state2 = prepare(application1, 3, 4, 4, 5, new NodeResources(1, 1, 1), tester); state2.assertExtends(state1); assertEquals("New nodes are reserved", 6, tester.getNodes(application1, Node.State.reserved).size()); tester.activate(application1, state2.allHosts); // decrease again - SystemState state3 = prepare(application1, 2, 2, 3, 3, "default", tester); + SystemState state3 = prepare(application1, 2, 2, 3, 3, new NodeResources(1, 1, 1), tester); tester.activate(application1, state3.allHosts); assertEquals("Superfluous container nodes are deactivated", 3-2 + 4-2, tester.getNodes(application1, Node.State.inactive).size()); @@ -191,7 +193,7 @@ public class ProvisioningTest { 4-3 + 5-3, tester.getNodes(application1, Node.State.active).retired().size()); // increase even more, and remove one node before deploying - SystemState state4 = prepare(application1, 4, 5, 5, 6, "default", tester); + SystemState state4 = prepare(application1, 4, 5, 5, 6, new NodeResources(1, 1, 1), tester); assertEquals("Inactive nodes are reused", 0, tester.getNodes(application1, Node.State.inactive).size()); assertEquals("Earlier retired nodes are not unretired before activate", 4-3 + 5-3, tester.getNodes(application1, Node.State.active).retired().size()); @@ -207,7 +209,7 @@ public class ProvisioningTest { 0, tester.getNodes(application1, Node.State.active).retired().size()); // decrease again - SystemState state5 = prepare(application1, 2, 2, 3, 3, "default", tester); + SystemState state5 = prepare(application1, 2, 2, 3, 3, new NodeResources(1, 1, 1), tester); tester.activate(application1, state5.allHosts); assertEquals("Superfluous container nodes are also deactivated", 4-2 + 5-2 + 1, tester.getNodes(application1, Node.State.inactive).size()); // @@ -215,13 +217,13 @@ public class ProvisioningTest { 5-3 + 6-3 - 1, tester.getNodes(application1, Node.State.active).retired().size()); // increase content slightly - SystemState state6 = prepare(application1, 2, 2, 4, 3, "default", tester); + SystemState state6 = prepare(application1, 2, 2, 4, 3, new NodeResources(1, 1, 1), tester); tester.activate(application1, state6.allHosts); assertEquals("One content node is unretired", 5-4 + 6-3 - 1, tester.getNodes(application1, Node.State.active).retired().size()); // Then reserve more - SystemState state7 = prepare(application1, 8, 2, 2, 2, "default", tester); + SystemState state7 = prepare(application1, 8, 2, 2, 2, new NodeResources(1, 1, 1), tester); // delete app NestedTransaction removeTransaction = new NestedTransaction(); @@ -237,27 +239,30 @@ public class ProvisioningTest { ApplicationId application1 = tester.makeApplicationId(); - tester.makeReadyNodes(12, "small"); - tester.makeReadyNodes(16, "large"); + tester.makeReadyNodes(12, "d-1-1-1"); + tester.makeReadyNodes(16, "d-2-2-2"); + + NodeResources small = new NodeResources(1, 1, 1); + NodeResources large = new NodeResources(2, 2, 2); // deploy - SystemState state1 = prepare(application1, 2, 2, 4, 4, "small", tester); + SystemState state1 = prepare(application1, 2, 2, 4, 4, small, tester); tester.activate(application1, state1.allHosts); // redeploy with reduced size (to cause us to have retired nodes before switching flavor) - SystemState state2 = prepare(application1, 2, 2, 3, 3, "small", tester); + SystemState state2 = prepare(application1, 2, 2, 3, 3, small, tester); tester.activate(application1, state2.allHosts); // redeploy with increased sizes and new flavor - SystemState state3 = prepare(application1, 3, 4, 4, 5, "large", tester); + SystemState state3 = prepare(application1, 3, 4, 4, 5, large, tester); assertEquals("New nodes are reserved", 16, tester.nodeRepository().getNodes(application1, Node.State.reserved).size()); tester.activate(application1, state3.allHosts); - assertEquals("'small' container nodes are retired because we are swapping the entire cluster", - 2 + 2, tester.getNodes(application1, Node.State.active).retired().type(ClusterSpec.Type.container).flavor("small").size()); - assertEquals("'small' content nodes are retired", - 4 + 4, tester.getNodes(application1, Node.State.active).retired().type(ClusterSpec.Type.content).flavor("small").size()); - assertEquals("No 'large' content nodes are retired", - 0, tester.getNodes(application1, Node.State.active).retired().flavor("large").size()); + assertEquals("small container nodes are retired because we are swapping the entire cluster", + 2 + 2, tester.getNodes(application1, Node.State.active).retired().type(ClusterSpec.Type.container).resources(small).size()); + assertEquals("'small content nodes are retired", + 4 + 4, tester.getNodes(application1, Node.State.active).retired().type(ClusterSpec.Type.content).resources(small).size()); + assertEquals("No large content nodes are retired", + 0, tester.getNodes(application1, Node.State.active).retired().resources(large).size()); } // TODO: Enable when this feature is re-enabled @@ -268,14 +273,14 @@ public class ProvisioningTest { ApplicationId application1 = tester.makeApplicationId(); - tester.makeReadyNodes(14, "dockerLarge"); + tester.makeReadyNodes(14, "d-2-2-2", NodeType.host); // deploy - SystemState state1 = prepare(application1, 2, 2, 4, 4, "dockerLarge", tester); + SystemState state1 = prepare(application1, 2, 2, 4, 4, new NodeResources(2, 2, 2), tester); tester.activate(application1, state1.allHosts); // redeploy with smaller docker flavor - causes in-place flavor change - SystemState state2 = prepare(application1, 2, 2, 4, 4, "dockerSmall", tester); + SystemState state2 = prepare(application1, 2, 2, 4, 4, new NodeResources(1, 1, 1), tester); tester.activate(application1, state2.allHosts); assertEquals(12, tester.getNodes(application1, Node.State.active).size()); @@ -320,11 +325,13 @@ public class ProvisioningTest { tester.makeReadyNodes(8, "large-variant"); // deploy with flavor which will be fulfilled by some old and new nodes - SystemState state1 = prepare(application1, 2, 2, 4, 4, "old-large1", tester); + SystemState state1 = prepare(application1, 2, 2, 4, 4, + NodeResources.fromLegacyName("old-large1"), tester); tester.activate(application1, state1.allHosts); // redeploy with increased sizes, this will map to the remaining old/new nodes - SystemState state2 = prepare(application1, 3, 4, 4, 5, "old-large2", tester); + SystemState state2 = prepare(application1, 3, 4, 4, 5, + NodeResources.fromLegacyName("old-large2"), tester); assertEquals("New nodes are reserved", 4, tester.getNodes(application1, Node.State.reserved).size()); tester.activate(application1, state2.allHosts); assertEquals("All nodes are used", @@ -333,12 +340,14 @@ public class ProvisioningTest { 0, tester.getNodes(application1, Node.State.active).retired().size()); // This is a noop as we are already using large nodes and nodes which replace large - SystemState state3 = prepare(application1, 3, 4, 4, 5, "large", tester); + SystemState state3 = prepare(application1, 3, 4, 4, 5, + NodeResources.fromLegacyName("large"), tester); assertEquals("Noop", 0, tester.getNodes(application1, Node.State.reserved).size()); tester.activate(application1, state3.allHosts); try { - SystemState state4 = prepare(application1, 3, 4, 4, 5, "large-variant", tester); + SystemState state4 = prepare(application1, 3, 4, 4, 5, + NodeResources.fromLegacyName("large-variant"), tester); fail("Should fail as we don't have that many large-variant nodes"); } catch (OutOfCapacityException expected) { @@ -346,7 +355,8 @@ public class ProvisioningTest { // make enough nodes to complete the switch to large-variant tester.makeReadyNodes(8, "large-variant"); - SystemState state4 = prepare(application1, 3, 4, 4, 5, "large-variant", tester); + SystemState state4 = prepare(application1, 3, 4, 4, 5, + NodeResources.fromLegacyName("large-variant"), tester); assertEquals("New 'large-variant' nodes are reserved", 8, tester.getNodes(application1, Node.State.reserved).size()); tester.activate(application1, state4.allHosts); // (we can not check for the precise state here without carrying over from earlier as the distribution of @@ -359,22 +369,25 @@ public class ProvisioningTest { ApplicationId application1 = tester.makeApplicationId(); - tester.makeReadyNodes(5, "default"); + tester.makeReadyNodes(5, "d-1-1-1"); // deploy - SystemState state1 = prepare(application1, 2, 0, 3, 0, "default", tester); + SystemState state1 = prepare(application1, 2, 0, 3, 0, + new NodeResources(1, 1, 1), tester); tester.activate(application1, state1.allHosts); // redeploy a too large application try { - SystemState state2 = prepare(application1, 3, 0, 3, 0, "default", tester); + SystemState state2 = prepare(application1, 3, 0, 3, 0, + new NodeResources(1, 1, 1), tester); fail("Expected out of capacity exception"); } catch (OutOfCapacityException expected) { } // deploy first state again - SystemState state3 = prepare(application1, 2, 0, 3, 0, "default", tester); + SystemState state3 = prepare(application1, 2, 0, 3, 0, + new NodeResources(1, 1, 1), tester); tester.activate(application1, state3.allHosts); } @@ -383,8 +396,9 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.dev, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - tester.makeReadyNodes(4, "default"); - SystemState state = prepare(application, 2, 2, 3, 3, "default", tester); + tester.makeReadyNodes(4, "d-1-1-1"); + SystemState state = prepare(application, 2, 2, 3, 3, + new NodeResources(1, 1, 1), tester); assertEquals(4, state.allHosts.size()); tester.activate(application, state.allHosts); } @@ -394,8 +408,8 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.dev, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - tester.makeReadyNodes(4, "default"); - SystemState state = prepare(application, 2, 2, 3, 3, "default", Version.fromString("6.91"), tester); + tester.makeReadyNodes(4, "d-1-1-1"); + SystemState state = prepare(application, 2, 2, 3, 3, new NodeResources(1, 1, 1), Version.fromString("6.91"), tester); assertEquals(4, state.allHosts.size()); tester.activate(application, state.allHosts); } @@ -405,8 +419,9 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.test, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - tester.makeReadyNodes(4, "default"); - SystemState state = prepare(application, 2, 2, 3, 3, "default", tester); + tester.makeReadyNodes(4, "d-1-1-1"); + SystemState state = prepare(application, 2, 2, 3, 3, + new NodeResources(1, 1, 1), tester); assertEquals(4, state.allHosts.size()); tester.activate(application, state.allHosts); } @@ -416,8 +431,9 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - tester.makeReadyNodes(10, "default"); - prepare(application, 1, 2, 3, 3, "default", tester); + tester.makeReadyNodes(10, "d-1-1-1"); + prepare(application, 1, 2, 3, 3, + new NodeResources(1, 1, 1), tester); } /** Dev always uses the zone default flavor */ @@ -426,8 +442,9 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.dev, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - tester.makeReadyNodes(4, "default"); - SystemState state = prepare(application, 2, 2, 3, 3, "large", tester); + tester.makeReadyNodes(4, "d-2-2-2"); + SystemState state = prepare(application, 2, 2, 3, 3, + new NodeResources(2, 2, 2), tester); assertEquals(4, state.allHosts.size()); tester.activate(application, state.allHosts); } @@ -438,8 +455,9 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.test, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - tester.makeReadyNodes(4, "default"); - SystemState state = prepare(application, 2, 2, 3, 3, "large", tester); + tester.makeReadyNodes(4, "d-2-2-2"); + SystemState state = prepare(application, 2, 2, 3, 3, + new NodeResources(2, 2, 2), tester); assertEquals(4, state.allHosts.size()); tester.activate(application, state.allHosts); } @@ -449,8 +467,9 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.staging, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - tester.makeReadyNodes(14, "default"); - SystemState state = prepare(application, 1, 1, 1, 64, "default", tester); // becomes 1, 1, 1, 6 + tester.makeReadyNodes(14, "d-1-1-1"); + SystemState state = prepare(application, 1, 1, 1, 64, + new NodeResources(1, 1, 1), tester); // becomes 1, 1, 1, 6 assertEquals(9, state.allHosts.size()); tester.activate(application, state.allHosts); } @@ -459,9 +478,10 @@ 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.makeReadyNodes(10, "default"); + tester.makeReadyNodes(10, "d-1-1-1"); ApplicationId application = tester.makeApplicationId(); - SystemState state = prepare(application, 2, 2, 3, 3, "default", tester); + SystemState state = prepare(application, 2, 2, 3, 3, + new NodeResources(1, 1, 1), tester); // Simulate expiry NestedTransaction deactivateTransaction = new NestedTransaction(); @@ -481,10 +501,11 @@ public class ProvisioningTest { public void out_of_capacity() { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); - tester.makeReadyNodes(9, "default"); // need 2+2+3+3=10 + tester.makeReadyNodes(9, "d-1-1-1"); // need 2+2+3+3=10 ApplicationId application = tester.makeApplicationId(); try { - prepare(application, 2, 2, 3, 3, "default", tester); + prepare(application, 2, 2, 3, 3, + new NodeResources(1, 1, 1), tester); fail("Expected exception"); } catch (OutOfCapacityException e) { @@ -495,7 +516,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.makeReadyNodes(4, "default"); + tester.makeReadyNodes(4, "d-1-1-1"); ApplicationId application = tester.makeApplicationId(); ClusterSpec cluster = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("music"), @@ -513,11 +534,12 @@ public class ProvisioningTest { tester.makeReadyNodes( 9, "large"); // need 2+2+3+3=10 ApplicationId application = tester.makeApplicationId(); try { - prepare(application, 2, 2, 3, 3, "large", tester); + prepare(application, 2, 2, 3, 3, + NodeResources.fromLegacyName("large"), tester); fail("Expected exception"); } catch (OutOfCapacityException e) { - assertTrue(e.getMessage().startsWith("Could not satisfy request for 3 nodes of flavor 'large'")); + assertTrue(e.getMessage().startsWith("Could not satisfy request for 3 nodes with flavor 'large'")); } } @@ -537,8 +559,8 @@ public class ProvisioningTest { ApplicationId application = tester.makeApplicationId(); try { - prepare(application, 2, 0, 2, 0, flavorToRetire, - tester); + prepare(application, 2, 0, 2, 0, + NodeResources.fromLegacyName(flavorToRetire), tester); fail("Expected exception"); } catch (OutOfCapacityException e) { assertTrue(e.getMessage().startsWith("Could not satisfy request")); @@ -551,11 +573,12 @@ public class ProvisioningTest { ApplicationId application = tester.makeApplicationId(); // Flag all nodes for retirement - List<Node> readyNodes = tester.makeReadyNodes(5, "default"); + List<Node> readyNodes = tester.makeReadyNodes(5, "d-1-1-1"); readyNodes.forEach(node -> tester.patchNode(node.with(node.status().withWantToRetire(true)))); try { - prepare(application, 2, 0, 2, 0, "default", tester); + prepare(application, 2, 0, 2, 0, + new NodeResources(1, 1, 1), tester); fail("Expected exception"); } catch (OutOfCapacityException e) { assertTrue(e.getMessage().startsWith("Could not satisfy request")); @@ -568,7 +591,8 @@ public class ProvisioningTest { ApplicationId application = tester.makeApplicationId(); try { - prepare(application, 2, 2, 3, 3, "nonexisting", tester); + prepare(application, 2, 2, 3, 3, + NodeResources.fromLegacyName("nonexisting"), tester); fail("Expected exception"); } catch (IllegalArgumentException e) { @@ -582,14 +606,14 @@ public class ProvisioningTest { ApplicationId application1 = tester.makeApplicationId(); - tester.makeReadyNodes(14, "default"); + tester.makeReadyNodes(14, "d-1-1-1"); // deploy - SystemState state1 = prepare(application1, 3, 3, 4, 4, "default", tester); + SystemState state1 = prepare(application1, 3, 3, 4, 4, new NodeResources(1, 1, 1), tester); tester.activate(application1, state1.allHosts); // decrease cluster sizes - SystemState state2 = prepare(application1, 2, 2, 2, 2, "default", tester); + SystemState state2 = prepare(application1, 2, 2, 2, 2, new NodeResources(1, 1, 1), tester); tester.activate(application1, state2.allHosts); // content0 @@ -607,12 +631,12 @@ public class ProvisioningTest { @Test public void application_deployment_prefers_cheapest_stock_nodes() { - assertCorrectFlavorPreferences(true); + assertCorrectBareMetalFlavorPreferences(true); } @Test public void application_deployment_prefers_exact_nonstock_nodes() { - assertCorrectFlavorPreferences(false); + assertCorrectBareMetalFlavorPreferences(false); } @Test @@ -635,7 +659,7 @@ public class ProvisioningTest { .flavorsConfig(b.build()).curator(curator).nameResolver(nameResolver).build(); tester.makeReadyNodes(4, flavorToRetire); SystemState state = prepare(application, 2, 0, 2, 0, - flavorToRetire, tester); + NodeResources.fromLegacyName(flavorToRetire), tester); tester.activate(application, state.allHosts); } @@ -655,7 +679,7 @@ public class ProvisioningTest { tester.makeReadyNodes(4, replacementFlavor); SystemState state = prepare(application, 2, 0, 2, 0, - flavorToRetire, tester); + NodeResources.fromLegacyName(flavorToRetire), tester); tester.activate(application, state.allHosts); @@ -686,7 +710,7 @@ public class ProvisioningTest { tester.makeReadyNodes(4, replacementFlavor); SystemState state = prepare(application, 2, 0, 2, 0, - flavorToRetire, tester); + NodeResources.fromLegacyName(flavorToRetire), tester); tester.activate(application, state.allHosts); @@ -700,12 +724,12 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - tester.makeReadyNodes(10, "default"); + tester.makeReadyNodes(10, "d-1-1-1"); // Deploy application { SystemState state = prepare(application, 2, 0, 2, 0, - "default", tester); + new NodeResources(1, 1, 1), tester); tester.activate(application, state.allHosts); assertEquals(4, tester.getNodes(application, Node.State.active).size()); } @@ -715,7 +739,7 @@ public class ProvisioningTest { List<Node> nodesToRetire = tester.getNodes(application, Node.State.active).asList().subList(0, 2); nodesToRetire.forEach(node -> tester.patchNode(node.with(node.status().withWantToRetire(true)))); - SystemState state = prepare(application, 2, 0, 2, 0, "default", tester); + SystemState state = prepare(application, 2, 0, 2, 0, new NodeResources(1, 1, 1), tester); tester.activate(application, state.allHosts); List<Node> retiredNodes = tester.getNodes(application).retired().asList(); @@ -729,24 +753,24 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); - tester.makeReadyNodes(2, "default"); + tester.makeReadyNodes(2, "d-1-1-1"); // Deploy fails with out of capacity try { prepare(application, 2, 0, 2, 0, - "default", tester); + new NodeResources(1, 1, 1), tester); fail("Expected exception"); } catch (OutOfCapacityException ignored) {} assertEquals("Reserved a subset of required nodes", 2, tester.getNodes(application, Node.State.reserved).size()); // Enough nodes become available - tester.makeReadyNodes(2, "default"); + tester.makeReadyNodes(2, "d-1-1-1"); // Deploy is retried after a few minutes tester.clock().advance(Duration.ofMinutes(2)); SystemState state = prepare(application, 2, 0, 2, 0, - "default", tester); + new NodeResources(1, 1, 1), tester); List<Node> reserved = tester.getNodes(application, Node.State.reserved).asList(); assertEquals("Reserved required nodes", 4, reserved.size()); assertTrue("Time of event is updated for all nodes", @@ -772,12 +796,12 @@ public class ProvisioningTest { ProvisioningTester tester = new ProvisioningTester.Builder().zone(new Zone(Environment.prod, RegionName.from("us-east"))).build(); ApplicationId application = tester.makeApplicationId(); try { - prepare(application, 1, 0, 1, 0, true, "default", Version.fromString("6.42"), tester); + prepare(application, 1, 0, 1, 0, true, new NodeResources(1, 1, 1), Version.fromString("6.42"), tester); fail("Expected exception"); } catch (IllegalArgumentException ignored) {} } - private void assertCorrectFlavorPreferences(boolean largeIsStock) { + private void assertCorrectBareMetalFlavorPreferences(boolean largeIsStock) { FlavorConfigBuilder b = new FlavorConfigBuilder(); b.addFlavor("large", 4., 8., 100, Flavor.Type.BARE_METAL).cost(10).stock(largeIsStock); FlavorsConfig.Flavor.Builder largeVariant = b.addFlavor("large-variant", 3., 9., 101, Flavor.Type.BARE_METAL).cost(9); @@ -795,8 +819,10 @@ public class ProvisioningTest { ClusterSpec contentClusterSpec = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.42"), false, Collections.emptySet()); ClusterSpec containerClusterSpec = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContainer"), Version.fromString("6.42"), false, Collections.emptySet()); - List<HostSpec> containerNodes = tester.prepare(applicationId, containerClusterSpec, 5, 1, "large"); - List<HostSpec> contentNodes = tester.prepare(applicationId, contentClusterSpec, 10, 1, "large"); + List<HostSpec> containerNodes = tester.prepare(applicationId, containerClusterSpec, 5, 1, + NodeResources.fromLegacyName("large")); + List<HostSpec> contentNodes = tester.prepare(applicationId, contentClusterSpec, 10, 1, + NodeResources.fromLegacyName("large")); if (largeIsStock) { // 'large' is replaced by 'large-variant' when possible, as it is cheaper tester.assertNumberOfNodesWithFlavor(containerNodes, "large-variant", 5); @@ -813,19 +839,19 @@ public class ProvisioningTest { } private SystemState prepare(ApplicationId application, int container0Size, int container1Size, int content0Size, - int content1Size, String flavor, ProvisioningTester tester) { + int content1Size, NodeResources flavor, ProvisioningTester tester) { return prepare(application, container0Size, container1Size, content0Size, content1Size, flavor, Version.fromString("6.42"), tester); } private SystemState prepare(ApplicationId application, int container0Size, int container1Size, int content0Size, - int content1Size, String flavor, Version wantedVersion, ProvisioningTester tester) { + int content1Size, NodeResources flavor, Version wantedVersion, ProvisioningTester tester) { return prepare(application, container0Size, container1Size, content0Size, content1Size, false, flavor, wantedVersion, tester); } private SystemState prepare(ApplicationId application, int container0Size, int container1Size, int content0Size, - int content1Size, boolean required, String flavor, Version wantedVersion, + int content1Size, boolean required, NodeResources flavor, Version wantedVersion, ProvisioningTester tester) { // "deploy prepare" with a two container clusters and a storage cluster having of two groups ClusterSpec containerCluster0 = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("container0"), wantedVersion, false, Collections.emptySet()); @@ -868,7 +894,7 @@ public class ProvisioningTest { } private Set<HostSpec> prepare(ApplicationId application, ClusterSpec cluster, int nodeCount, int groups, - boolean required, String flavor, ProvisioningTester tester) { + boolean required, NodeResources flavor, ProvisioningTester tester) { if (nodeCount == 0) return Collections.emptySet(); // this is a shady practice return new HashSet<>(tester.prepare(application, cluster, nodeCount, groups, required, flavor)); } 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 8e41ddc0c0c..708ccf486f7 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 @@ -8,6 +8,7 @@ import com.yahoo.config.provision.Capacity; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.Flavor; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.HostFilter; import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.InstanceName; @@ -129,12 +130,12 @@ public class ProvisioningTester { public void patchNode(Node node) { nodeRepository.write(node); } - public List<HostSpec> prepare(ApplicationId application, ClusterSpec cluster, int nodeCount, int groups, String flavor) { + public List<HostSpec> prepare(ApplicationId application, ClusterSpec cluster, int nodeCount, int groups, NodeResources flavor) { return prepare(application, cluster, nodeCount, groups, false, flavor); } - public List<HostSpec> prepare(ApplicationId application, ClusterSpec cluster, int nodeCount, int groups, boolean required, String flavor) { - return prepare(application, cluster, Capacity.fromNodeCount(nodeCount, Optional.ofNullable(flavor), required, true), groups); + public List<HostSpec> prepare(ApplicationId application, ClusterSpec cluster, int nodeCount, int groups, boolean required, NodeResources flavor) { + return prepare(application, cluster, Capacity.fromCount(nodeCount, Optional.ofNullable(flavor), required, true), groups); } public List<HostSpec> prepare(ApplicationId application, ClusterSpec cluster, Capacity capacity, int groups) { @@ -234,7 +235,7 @@ public class ProvisioningTester { return makeProvisionedNodes(count, flavor, type, ipAddressPoolSize, false); } - public List<Node> makeProvisionedNodes(int n, String flavor, NodeType type, int ipAddressPoolSize, boolean dualStack) { + public List<Node> makeProvisionedNodes(int n, String flavorName, NodeType type, int ipAddressPoolSize, boolean dualStack) { List<Node> nodes = new ArrayList<>(n); for (int i = 0; i < n; i++) { @@ -271,6 +272,13 @@ public class ProvisioningTester { nameResolver.addRecord(String.format("node-%d-of-%s", poolIp, hostname), ipv4Addr); } } + Optional<Flavor> flavor = nodeFlavors.getFlavor(flavorName); + if (flavor.isEmpty()) { + if (type == NodeType.tenant) // Tenant nodes can have any (docker) flavor + flavor = Optional.of(new Flavor(NodeResources.fromLegacyName(flavorName))); + else + throw new IllegalArgumentException("No flavor '" + flavorName + "'"); + } nodes.add(nodeRepository.createNode(hostname, hostname, @@ -278,7 +286,7 @@ public class ProvisioningTester { ipAddressPool, Optional.empty(), Optional.empty(), - nodeFlavors.getFlavorOrThrow(flavor), + flavor.get(), type)); } nodes = nodeRepository.addNodes(nodes); @@ -328,37 +336,37 @@ public class ProvisioningTester { } /** Creates a set of virtual docker hosts */ - public List<Node> makeReadyVirtualDockerHosts(int n, String flavor) { + public List<Node> makeReadyVirtualDockerHosts(int n, NodeResources flavor) { return makeReadyVirtualNodes(n, 1, flavor, Optional.empty(), i -> "dockerHost" + i, NodeType.host); } /** Creates a set of virtual docker nodes on a single docker host starting with index 1 and increasing */ - public List<Node> makeReadyVirtualDockerNodes(int n, String flavor, String dockerHostId) { + public List<Node> makeReadyVirtualDockerNodes(int n, NodeResources flavor, String dockerHostId) { return makeReadyVirtualNodes(n, 1, flavor, Optional.of(dockerHostId), i -> String.format("%s-%03d", dockerHostId, i), NodeType.tenant); } /** Creates a single of virtual docker node on a single parent host */ - public List<Node> makeReadyVirtualDockerNode(int index, String flavor, String dockerHostId) { + public List<Node> makeReadyVirtualDockerNode(int index, NodeResources flavor, String dockerHostId) { return makeReadyVirtualNodes(1, index, flavor, Optional.of(dockerHostId), i -> String.format("%s-%03d", dockerHostId, i), NodeType.tenant); } /** Creates a set of virtual nodes without a parent host */ - public List<Node> makeReadyVirtualNodes(int n, String flavor) { + public List<Node> makeReadyVirtualNodes(int n, NodeResources flavor) { return makeReadyVirtualNodes(n, 0, flavor, Optional.empty(), i -> UUID.randomUUID().toString(), NodeType.tenant); } /** Creates a set of virtual nodes on a single parent host */ - private List<Node> makeReadyVirtualNodes(int count, int startIndex, String flavor, Optional<String> parentHostId, + private List<Node> makeReadyVirtualNodes(int count, int startIndex, NodeResources flavor, Optional<String> parentHostId, Function<Integer, String> nodeNamer, NodeType nodeType) { List<Node> nodes = new ArrayList<>(count); for (int i = startIndex; i < count + startIndex; i++) { String hostname = nodeNamer.apply(i); nodes.add(nodeRepository.createNode("openstack-id", hostname, parentHostId, - nodeFlavors.getFlavorOrThrow(flavor), nodeType)); + new Flavor(flavor), nodeType)); } nodes = nodeRepository.addNodes(nodes); nodes = nodeRepository.setDirty(nodes, Agent.system, getClass().getSimpleName()); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ResourceCapacityTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ResourceCapacityTest.java index 7883d9e58ed..5a021466e58 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ResourceCapacityTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ResourceCapacityTest.java @@ -51,29 +51,34 @@ public class ResourceCapacityTest { assertFalse(capacityOfHostSmall.hasCapacityFor(d3CPUFlavor)); // Compare it to various flavors - assertEquals(1, capacityOfHostSmall.compare(nodeCapacity(d1Flavor))); - assertEquals(1, capacityOfHostSmall.compare(nodeCapacity(d2Flavor))); - assertEquals(0, capacityOfHostSmall.compare(nodeCapacity(d3Flavor))); - assertEquals(-1, capacityOfHostSmall.compare(nodeCapacity(d3DiskFlavor))); - assertEquals(-1, capacityOfHostSmall.compare(nodeCapacity(d3CPUFlavor))); - assertEquals(-1, capacityOfHostSmall.compare(nodeCapacity(d3MemFlavor))); + assertEquals(1, compare(capacityOfHostSmall, nodeCapacity(d1Flavor))); + assertEquals(1, compare(capacityOfHostSmall, nodeCapacity(d2Flavor))); + assertEquals(0, compare(capacityOfHostSmall, nodeCapacity(d3Flavor))); + assertEquals(-1, compare(capacityOfHostSmall, nodeCapacity(d3DiskFlavor))); + assertEquals(-1, compare(capacityOfHostSmall, nodeCapacity(d3CPUFlavor))); + assertEquals(-1, compare(capacityOfHostSmall, nodeCapacity(d3MemFlavor))); // Change free capacity and assert on rest capacity capacityOfHostSmall = capacityOfHostSmall.subtract(ResourceCapacity.of(d1Flavor)); - assertEquals(0, capacityOfHostSmall.compare(nodeCapacity(d2Flavor))); + assertEquals(0, compare(capacityOfHostSmall, nodeCapacity(d2Flavor))); // Assert on rest capacity assertTrue(capacityOfHostSmall.hasCapacityFor(d1Flavor)); assertFalse(capacityOfHostSmall.hasCapacityFor(d3Flavor)); // At last compare the disk and cpu and mem variations - assertEquals(-1, nodeCapacity(d3Flavor).compare(nodeCapacity(d3DiskFlavor))); - assertEquals(1, nodeCapacity(d3DiskFlavor).compare(nodeCapacity(d3CPUFlavor))); - assertEquals(-1, nodeCapacity(d3CPUFlavor).compare(nodeCapacity(d3MemFlavor))); - assertEquals(1, nodeCapacity(d3MemFlavor).compare(nodeCapacity(d3DiskFlavor))); + assertEquals(-1, compare(nodeCapacity(d3Flavor), nodeCapacity(d3DiskFlavor))); + assertEquals(1, compare(nodeCapacity(d3DiskFlavor), nodeCapacity(d3CPUFlavor))); + assertEquals(-1, compare(nodeCapacity(d3CPUFlavor), nodeCapacity(d3MemFlavor))); + assertEquals(1, compare(nodeCapacity(d3MemFlavor), nodeCapacity(d3DiskFlavor))); } private ResourceCapacity nodeCapacity(Flavor flavor) { return ResourceCapacity.of(flavor); } + + private int compare(ResourceCapacity a, ResourceCapacity b) { + return ResourceCapacityComparator.defaultOrder().compare(a, b); + } + } 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 0d9ce179d5c..1f03db8f7c2 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 @@ -5,6 +5,7 @@ import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.HostSpec; import com.yahoo.config.provision.OutOfCapacityException; import com.yahoo.config.provision.RegionName; @@ -34,7 +35,7 @@ import static org.junit.Assert.assertNotNull; // to remove these tests public class VirtualNodeProvisioningTest { - private static final String flavor = "v-4-8-100"; + private static final NodeResources flavor = new NodeResources(4, 8, 100); private static final ClusterSpec contentClusterSpec = ClusterSpec.request(ClusterSpec.Type.content, ClusterSpec.Id.from("myContent"), Version.fromString("6.42"), false, Collections.emptySet()); private static final ClusterSpec containerClusterSpec = ClusterSpec.request(ClusterSpec.Type.container, ClusterSpec.Id.from("myContainer"), Version.fromString("6.42"), false, Collections.emptySet()); @@ -63,13 +64,13 @@ public class VirtualNodeProvisioningTest { // Go down to 3 nodes in container cluster List<HostSpec> containerHosts2 = prepare(containerClusterSpec, containerNodeCount - 1, groups); activate(containerHosts2); - final List<Node> nodes2 = getNodes(applicationId); + List<Node> nodes2 = getNodes(applicationId); assertDistinctParentHosts(nodes2, ClusterSpec.Type.container, containerNodeCount - 1); // Go up to 4 nodes again in container cluster List<HostSpec> containerHosts3 = prepare(containerClusterSpec, containerNodeCount, groups); activate(containerHosts3); - final List<Node> nodes3 = getNodes(applicationId); + List<Node> nodes3 = getNodes(applicationId); assertDistinctParentHosts(nodes3, ClusterSpec.Type.container, containerNodeCount); } @@ -81,11 +82,12 @@ public class VirtualNodeProvisioningTest { // Allowed to use same parent host for several nodes in same cluster in dev { + NodeResources flavor = new NodeResources(1, 1, 1); tester = new ProvisioningTester.Builder().zone(new Zone(Environment.dev, RegionName.from("us-east"))).build(); - tester.makeReadyVirtualDockerNodes(4, "default", "parentHost1"); + tester.makeReadyVirtualDockerNodes(4, flavor, "parentHost1"); - List<HostSpec> containerHosts = prepare(containerClusterSpec, containerNodeCount, groups); - List<HostSpec> contentHosts = prepare(contentClusterSpec, contentNodeCount, groups); + List<HostSpec> containerHosts = prepare(containerClusterSpec, containerNodeCount, groups, flavor); + List<HostSpec> contentHosts = prepare(contentClusterSpec, contentNodeCount, groups, flavor); activate(containerHosts, contentHosts); // downscaled to 1 node per cluster in dev, so 2 in total @@ -251,9 +253,9 @@ public class VirtualNodeProvisioningTest { public void unknown_distribution_with_known_and_unknown_ready_nodes() { tester.makeReadyVirtualNodes(3, flavor); - final int contentNodeCount = 3; - final int groups = 1; - final List<HostSpec> contentHosts = prepare(contentClusterSpec, contentNodeCount, groups); + int contentNodeCount = 3; + int groups = 1; + List<HostSpec> contentHosts = prepare(contentClusterSpec, contentNodeCount, groups); activate(contentHosts); assertEquals(3, getNodes(applicationId).size()); @@ -293,6 +295,10 @@ public class VirtualNodeProvisioningTest { return tester.prepare(applicationId, clusterSpec, nodeCount, groups, flavor); } + private List<HostSpec> prepare(ClusterSpec clusterSpec, int nodeCount, int groups, NodeResources flavor) { + return tester.prepare(applicationId, clusterSpec, nodeCount, groups, flavor); + } + @SafeVarargs private final void activate(List<HostSpec>... hostLists) { HashSet<HostSpec> hosts = new HashSet<>(); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java index 75995245274..d75cdaa3a2b 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java @@ -798,7 +798,7 @@ public class RestApiTest { return "{\"hostname\":\"" + hostname + "\", \"parentHostname\":\"" + parentHostname + "\"," + createIpAddresses(ipAddress) + createAdditionalIpAddresses(additionalIpCount) + - "\"openStackId\":\"" + hostname + "\",\"flavor\":\"docker\"}"; + "\"openStackId\":\"" + hostname + "\",\"flavor\":\"d-1-1-100\"}"; } private String asNodeJson(String hostname, String flavor, String... ipAddress) { diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifierTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifierTest.java index 2a9a1c4fa3b..914a2e62164 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifierTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifierTest.java @@ -6,13 +6,12 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; -import com.yahoo.config.provision.Flavor; +import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.RegionName; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.Zone; import com.yahoo.config.provisioning.ConfigServerSecurityConfig; -import com.yahoo.config.provisioning.FlavorsConfig; import com.yahoo.security.KeyUtils; import com.yahoo.security.Pkcs10Csr; import com.yahoo.security.Pkcs10CsrBuilder; @@ -22,7 +21,6 @@ import com.yahoo.vespa.hosted.provision.Node; import com.yahoo.vespa.hosted.provision.NodeRepositoryTester; import com.yahoo.vespa.hosted.provision.node.Allocation; import com.yahoo.vespa.hosted.provision.node.Generation; -import com.yahoo.vespa.hosted.provision.provisioning.FlavorConfigBuilder; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -243,7 +241,7 @@ public class NodeIdentifierTest { emptySet(), HOSTNAME, Optional.of("parenthost"), - new Flavor(createFlavourConfig().flavor(0)), + new NodeResources(1, 2, 50), NodeType.tenant) .with( new Allocation( @@ -271,10 +269,4 @@ public class NodeIdentifierTest { } - private static FlavorsConfig createFlavourConfig() { - FlavorConfigBuilder b = new FlavorConfigBuilder(); - b.addFlavor("docker", 1., 2., 50, Flavor.Type.DOCKER_CONTAINER).cost(1); - return b.build(); - } - } diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/docker-container1.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/docker-container1.json index c98fbb46ff8..5c3829bd5ff 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/docker-container1.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/docker-container1.json @@ -6,14 +6,13 @@ "hostname": "test-container-1", "parentHostname": "dockerhost3.yahoo.com", "openStackId": "fake-test-container-1", - "flavor": "docker", - "canonicalFlavor": "docker", + "flavor": "[vcpu: 1.0, memory: 1.0 Gb, disk 100.0 Gb]", + "canonicalFlavor": "[vcpu: 1.0, memory: 1.0 Gb, disk 100.0 Gb]", "minDiskAvailableGb": 100.0, - "minMainMemoryAvailableGb": 0.5, - "description": "Flavor-name-is-docker", - "minCpuCores": 0.2, + "minMainMemoryAvailableGb": 1.0, + "minCpuCores": 1.0, "fastDisk": true, - "bandwidth":0.0, + "bandwidth":1.0, "environment": "DOCKER_CONTAINER", "owner": { "tenant": "tenant3", diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/node11.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/node11.json index b1329eebb2d..0733473c2fe 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/node11.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/node11.json @@ -6,14 +6,13 @@ "hostname": "host11.yahoo.com", "parentHostname": "parent.host.yahoo.com", "openStackId": "host11.yahoo.com", - "flavor": "docker", - "canonicalFlavor": "docker", + "flavor": "[vcpu: 1.0, memory: 1.0 Gb, disk 100.0 Gb]", + "canonicalFlavor": "[vcpu: 1.0, memory: 1.0 Gb, disk 100.0 Gb]", "minDiskAvailableGb": 100.0, - "minMainMemoryAvailableGb": 0.5, - "description": "Flavor-name-is-docker", - "minCpuCores": 0.2, + "minMainMemoryAvailableGb": 1.0, + "minCpuCores": 1.0, "fastDisk": true, - "bandwidth":0.0, + "bandwidth":1.0, "environment": "DOCKER_CONTAINER", "rebootGeneration": 0, "currentRebootGeneration": 0, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/node4.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/node4.json index a02035efd88..1cb1f6657e4 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/node4.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/node4.json @@ -6,14 +6,13 @@ "hostname": "host4.yahoo.com", "parentHostname": "dockerhost1.yahoo.com", "openStackId": "node4", - "flavor": "docker", - "canonicalFlavor": "docker", + "flavor": "[vcpu: 1.0, memory: 1.0 Gb, disk 100.0 Gb]", + "canonicalFlavor": "[vcpu: 1.0, memory: 1.0 Gb, disk 100.0 Gb]", "minDiskAvailableGb": 100.0, - "minMainMemoryAvailableGb": 0.5, - "description": "Flavor-name-is-docker", - "minCpuCores": 0.2, + "minMainMemoryAvailableGb": 1.0, + "minCpuCores": 1.0, "fastDisk": true, - "bandwidth":0.0, + "bandwidth":1.0, "environment": "DOCKER_CONTAINER", "owner": { "tenant": "tenant3", diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/node5-after-changes.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/node5-after-changes.json index 8eb3a74ce2a..8a5c80212c7 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/node5-after-changes.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/node5-after-changes.json @@ -6,14 +6,13 @@ "hostname": "host5.yahoo.com", "parentHostname": "dockerhost2.yahoo.com", "openStackId": "node5", - "flavor": "docker", - "canonicalFlavor": "docker", + "flavor": "[vcpu: 1.0, memory: 1.0 Gb, disk 100.0 Gb]", + "canonicalFlavor": "[vcpu: 1.0, memory: 1.0 Gb, disk 100.0 Gb]", "minDiskAvailableGb": 100.0, - "minMainMemoryAvailableGb": 0.5, - "description": "Flavor-name-is-docker", - "minCpuCores": 0.2, + "minMainMemoryAvailableGb": 1.0, + "minCpuCores": 1.0, "fastDisk": true, - "bandwidth":0.0, + "bandwidth": 1.0, "environment": "DOCKER_CONTAINER", "rebootGeneration": 1, "currentRebootGeneration": 0, diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/node5.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/node5.json index a14443e096d..08817a67324 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/node5.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/node5.json @@ -6,14 +6,13 @@ "hostname": "host5.yahoo.com", "parentHostname": "dockerhost2.yahoo.com", "openStackId": "node5", - "flavor": "docker", - "canonicalFlavor": "docker", + "flavor": "[vcpu: 1.0, memory: 1.0 Gb, disk 100.0 Gb]", + "canonicalFlavor": "[vcpu: 1.0, memory: 1.0 Gb, disk 100.0 Gb]", "minDiskAvailableGb": 100.0, - "minMainMemoryAvailableGb": 0.5, - "description": "Flavor-name-is-docker", - "minCpuCores": 0.2, + "minMainMemoryAvailableGb": 1.0, + "minCpuCores": 1.0, "fastDisk": true, - "bandwidth":0.0, + "bandwidth": 1.0, "environment": "DOCKER_CONTAINER", "rebootGeneration": 1, "currentRebootGeneration": 0, |