summaryrefslogtreecommitdiffstats
path: root/node-repository
diff options
context:
space:
mode:
authorjonmv <venstad@gmail.com>2023-05-25 08:48:52 +0200
committerjonmv <venstad@gmail.com>2023-05-26 10:38:45 +0200
commit4d5ec01f49319a76183bd7cce4682b0365cf39a4 (patch)
tree0c87fc07cecbc21a4d039ba0aaa9f72bfd292889 /node-repository
parent419ea33bd064a0cedfbfa11cfba2bba1356b3f2c (diff)
Wire hostTTL from config model through provisioning
Diffstat (limited to 'node-repository')
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/GroupPreparer.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostProvisioner.java2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeRepositoryProvisioner.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeSpec.java21
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisionedHost.java16
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockHostProvisioner.java5
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java1
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java2
9 files changed, 43 insertions, 18 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java
index a4bc3a1aea5..28320743964 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainer.java
@@ -197,7 +197,8 @@ public class HostCapacityMaintainer extends NodeRepositoryMaintainer {
List<Integer> provisionIndices = nodeRepository().database().readProvisionIndices(count);
List<Node> hosts = new ArrayList<>();
hostProvisioner.provisionHosts(provisionIndices, NodeType.host, nodeResources, ApplicationId.defaultId(), osVersion,
- HostSharing.shared, Optional.empty(), Optional.empty(), nodeRepository().zone().cloud().account(),
+ HostSharing.shared, Optional.empty(), Optional.empty(), Duration.ZERO,
+ nodeRepository().zone().cloud().account(),
provisionedHosts -> {
hosts.addAll(provisionedHosts.stream().map(ProvisionedHost::generateHost).toList());
nodeRepository().nodes().addNodes(hosts, Agent.HostCapacityMaintainer);
@@ -246,7 +247,8 @@ public class HostCapacityMaintainer extends NodeRepositoryMaintainer {
// build() requires a version, even though it is not (should not be) used
.vespaVersion(Vtag.currentVersion)
.build();
- NodeSpec nodeSpec = NodeSpec.from(clusterCapacity.count(), nodeResources, false, true, nodeRepository().zone().cloud().account());
+ NodeSpec nodeSpec = NodeSpec.from(clusterCapacity.count(), nodeResources, false, true,
+ nodeRepository().zone().cloud().account(), Duration.ZERO);
int wantedGroups = 1;
NodePrioritizer prioritizer = new NodePrioritizer(allNodes, applicationId, clusterSpec, nodeSpec, wantedGroups,
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 06c1916dd4f..0fa6d3c57fa 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
@@ -114,8 +114,8 @@ public class GroupPreparer {
try {
hostProvisioner.get().provisionHosts(
allocation.provisionIndices(deficit.count()), hostType, deficit.resources(), application,
- osVersion, sharing, Optional.of(cluster.type()), Optional.of(cluster.id()), requestedNodes.cloudAccount(),
- provisionedHostsConsumer);
+ osVersion, sharing, Optional.of(cluster.type()), Optional.of(cluster.id()), requestedNodes.hostTTL(),
+ requestedNodes.cloudAccount(), provisionedHostsConsumer);
} catch (NodeAllocationException e) {
// Mark the nodes that were written to ZK in the consumer for deprovisioning. While these hosts do
// not exist, we cannot remove them from ZK here because other nodes may already have been
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 ce48c5adab8..d4a0b7de057 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
@@ -11,6 +11,7 @@ import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.hosted.provision.Node;
+import java.time.Duration;
import java.util.List;
import java.util.Optional;
import java.util.Set;
@@ -66,6 +67,7 @@ public interface HostProvisioner {
HostSharing sharing,
Optional<ClusterSpec.Type> clusterType,
Optional<ClusterSpec.Id> clusterId,
+ Duration hostTTL,
CloudAccount cloudAccount,
Consumer<List<ProvisionedHost>> provisionedHostConsumer) throws NodeAllocationException;
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 c8d20d89dfa..ffd2805bcff 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
@@ -38,7 +38,6 @@ import java.util.List;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
-import java.util.stream.Stream;
/**
* Implementation of the host provisioner API for hosted Vespa, using the node repository to allocate nodes.
@@ -101,7 +100,8 @@ public class NodeRepositoryProvisioner implements Provisioner {
groups = target.groups();
resources = getNodeResources(cluster, target.nodeResources(), application);
nodeSpec = NodeSpec.from(target.nodes(), resources, cluster.isExclusive(), actual.canFail(),
- requested.cloudAccount().orElse(nodeRepository.zone().cloud().account()));
+ requested.cloudAccount().orElse(nodeRepository.zone().cloud().account()),
+ requested.clusterInfo().hostTTL());
}
else {
groups = 1; // type request with multiple groups is not supported
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 28d1e7c1c68..bfe6f4211cb 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
@@ -7,6 +7,7 @@ import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.hosted.provision.Node;
+import java.time.Duration;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -67,6 +68,9 @@ public interface NodeSpec {
/** Returns the cloud account to use when fulfilling this spec */
CloudAccount cloudAccount();
+ /** Returns the host TTL to use for any hosts provisioned as a result of this fulfilling this spec. */
+ default Duration hostTTL() { return Duration.ZERO; }
+
/**
* Returns true if a node with given current resources and current spare host resources can be resized
* in-place to resources in this spec.
@@ -76,8 +80,9 @@ public interface NodeSpec {
return false;
}
- static NodeSpec from(int nodeCount, NodeResources resources, boolean exclusive, boolean canFail, CloudAccount cloudAccount) {
- return new CountNodeSpec(nodeCount, resources, exclusive, canFail, canFail, cloudAccount);
+ static NodeSpec from(int nodeCount, NodeResources resources, boolean exclusive, boolean canFail,
+ CloudAccount cloudAccount, Duration hostTTL) {
+ return new CountNodeSpec(nodeCount, resources, exclusive, canFail, canFail, cloudAccount, hostTTL);
}
static NodeSpec from(NodeType type, CloudAccount cloudAccount) {
@@ -93,14 +98,17 @@ public interface NodeSpec {
private final boolean canFail;
private final boolean considerRetiring;
private final CloudAccount cloudAccount;
+ private final Duration hostTTL;
- private CountNodeSpec(int count, NodeResources resources, boolean exclusive, boolean canFail, boolean considerRetiring, CloudAccount cloudAccount) {
+ private CountNodeSpec(int count, NodeResources resources, boolean exclusive, boolean canFail,
+ boolean considerRetiring, CloudAccount cloudAccount, Duration hostTTL) {
this.count = count;
this.requestedNodeResources = Objects.requireNonNull(resources, "Resources must be specified");
this.exclusive = exclusive;
this.canFail = canFail;
this.considerRetiring = considerRetiring;
this.cloudAccount = Objects.requireNonNull(cloudAccount);
+ this.hostTTL = Objects.requireNonNull(hostTTL);
if (!canFail && considerRetiring)
throw new IllegalArgumentException("Cannot consider retiring nodes if we cannot fail");
@@ -145,11 +153,11 @@ public interface NodeSpec {
@Override
public NodeSpec fraction(int divisor) {
- return new CountNodeSpec(count/divisor, requestedNodeResources, exclusive, canFail, considerRetiring, cloudAccount);
+ return new CountNodeSpec(count/divisor, requestedNodeResources, exclusive, canFail, considerRetiring, cloudAccount, hostTTL);
}
public NodeSpec withoutRetiring() {
- return new CountNodeSpec(count, requestedNodeResources, exclusive, canFail, false, cloudAccount);
+ return new CountNodeSpec(count, requestedNodeResources, exclusive, canFail, false, cloudAccount, hostTTL);
}
@Override
@@ -187,6 +195,9 @@ public interface NodeSpec {
}
@Override
+ public Duration hostTTL() { return hostTTL; }
+
+ @Override
public String toString() { return "request for " + count + " nodes with " + requestedNodeResources; }
}
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 98afc6e7482..3b3a5e3a82b 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
@@ -14,6 +14,7 @@ import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.node.OsVersion;
import com.yahoo.vespa.hosted.provision.node.Status;
+import java.time.Duration;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -32,6 +33,7 @@ public class ProvisionedHost {
private final NodeType hostType;
private final Optional<ApplicationId> exclusiveToApplicationId;
private final Optional<ClusterSpec.Type> exclusiveToClusterType;
+ private final Duration hostTTL;
private final List<HostName> nodeHostnames;
private final NodeResources nodeResources;
private final Version osVersion;
@@ -39,18 +41,20 @@ public class ProvisionedHost {
public ProvisionedHost(String id, String hostHostname, Flavor hostFlavor, NodeType hostType,
Optional<ApplicationId> exclusiveToApplicationId, Optional<ClusterSpec.Type> exclusiveToClusterType,
- List<HostName> nodeHostnames, NodeResources nodeResources, Version osVersion, CloudAccount cloudAccount) {
+ Duration hostTTL, List<HostName> nodeHostnames, NodeResources nodeResources,
+ Version osVersion, CloudAccount cloudAccount) {
+ if (!hostType.isHost()) throw new IllegalArgumentException(hostType + " is not a host");
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.hostType = Objects.requireNonNull(hostType, "Host type must be set");
this.exclusiveToApplicationId = Objects.requireNonNull(exclusiveToApplicationId, "exclusiveToApplicationId must be set");
+ this.hostTTL = Objects.requireNonNull(hostTTL, "hostTTL must be set");
this.exclusiveToClusterType = Objects.requireNonNull(exclusiveToClusterType, "exclusiveToClusterType must be set");
this.nodeHostnames = validateNodeAddresses(nodeHostnames);
this.nodeResources = Objects.requireNonNull(nodeResources, "Node resources must be set");
this.osVersion = Objects.requireNonNull(osVersion, "OS version must be set");
this.cloudAccount = Objects.requireNonNull(cloudAccount, "Cloud account must be set");
- if (!hostType.isHost()) throw new IllegalArgumentException(hostType + " is not a host");
}
private static List<HostName> validateNodeAddresses(List<HostName> nodeHostnames) {
@@ -63,12 +67,12 @@ public class ProvisionedHost {
/** Generate {@link Node} instance representing the provisioned physical host */
public Node generateHost() {
- Node.Builder builder = Node.create(id, IP.Config.of(Set.of(), Set.of(), nodeHostnames), hostHostname, hostFlavor,
- hostType)
+ Node.Builder builder = Node.create(id, IP.Config.of(Set.of(), Set.of(), nodeHostnames), hostHostname, hostFlavor, hostType)
.status(Status.initial().withOsVersion(OsVersion.EMPTY.withCurrent(Optional.of(osVersion))))
.cloudAccount(cloudAccount);
exclusiveToApplicationId.ifPresent(builder::exclusiveToApplicationId);
exclusiveToClusterType.ifPresent(builder::exclusiveToClusterType);
+ if ( ! hostTTL.isZero()) builder.hostTTL(hostTTL);
return builder.build();
}
@@ -103,6 +107,7 @@ public class ProvisionedHost {
hostType == that.hostType &&
exclusiveToApplicationId.equals(that.exclusiveToApplicationId) &&
exclusiveToClusterType.equals(that.exclusiveToClusterType) &&
+ hostTTL.equals(that.hostTTL) &&
nodeHostnames.equals(that.nodeHostnames) &&
nodeResources.equals(that.nodeResources) &&
osVersion.equals(that.osVersion) &&
@@ -111,7 +116,7 @@ public class ProvisionedHost {
@Override
public int hashCode() {
- return Objects.hash(id, hostHostname, hostFlavor, hostType, exclusiveToApplicationId, exclusiveToClusterType, nodeHostnames, nodeResources, osVersion, cloudAccount);
+ return Objects.hash(id, hostHostname, hostFlavor, hostType, exclusiveToApplicationId, exclusiveToClusterType, hostTTL, nodeHostnames, nodeResources, osVersion, cloudAccount);
}
@Override
@@ -123,6 +128,7 @@ public class ProvisionedHost {
", hostType=" + hostType +
", exclusiveToApplicationId=" + exclusiveToApplicationId +
", exclusiveToClusterType=" + exclusiveToClusterType +
+ ", hostTTL=" + hostTTL +
", nodeAddresses=" + nodeHostnames +
", nodeResources=" + nodeResources +
", osVersion=" + osVersion +
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 24ea9361823..962c45cff5b 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
@@ -20,6 +20,7 @@ import com.yahoo.vespa.hosted.provision.provisioning.HostProvisioner;
import com.yahoo.vespa.hosted.provision.provisioning.HostResourcesCalculator;
import com.yahoo.vespa.hosted.provision.provisioning.ProvisionedHost;
+import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
@@ -67,7 +68,8 @@ public class MockHostProvisioner implements HostProvisioner {
public void provisionHosts(List<Integer> provisionIndices, NodeType hostType, NodeResources resources,
ApplicationId applicationId, Version osVersion, HostSharing sharing,
Optional<ClusterSpec.Type> clusterType, Optional<ClusterSpec.Id> clusterId,
- CloudAccount cloudAccount, Consumer<List<ProvisionedHost>> provisionedHostsConsumer) {
+ Duration hostTTL, CloudAccount cloudAccount,
+ Consumer<List<ProvisionedHost>> provisionedHostsConsumer) {
Flavor hostFlavor = hostFlavors.get(clusterType.orElse(ClusterSpec.Type.content));
if (hostFlavor == null)
hostFlavor = flavors.stream()
@@ -85,6 +87,7 @@ public class MockHostProvisioner implements HostProvisioner {
hostType,
sharing == HostSharing.exclusive ? Optional.of(applicationId) : Optional.empty(),
Optional.empty(),
+ hostTTL,
createHostnames(hostType, hostFlavor, index),
resources,
osVersion,
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java
index 382d2840377..60ac30293a3 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java
@@ -85,6 +85,7 @@ public class DynamicProvisioningTest {
assertEquals(20, tester.nodeRepository().nodes().list().size());
assertEquals(8, tester.nodeRepository().nodes().list(Node.State.active).nodeType(NodeType.host).size());
assertEquals(12, tester.nodeRepository().nodes().list(Node.State.active).nodeType(NodeType.tenant).size());
+ assert false: "TODO: test with TTL";
}
@Test
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 3107d9738a9..f0794fdc4a1 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
@@ -86,7 +86,7 @@ public class ProvisioningTest {
// deploy another application
SystemState state1App2 = prepare(application2, 2, 2, 3, 3, defaultResources, tester);
- assertFalse("Hosts to different apps are disjunct", state1App2.allHosts.removeAll(state1.allHosts));
+ assertFalse("Hosts to different apps are disjoint", state1App2.allHosts.removeAll(state1.allHosts));
tester.activate(application2, state1App2.allHosts);
// prepare twice