From ea1bc491ddf17062112981a2131ced8444b9a70f Mon Sep 17 00:00:00 2001 From: Jon Bratseth Date: Sun, 12 May 2024 18:26:21 +0200 Subject: Move CapacityPolicies to config-privisioning --- .../yahoo/config/provision/CapacityPolicies.java | 185 +++++++++++++++++++ .../hosted/provision/autoscale/ClusterModel.java | 2 +- .../vespa/hosted/provision/autoscale/Limits.java | 2 +- .../provision/provisioning/CapacityPolicies.java | 195 --------------------- .../provisioning/NodeRepositoryProvisioner.java | 1 + .../provision/provisioning/NodeResourceLimits.java | 1 + .../provision/autoscale/AutoscalingTest.java | 2 +- .../provisioning/DynamicProvisioningTester.java | 1 + .../provision/provisioning/ProvisioningTest.java | 24 ++- .../provision/provisioning/ProvisioningTester.java | 1 + 10 files changed, 210 insertions(+), 204 deletions(-) create mode 100644 config-provisioning/src/main/java/com/yahoo/config/provision/CapacityPolicies.java delete mode 100644 node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/CapacityPolicies.java b/config-provisioning/src/main/java/com/yahoo/config/provision/CapacityPolicies.java new file mode 100644 index 00000000000..dec2984846e --- /dev/null +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/CapacityPolicies.java @@ -0,0 +1,185 @@ +// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.config.provision; + +import com.yahoo.component.Version; +import com.yahoo.config.provision.NodeResources.DiskSpeed; +import com.yahoo.vespa.flags.FlagSource; +import com.yahoo.vespa.flags.PermanentFlags; +import com.yahoo.vespa.flags.StringFlag; + +import java.util.Map; +import java.util.TreeMap; + +import static com.yahoo.config.provision.NodeResources.Architecture; +import static com.yahoo.vespa.flags.Dimension.INSTANCE_ID; +import static java.util.Objects.requireNonNull; + +/** + * Defines the policies for assigning cluster capacity in various environments. + * + * @author bratseth + */ +public class CapacityPolicies { + + private final Zone zone; + private final Exclusivity exclusivity; + private final StringFlag adminClusterNodeArchitecture; + + public CapacityPolicies(Zone zone, Exclusivity exclusivity, FlagSource flagSource) { + this.zone = zone; + this.exclusivity = exclusivity; + this.adminClusterNodeArchitecture = PermanentFlags.ADMIN_CLUSTER_NODE_ARCHITECTURE.bindTo(flagSource); + } + + public Capacity applyOn(Capacity capacity, ApplicationId application, boolean exclusive) { + var min = applyOn(capacity.minResources(), capacity, application, exclusive); + var max = applyOn(capacity.maxResources(), capacity, application, exclusive); + var groupSize = capacity.groupSize().fromAtMost(max.nodes() / min.groups()) + .toAtLeast(min.nodes() / max.groups()); + return capacity.withLimits(min, max, groupSize); + } + + private ClusterResources applyOn(ClusterResources resources, Capacity capacity, ApplicationId application, boolean exclusive) { + int nodes = decideCount(resources.nodes(), capacity.isRequired(), application.instance().isTester()); + int groups = decideGroups(resources.nodes(), resources.groups(), nodes); + var nodeResources = decideNodeResources(resources.nodeResources(), capacity.isRequired(), exclusive); + return new ClusterResources(nodes, groups, nodeResources); + } + + private int decideCount(int requested, boolean required, boolean isTester) { + if (isTester) return 1; + + if (required) return requested; + return switch (zone.environment()) { + case dev, test -> 1; + case perf -> Math.min(requested, 3); + case staging -> requested <= 1 ? requested : Math.max(2, requested / 10); + case prod -> requested; + }; + } + + private int decideGroups(int requestedNodes, int requestedGroups, int decidedNodes) { + if (requestedNodes == decidedNodes) return requestedGroups; + int groups = Math.min(requestedGroups, decidedNodes); // cannot have more groups than nodes + while (groups > 1 && decidedNodes % groups != 0) + groups--; // Must be divisible by the number of groups + return groups; + } + + private NodeResources decideNodeResources(NodeResources target, boolean required, boolean exclusive) { + if (required || exclusive) return target; // Cannot downsize if resources are required, or exclusively allocated + if (target.isUnspecified()) return target; // Cannot be modified + + if (zone.environment() == Environment.dev && zone.cloud().allowHostSharing()) { + // Dev does not cap the cpu or network of containers since usage is spotty: Allocate just a small amount exclusively + target = target.withVcpu(0.1).withBandwidthGbps(0.1); + + // Allocate without GPU in dev + target = target.with(NodeResources.GpuResources.zero()); + } + + // Allow slow storage in zones which are not performance sensitive + if (zone.system().isCd() || zone.environment() == Environment.dev || zone.environment() == Environment.test) + target = target.with(NodeResources.DiskSpeed.any).with(NodeResources.StorageType.any).withBandwidthGbps(0.1); + + return target; + } + + public ClusterResources specifyFully(ClusterResources resources, ClusterSpec clusterSpec, ApplicationId applicationId) { + return resources.with(specifyFully(resources.nodeResources(), clusterSpec, applicationId)); + } + + public NodeResources specifyFully(NodeResources resources, ClusterSpec clusterSpec, ApplicationId applicationId) { + return resources.withUnspecifiedFieldsFrom(defaultResources(clusterSpec, applicationId).with(DiskSpeed.any)); + } + + private NodeResources defaultResources(ClusterSpec clusterSpec, ApplicationId applicationId) { + if (clusterSpec.type() == ClusterSpec.Type.admin) { + Architecture architecture = adminClusterArchitecture(applicationId); + + if (exclusivity.allocation(clusterSpec)) { + return smallestExclusiveResources().with(architecture); + } + + if (clusterSpec.id().value().equals("cluster-controllers")) { + return clusterControllerResources(clusterSpec, architecture).with(architecture); + } + + if (clusterSpec.id().value().equals("logserver")) { + return logserverResources(architecture).with(architecture); + } + + return versioned(clusterSpec, Map.of(new Version(0), smallestSharedResources())).with(architecture); + } + + if (clusterSpec.type() == ClusterSpec.Type.content) { + // When changing defaults here update cloud.vespa.ai/en/reference/services + return zone.cloud().dynamicProvisioning() + ? versioned(clusterSpec, Map.of(new Version(0), new NodeResources(2, 16, 300, 0.3))) + : versioned(clusterSpec, Map.of(new Version(0), new NodeResources(1.5, 8, 50, 0.3))); + } + else { + // When changing defaults here update cloud.vespa.ai/en/reference/services + return zone.cloud().dynamicProvisioning() + ? versioned(clusterSpec, Map.of(new Version(0), new NodeResources(2.0, 8, 50, 0.3))) + : versioned(clusterSpec, Map.of(new Version(0), new NodeResources(1.5, 8, 50, 0.3))); + } + } + + private NodeResources clusterControllerResources(ClusterSpec clusterSpec, Architecture architecture) { + // 1.32 fits floor(8/1.32) = 6 cluster controllers on each 8Gb host, and each will have + // 1.32-(0.7+0.6)*(1.32/8) = 1.1 Gb real memory given current taxes. + if (architecture == Architecture.x86_64) + return versioned(clusterSpec, Map.of(new Version(0), new NodeResources(0.25, 1.32, 10, 0.3))); + else + // arm64 nodes need more memory + return versioned(clusterSpec, Map.of(new Version(0), new NodeResources(0.25, 1.50, 10, 0.3))); + } + + private NodeResources logserverResources(Architecture architecture) { + if (zone.cloud().name() == CloudName.AZURE) + return new NodeResources(2, 4, 50, 0.3); + + if (zone.cloud().name() == CloudName.GCP) + return new NodeResources(1, 4, 50, 0.3); + + return architecture == Architecture.arm64 + ? new NodeResources(0.5, 2.5, 50, 0.3) + : new NodeResources(0.5, 2, 50, 0.3); + } + + private Architecture adminClusterArchitecture(ApplicationId instance) { + return Architecture.valueOf(adminClusterNodeArchitecture.with(INSTANCE_ID, instance.serializedForm()).value()); + } + + // The lowest amount of resources that can be exclusive allocated (i.e. a matching host flavor for this exists) + private NodeResources smallestExclusiveResources() { + return zone.cloud().name() == CloudName.AZURE || zone.cloud().name() == CloudName.GCP + ? new NodeResources(2, 8, 50, 0.3) + : new NodeResources(0.5, 8, 50, 0.3); + } + + // The lowest amount of resources that can be shared (i.e. a matching host flavor for this exists) + private NodeResources smallestSharedResources() { + return zone.cloud().name() == CloudName.GCP + ? new NodeResources(1, 4, 50, 0.3) + : new NodeResources(0.5, 2, 50, 0.3); + } + + /** Returns whether the nodes requested can share physical host with other applications */ + public ClusterSpec decideExclusivity(Capacity capacity, ClusterSpec requestedCluster) { + if (capacity.cloudAccount().isPresent()) return requestedCluster.withExclusivity(true); // Implicit exclusive + boolean exclusive = requestedCluster.isExclusive() && (capacity.isRequired() || zone.environment() == Environment.prod); + return requestedCluster.withExclusivity(exclusive); + } + + /** + * Returns the resources for the newest version not newer than that requested in the cluster spec. + */ + private static NodeResources versioned(ClusterSpec spec, Map resources) { + return requireNonNull(new TreeMap<>(resources).floorEntry(spec.vespaVersion()), + "no default resources applicable for " + spec + " among: " + resources) + .getValue(); + } + +} diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java index ece84cfd909..5310ecaa1c2 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/ClusterModel.java @@ -8,7 +8,7 @@ import com.yahoo.vespa.hosted.provision.NodeList; import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.applications.Application; import com.yahoo.vespa.hosted.provision.applications.Cluster; -import com.yahoo.vespa.hosted.provision.provisioning.CapacityPolicies; +import com.yahoo.config.provision.CapacityPolicies; import java.time.Clock; import java.time.Duration; diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java index 2cd06706d7f..54e611eef18 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/autoscale/Limits.java @@ -9,7 +9,7 @@ import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.NodeResources; import com.yahoo.vespa.hosted.provision.NodeRepository; import com.yahoo.vespa.hosted.provision.applications.Cluster; -import com.yahoo.vespa.hosted.provision.provisioning.CapacityPolicies; +import com.yahoo.config.provision.CapacityPolicies; import java.util.Objects; 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 deleted file mode 100644 index bb9aaccd1e3..00000000000 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/CapacityPolicies.java +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.provision.provisioning; - -import com.yahoo.component.Version; -import com.yahoo.config.provision.ApplicationId; -import com.yahoo.config.provision.Capacity; -import com.yahoo.config.provision.CloudName; -import com.yahoo.config.provision.ClusterResources; -import com.yahoo.config.provision.ClusterSpec; -import com.yahoo.config.provision.Environment; -import com.yahoo.config.provision.Exclusivity; -import com.yahoo.config.provision.NodeResources; -import com.yahoo.config.provision.NodeResources.DiskSpeed; -import com.yahoo.config.provision.Zone; -import com.yahoo.vespa.flags.FlagSource; -import com.yahoo.vespa.flags.PermanentFlags; -import com.yahoo.vespa.flags.StringFlag; - -import java.util.Map; -import java.util.TreeMap; - -import static com.yahoo.config.provision.NodeResources.Architecture; -import static com.yahoo.vespa.flags.Dimension.INSTANCE_ID; -import static java.util.Objects.requireNonNull; - -/** - * Defines the policies for assigning cluster capacity in various environments - * - * @author bratseth - * @see NodeResourceLimits - */ -public class CapacityPolicies { - - private final Zone zone; - private final Exclusivity exclusivity; - private final StringFlag adminClusterNodeArchitecture; - - public CapacityPolicies(Zone zone, Exclusivity exclusivity, FlagSource flagSource) { - this.zone = zone; - this.exclusivity = exclusivity; - this.adminClusterNodeArchitecture = PermanentFlags.ADMIN_CLUSTER_NODE_ARCHITECTURE.bindTo(flagSource); - } - - public Capacity applyOn(Capacity capacity, ApplicationId application, boolean exclusive) { - var min = applyOn(capacity.minResources(), capacity, application, exclusive); - var max = applyOn(capacity.maxResources(), capacity, application, exclusive); - var groupSize = capacity.groupSize().fromAtMost(max.nodes() / min.groups()) - .toAtLeast(min.nodes() / max.groups()); - return capacity.withLimits(min, max, groupSize); - } - - private ClusterResources applyOn(ClusterResources resources, Capacity capacity, ApplicationId application, boolean exclusive) { - int nodes = decideCount(resources.nodes(), capacity.isRequired(), application.instance().isTester()); - int groups = decideGroups(resources.nodes(), resources.groups(), nodes); - var nodeResources = decideNodeResources(resources.nodeResources(), capacity.isRequired(), exclusive); - return new ClusterResources(nodes, groups, nodeResources); - } - - private int decideCount(int requested, boolean required, boolean isTester) { - if (isTester) return 1; - - if (required) return requested; - return switch (zone.environment()) { - case dev, test -> 1; - case perf -> Math.min(requested, 3); - case staging -> requested <= 1 ? requested : Math.max(2, requested / 10); - case prod -> requested; - }; - } - - private int decideGroups(int requestedNodes, int requestedGroups, int decidedNodes) { - if (requestedNodes == decidedNodes) return requestedGroups; - int groups = Math.min(requestedGroups, decidedNodes); // cannot have more groups than nodes - while (groups > 1 && decidedNodes % groups != 0) - groups--; // Must be divisible by the number of groups - return groups; - } - - private NodeResources decideNodeResources(NodeResources target, boolean required, boolean exclusive) { - if (required || exclusive) return target; // Cannot downsize if resources are required, or exclusively allocated - if (target.isUnspecified()) return target; // Cannot be modified - - if (zone.environment() == Environment.dev && zone.cloud().allowHostSharing()) { - // Dev does not cap the cpu or network of containers since usage is spotty: Allocate just a small amount exclusively - target = target.withVcpu(0.1).withBandwidthGbps(0.1); - - // Allocate without GPU in dev - target = target.with(NodeResources.GpuResources.zero()); - } - - // Allow slow storage in zones which are not performance sensitive - if (zone.system().isCd() || zone.environment() == Environment.dev || zone.environment() == Environment.test) - target = target.with(NodeResources.DiskSpeed.any).with(NodeResources.StorageType.any).withBandwidthGbps(0.1); - - return target; - } - - public ClusterResources specifyFully(ClusterResources resources, ClusterSpec clusterSpec, ApplicationId applicationId) { - return resources.with(specifyFully(resources.nodeResources(), clusterSpec, applicationId)); - } - - public NodeResources specifyFully(NodeResources resources, ClusterSpec clusterSpec, ApplicationId applicationId) { - return resources.withUnspecifiedFieldsFrom(defaultResources(clusterSpec, applicationId).with(DiskSpeed.any)); - } - - private NodeResources defaultResources(ClusterSpec clusterSpec, ApplicationId applicationId) { - if (clusterSpec.type() == ClusterSpec.Type.admin) { - Architecture architecture = adminClusterArchitecture(applicationId); - - if (exclusivity.allocation(clusterSpec)) { - return smallestExclusiveResources().with(architecture); - } - - if (clusterSpec.id().value().equals("cluster-controllers")) { - return clusterControllerResources(clusterSpec, architecture).with(architecture); - } - - if (clusterSpec.id().value().equals("logserver")) { - return logserverResources(architecture).with(architecture); - } - - return versioned(clusterSpec, Map.of(new Version(0), smallestSharedResources())).with(architecture); - } - - if (clusterSpec.type() == ClusterSpec.Type.content) { - // When changing defaults here update cloud.vespa.ai/en/reference/services - return zone.cloud().dynamicProvisioning() - ? versioned(clusterSpec, Map.of(new Version(0), new NodeResources(2, 16, 300, 0.3))) - : versioned(clusterSpec, Map.of(new Version(0), new NodeResources(1.5, 8, 50, 0.3))); - } - else { - // When changing defaults here update cloud.vespa.ai/en/reference/services - return zone.cloud().dynamicProvisioning() - ? versioned(clusterSpec, Map.of(new Version(0), new NodeResources(2.0, 8, 50, 0.3))) - : versioned(clusterSpec, Map.of(new Version(0), new NodeResources(1.5, 8, 50, 0.3))); - } - } - - private NodeResources clusterControllerResources(ClusterSpec clusterSpec, Architecture architecture) { - // 1.32 fits floor(8/1.32) = 6 cluster controllers on each 8Gb host, and each will have - // 1.32-(0.7+0.6)*(1.32/8) = 1.1 Gb real memory given current taxes. - if (architecture == Architecture.x86_64) - return versioned(clusterSpec, Map.of(new Version(0), new NodeResources(0.25, 1.32, 10, 0.3))); - else - // arm64 nodes need more memory - return versioned(clusterSpec, Map.of(new Version(0), new NodeResources(0.25, 1.50, 10, 0.3))); - } - - private NodeResources logserverResources(Architecture architecture) { - if (zone.cloud().name() == CloudName.AZURE) - return new NodeResources(2, 4, 50, 0.3); - - if (zone.cloud().name() == CloudName.GCP) - return new NodeResources(1, 4, 50, 0.3); - - return architecture == Architecture.arm64 - ? new NodeResources(0.5, 2.5, 50, 0.3) - : new NodeResources(0.5, 2, 50, 0.3); - } - - private Architecture adminClusterArchitecture(ApplicationId instance) { - return Architecture.valueOf(adminClusterNodeArchitecture.with(INSTANCE_ID, instance.serializedForm()).value()); - } - - /** - * Returns the resources for the newest version not newer than that requested in the cluster spec. - */ - static NodeResources versioned(ClusterSpec spec, Map resources) { - return requireNonNull(new TreeMap<>(resources).floorEntry(spec.vespaVersion()), - "no default resources applicable for " + spec + " among: " + resources) - .getValue(); - } - - // The lowest amount of resources that can be exclusive allocated (i.e. a matching host flavor for this exists) - private NodeResources smallestExclusiveResources() { - return zone.cloud().name() == CloudName.AZURE || zone.cloud().name() == CloudName.GCP - ? new NodeResources(2, 8, 50, 0.3) - : new NodeResources(0.5, 8, 50, 0.3); - } - - // The lowest amount of resources that can be shared (i.e. a matching host flavor for this exists) - private NodeResources smallestSharedResources() { - return zone.cloud().name() == CloudName.GCP - ? new NodeResources(1, 4, 50, 0.3) - : new NodeResources(0.5, 2, 50, 0.3); - } - - /** Returns whether the nodes requested can share physical host with other applications */ - public ClusterSpec decideExclusivity(Capacity capacity, ClusterSpec requestedCluster) { - if (capacity.cloudAccount().isPresent()) return requestedCluster.withExclusivity(true); // Implicit exclusive - boolean exclusive = requestedCluster.isExclusive() && (capacity.isRequired() || zone.environment() == Environment.prod); - return requestedCluster.withExclusivity(exclusive); - } - -} 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 89c508f7fa8..44750988a3e 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,6 +6,7 @@ import com.yahoo.config.provision.ActivationContext; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ApplicationTransaction; import com.yahoo.config.provision.Capacity; +import com.yahoo.config.provision.CapacityPolicies; import com.yahoo.config.provision.CloudAccount; import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeResourceLimits.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeResourceLimits.java index ba22aa5a75a..5eb8c2e7fd7 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeResourceLimits.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeResourceLimits.java @@ -2,6 +2,7 @@ package com.yahoo.vespa.hosted.provision.provisioning; import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.CapacityPolicies; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.NodeResources; diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java index 91034aae0cf..6316adae887 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTest.java @@ -13,7 +13,7 @@ import com.yahoo.config.provision.NodeResources.DiskSpeed; import com.yahoo.config.provision.NodeResources.StorageType; import com.yahoo.config.provision.RegionName; import com.yahoo.config.provision.Zone; -import com.yahoo.vespa.hosted.provision.provisioning.CapacityPolicies; +import com.yahoo.config.provision.CapacityPolicies; import com.yahoo.vespa.hosted.provision.provisioning.DynamicProvisioningTester; import org.junit.Test; diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java index dde56700d0e..45004ad41ec 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java @@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.provision.provisioning; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Capacity; +import com.yahoo.config.provision.CapacityPolicies; import com.yahoo.config.provision.CloudAccount; import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; 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 7b690b880c2..9e6ae9f010c 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 @@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.provision.provisioning; import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Capacity; +import com.yahoo.config.provision.CapacityPolicies; import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.ClusterResources; import com.yahoo.config.provision.ClusterSpec; @@ -46,12 +47,14 @@ import java.util.Map; import java.util.Optional; import java.util.Random; import java.util.Set; +import java.util.TreeMap; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import static java.time.temporal.ChronoUnit.MILLIS; +import static java.util.Objects.requireNonNull; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -1114,12 +1117,12 @@ public class ProvisioningTest { new Version("6"), new NodeResources(1, 1, 1, 1)); assertThrows(NullPointerException.class, - () -> CapacityPolicies.versioned(spec.vespaVersion("5.0").build(), resources)); - assertEquals(new NodeResources(1, 1, 1, 1), CapacityPolicies.versioned(spec.vespaVersion("6.0").build(), resources)); - assertEquals(new NodeResources(2, 2, 2, 2), CapacityPolicies.versioned(spec.vespaVersion("7.0").build(), resources)); - assertEquals(new NodeResources(2, 2, 2, 2), CapacityPolicies.versioned(spec.vespaVersion("7.1").build(), resources)); - assertEquals(new NodeResources(3, 3, 3, 3), CapacityPolicies.versioned(spec.vespaVersion("8.0").build(), resources)); - assertEquals(new NodeResources(3, 3, 3, 3), CapacityPolicies.versioned(spec.vespaVersion("9.0").build(), resources)); + () -> versioned(spec.vespaVersion("5.0").build(), resources)); + assertEquals(new NodeResources(1, 1, 1, 1), versioned(spec.vespaVersion("6.0").build(), resources)); + assertEquals(new NodeResources(2, 2, 2, 2), versioned(spec.vespaVersion("7.0").build(), resources)); + assertEquals(new NodeResources(2, 2, 2, 2), versioned(spec.vespaVersion("7.1").build(), resources)); + assertEquals(new NodeResources(3, 3, 3, 3), versioned(spec.vespaVersion("8.0").build(), resources)); + assertEquals(new NodeResources(3, 3, 3, 3), versioned(spec.vespaVersion("9.0").build(), resources)); } @Test @@ -1292,4 +1295,13 @@ public class ProvisioningTest { return new ClusterResources(nodes, groups, new NodeResources(vcpu, memory, disk, 0.1)); } + /** + * Returns the resources for the newest version not newer than that requested in the cluster spec. + */ + static NodeResources versioned(ClusterSpec spec, Map resources) { + return requireNonNull(new TreeMap<>(resources).floorEntry(spec.vespaVersion()), + "no default resources applicable for " + spec + " among: " + resources) + .getValue(); + } + } 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 cbc2ccb61ac..8b49e5e5223 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.ApplicationMutex; import com.yahoo.config.provision.ApplicationName; import com.yahoo.config.provision.ApplicationTransaction; import com.yahoo.config.provision.Capacity; +import com.yahoo.config.provision.CapacityPolicies; import com.yahoo.config.provision.Cloud; import com.yahoo.config.provision.CloudAccount; import com.yahoo.config.provision.ClusterResources; -- cgit v1.2.3