diff options
author | Håkon Hallingstad <hakon@yahooinc.com> | 2023-10-16 14:19:40 +0200 |
---|---|---|
committer | Håkon Hallingstad <hakon@yahooinc.com> | 2023-10-16 14:19:40 +0200 |
commit | 3d4ed6313d47b92b39d1d215a04c059eff06ffd2 (patch) | |
tree | c6888a992a7ea1c1b533397a831ac692315763fa | |
parent | 0ccfe8aab8c12ecd518f882a048f8a13fb2084f1 (diff) |
Set provisioned-for if enabled and application has exclusive="true"
6 files changed, 43 insertions, 18 deletions
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java index ed0f9aac884..04aced68d1b 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java @@ -2,8 +2,6 @@ package com.yahoo.config.provision; import com.yahoo.component.Version; -import com.yahoo.config.provision.ZoneEndpoint.AccessType; -import com.yahoo.config.provision.ZoneEndpoint.AllowedUrn; import java.util.Objects; import java.util.Optional; @@ -24,19 +22,21 @@ public final class ClusterSpec { private final Version vespaVersion; private final boolean exclusive; + private final boolean provisionForApplication; private final Optional<Id> combinedId; private final Optional<DockerImage> dockerImageRepo; private final ZoneEndpoint zoneEndpoint; private final boolean stateful; private ClusterSpec(Type type, Id id, Optional<Group> groupId, Version vespaVersion, boolean exclusive, - Optional<Id> combinedId, Optional<DockerImage> dockerImageRepo, + boolean provisionForApplication, Optional<Id> combinedId, Optional<DockerImage> dockerImageRepo, ZoneEndpoint zoneEndpoint, boolean stateful) { this.type = type; this.id = id; this.groupId = groupId; this.vespaVersion = Objects.requireNonNull(vespaVersion, "vespaVersion cannot be null"); this.exclusive = exclusive; + this.provisionForApplication = provisionForApplication; if (type == Type.combined) { if (combinedId.isEmpty()) throw new IllegalArgumentException("combinedId must be set for cluster of type " + type); } else { @@ -85,21 +85,22 @@ public final class ClusterSpec { */ public boolean isExclusive() { return exclusive; } + /** Returns whether the physical hosts must be provisioned specifically for this application. */ + public boolean provisionForApplication() { return provisionForApplication; } + /** Returns whether this cluster has state */ public boolean isStateful() { return stateful; } public ClusterSpec with(Optional<Group> newGroup) { - return new ClusterSpec(type, id, newGroup, vespaVersion, exclusive, combinedId, dockerImageRepo, zoneEndpoint, stateful); + return new ClusterSpec(type, id, newGroup, vespaVersion, exclusive, provisionForApplication, combinedId, dockerImageRepo, zoneEndpoint, stateful); } public ClusterSpec withExclusivity(boolean exclusive) { - return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, combinedId, dockerImageRepo, zoneEndpoint, stateful); + return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, provisionForApplication, combinedId, dockerImageRepo, zoneEndpoint, stateful); } - // TODO: Remove after July 2023 - @Deprecated - public ClusterSpec exclusive(boolean exclusive) { - return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, combinedId, dockerImageRepo, zoneEndpoint, stateful); + public ClusterSpec withProvisionForApplication(boolean provisionForApplication) { + return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, provisionForApplication, combinedId, dockerImageRepo, zoneEndpoint, stateful); } /** Creates a ClusterSpec when requesting a cluster */ @@ -121,6 +122,7 @@ public final class ClusterSpec { private Optional<DockerImage> dockerImageRepo = Optional.empty(); private Version vespaVersion; private boolean exclusive = false; + private boolean provisionForApplication = false; private Optional<Id> combinedId = Optional.empty(); private ZoneEndpoint zoneEndpoint = ZoneEndpoint.defaultEndpoint; private boolean stateful; @@ -132,7 +134,7 @@ public final class ClusterSpec { } public ClusterSpec build() { - return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, combinedId, dockerImageRepo, zoneEndpoint, stateful); + return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, provisionForApplication, combinedId, dockerImageRepo, zoneEndpoint, stateful); } public Builder group(Group groupId) { @@ -155,6 +157,11 @@ public final class ClusterSpec { return this; } + public Builder provisionForApplication(boolean provisionForApplication) { + this.provisionForApplication = provisionForApplication; + return this; + } + public Builder combinedId(Optional<Id> combinedId) { this.combinedId = combinedId; return this; @@ -188,6 +195,7 @@ public final class ClusterSpec { if (o == null || getClass() != o.getClass()) return false; ClusterSpec that = (ClusterSpec) o; return exclusive == that.exclusive && + provisionForApplication == that.provisionForApplication && stateful == that.stateful && type == that.type && id.equals(that.id) && @@ -200,7 +208,7 @@ public final class ClusterSpec { @Override public int hashCode() { - return Objects.hash(type, id, groupId, vespaVersion, exclusive, combinedId, dockerImageRepo, zoneEndpoint, stateful); + return Objects.hash(type, id, groupId, vespaVersion, exclusive, provisionForApplication, combinedId, dockerImageRepo, zoneEndpoint, stateful); } /** diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java index 56cd06d3b35..a09c6ea00c6 100644 --- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java +++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java @@ -340,6 +340,14 @@ public class Flags { "Takes effect at redeployment", INSTANCE_ID); + public static final UnboundBooleanFlag EXCLUSIVE_PROVISIONING = defineFeatureFlag( + "exclusive-provisioning", false, + List.of("hakonhall"), "2023-10-12", "2023-12-12", + "Whether to provision a host exclusively to an application ID only based on exclusive=\"true\" from services.xml. " + + "Enabling this will produce hosts with exclusiveTo[ApplicationId] without provisionedToApplicationId.", + "Takes immediate effect newly provisioned hosts new hosts", + HOSTNAME, CLOUD_ACCOUNT); + public static final UnboundBooleanFlag WRITE_CONFIG_SERVER_SESSION_DATA_AS_ONE_BLOB = defineFeatureFlag( "write-config-server-session-data-as-blob", false, List.of("hmusum"), "2023-07-19", "2023-11-01", 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 f9ac7367778..b4fa3d549f8 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 @@ -174,7 +174,7 @@ public class CapacityPolicies { 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); + return requestedCluster.withExclusivity(exclusive).withProvisionForApplication(exclusive); } } 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 09d6f96d88e..a876999e80b 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 @@ -22,14 +22,21 @@ public interface HostProvisioner { enum HostSharing { - /** The host must be provisioned exclusively for the applicationId */ + /** The host must be provisioned exclusively for the application ID. */ + provision, + + /** The host must be exclusive to a single application ID */ exclusive, /** The host must be provisioned to be shared with other applications. */ shared, /** The client has no requirements on whether the host must be provisioned exclusively or shared. */ - any + any; + + public boolean isExclusiveAllocation() { + return this == provision || this == exclusive; + } } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java index 0ffd42aedba..bd84ec9b05a 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/Preparer.java @@ -208,7 +208,9 @@ public class Preparer { private HostSharing hostSharing(ClusterSpec cluster, NodeType hostType) { if ( hostType.isSharable()) - return nodeRepository.exclusiveAllocation(cluster) ? HostSharing.exclusive : HostSharing.any; + return cluster.provisionForApplication() ? HostSharing.provision : + nodeRepository.exclusiveAllocation(cluster) ? HostSharing.exclusive : + HostSharing.any; else return HostSharing.any; } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockHostProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockHostProvisioner.java index def3e003ab3..57a5308288d 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockHostProvisioner.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockHostProvisioner.java @@ -78,8 +78,8 @@ public class MockHostProvisioner implements HostProvisioner { Flavor hostFlavor = hostFlavors.get(request.clusterType().orElse(ClusterSpec.Type.content)); if (hostFlavor == null) hostFlavor = flavors.stream() - .filter(f -> request.sharing() == HostSharing.exclusive ? compatible(f, request.resources()) - : satisfies(f, request.resources())) + .filter(f -> request.sharing().isExclusiveAllocation() ? compatible(f, request.resources()) + : satisfies(f, request.resources())) .filter(f -> realHostResourcesWithinLimits.test(f.resources())) .findFirst() .orElseThrow(() -> new NodeAllocationException("No host flavor matches " + request.resources(), true)); @@ -91,7 +91,7 @@ public class MockHostProvisioner implements HostProvisioner { hostHostname, hostFlavor, request.type(), - request.sharing() == HostSharing.exclusive ? Optional.of(request.owner()) : Optional.empty(), + request.sharing().isExclusiveAllocation() ? Optional.of(request.owner()) : Optional.empty(), Optional.empty(), createHostnames(request.type(), hostFlavor, index), request.resources(), |