aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorØyvind Grønnesby <oyving@yahooinc.com>2023-03-13 13:42:05 +0100
committerØyvind Grønnesby <oyving@yahooinc.com>2023-03-13 13:42:05 +0100
commit7fd6ffae120c1d5448def2ec9ac8d929180de1ea (patch)
tree121adca604c69c64785858e00a7cb6e321364220
parent030ee3c5abe1001453b8c03da955203ba630c755 (diff)
parente73005677cdbc12d1cb613e3020b4fa0dbaef268 (diff)
Merge remote-tracking branch 'origin/master' into ogronnesby/only-compare-major-version
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java17
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java11
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/NodeList.java11
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityChecker.java4
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisioner.java43
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Address.java17
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/IP.java232
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java10
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java26
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostIpConfig.java40
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostProvisioner.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisionedHost.java30
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java22
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java17
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java8
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/testutils/MockHostProvisioner.java25
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTester.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTester.java10
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainerTest.java5
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisionerTest.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/MetricsReporterTest.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeFailTester.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainerTest.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/node/IPTest.java8
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializerTest.java14
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicAllocationTest.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTester.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacityTest.java7
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidateTest.java2
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTest.java8
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/ProvisioningTester.java12
35 files changed, 266 insertions, 357 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
index 06c3aa5330e..91f24be3da3 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java
@@ -280,23 +280,6 @@ public class SessionPreparer {
vespaPreprocess(applicationPackageDir.getAbsoluteFile(), hostsXml, meta, tags);
}
- if (zone.system().isPublic()) {
- // Validate all other XML files
- try (var paths = Files.find(applicationPackageDir.getAbsoluteFile().toPath(), Integer.MAX_VALUE,
- (path, attr) -> attr.isRegularFile() && path.getFileName().toString().matches(".*\\.[Xx][Mm][Ll]"))) {
- paths.filter(p -> !(p.equals(servicesXml.getAbsoluteFile().toPath()) || p.equals(hostsXml.getAbsoluteFile().toPath())))
- .forEach(xmlPath -> {
- try {
- new ValidationProcessor().process(XML.getDocument(xmlPath.toFile()));
- } catch (IOException | TransformerException e) {
- throw new RuntimeException(e);
- }
- });
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
// Validate pom.xml files in OSGi bundles
try (var paths = Files.find(applicationPackageDir.getAbsoluteFile().toPath(), Integer.MAX_VALUE,
(path, attr) -> attr.isRegularFile() && path.getFileName().toString().matches(".*\\.[Jj][Aa][Rr]"))) {
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java
index f265ec3116c..778e288f316 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java
@@ -20,6 +20,13 @@ import java.util.stream.Collectors;
*/
public class ResourceSnapshot {
+ private static final NodeResources zero = new NodeResources(
+ 0, 0, 0, 0,
+ NodeResources.DiskSpeed.any,
+ NodeResources.StorageType.any,
+ NodeResources.Architecture.any,
+ NodeResources.GpuResources.zero());
+
private final ApplicationId applicationId;
private final NodeResources resources;
private final Instant timestamp;
@@ -53,7 +60,7 @@ public class ResourceSnapshot {
var resources = nodes.stream()
.map(Node::resources)
- .reduce(NodeResources.zero(), ResourceSnapshot::addResources);
+ .reduce(zero, ResourceSnapshot::addResources);
return new ResourceSnapshot(applicationIds.iterator().next(), resources, timestamp, zoneId, versions.iterator().next());
}
@@ -112,7 +119,7 @@ public class ResourceSnapshot {
0,
NodeResources.DiskSpeed.any,
NodeResources.StorageType.any,
- a.architecture(),
+ a.architecture() == NodeResources.Architecture.any ? b.architecture() : a.architecture(),
a.gpuResources().plus(b.gpuResources()));
}
}
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 ba99219638b..acfb5d6fa5e 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
@@ -116,11 +116,11 @@ public final class Node implements Nodelike {
if (state == State.ready && type.isHost()) {
requireNonEmpty(ipConfig.primary(), "A " + type + " must have at least one primary IP address in state " + state);
- requireNonEmpty(ipConfig.pool().ipSet(), "A " + type + " must have a non-empty IP address pool in state " + state);
+ requireNonEmpty(ipConfig.pool().asSet(), "A " + type + " must have a non-empty IP address pool in state " + state);
}
if (parentHostname.isPresent()) {
- if (!ipConfig.pool().ipSet().isEmpty()) throw new IllegalArgumentException("A child node cannot have an IP address pool");
+ if (!ipConfig.pool().asSet().isEmpty()) throw new IllegalArgumentException("A child node cannot have an IP address pool");
if (modelName.isPresent()) throw new IllegalArgumentException("A child node cannot have model name set");
if (switchHostname.isPresent()) throw new IllegalArgumentException("A child node cannot have switch hostname set");
if (status.wantToRebuild()) throw new IllegalArgumentException("A child node cannot be rebuilt");
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 800cf2150e9..1cf7bcfa4f2 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
@@ -9,7 +9,6 @@ import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.hosted.provision.node.ClusterId;
-import com.yahoo.vespa.hosted.provision.node.IP;
import java.util.ArrayList;
import java.util.Comparator;
@@ -330,22 +329,22 @@ public class NodeList extends AbstractFilteringList<Node, NodeList> {
/**
* Returns the number of unused IP addresses in the pool, assuming any and all unaccounted for hostnames
- * in the pool are resolved to exactly 1 IP address (or 2 with {@link IP.IpAddresses.Protocol#dualStack}).
+ * in the pool are resolved to exactly 1 IP address (or 2 if dual-stack).
*/
public int eventuallyUnusedIpAddressCount(Node host) {
// The count in this method relies on the size of the IP address pool if that's non-empty,
// otherwise fall back to the address/hostname pool.
- if (host.ipConfig().pool().ipSet().isEmpty()) {
+ if (host.ipConfig().pool().asSet().isEmpty()) {
Set<String> allHostnames = cache().keySet();
- return (int) host.ipConfig().pool().getAddressList().stream()
- .filter(address -> !allHostnames.contains(address.hostname()))
+ return (int) host.ipConfig().pool().hostnames().stream()
+ .filter(hostname -> !allHostnames.contains(hostname.value()))
.count();
}
Set<String> allIps = ipCache.updateAndGet(old ->
old != null ? old : stream().flatMap(node -> node.ipConfig().primary().stream())
.collect(Collectors.toUnmodifiableSet())
);
- return (int) host.ipConfig().pool().ipSet().stream()
+ return (int) host.ipConfig().pool().asSet().stream()
.filter(address -> !allIps.contains(address))
.count();
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityChecker.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityChecker.java
index 603056856e2..c8b736cb25b 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityChecker.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityChecker.java
@@ -127,12 +127,12 @@ public class CapacityChecker {
for (var host : hosts) {
NodeResources hostResources = host.flavor().resources();
int occupiedIps = 0;
- Set<String> ipPool = host.ipConfig().pool().ipSet();
+ Set<String> ipPool = host.ipConfig().pool().asSet();
for (var child : nodeChildren.get(host)) {
hostResources = hostResources.subtract(child.resources().justNumbers());
occupiedIps += child.ipConfig().primary().stream().filter(ipPool::contains).count();
}
- availableResources.put(host, new AllocationResources(hostResources, host.ipConfig().pool().ipSet().size() - occupiedIps));
+ availableResources.put(host, new AllocationResources(hostResources, host.ipConfig().pool().asSet().size() - occupiedIps));
}
return availableResources;
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisioner.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisioner.java
index c606ede05d1..86c5a926900 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisioner.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisioner.java
@@ -9,17 +9,14 @@ import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.provisioning.FatalProvisioningException;
+import com.yahoo.vespa.hosted.provision.provisioning.HostIpConfig;
import com.yahoo.vespa.hosted.provision.provisioning.HostProvisioner;
import com.yahoo.yolean.Exceptions;
import javax.naming.NamingException;
import java.time.Duration;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
-import java.util.stream.Collectors;
/**
* Resumes provisioning (requests additional IP addresses, updates DNS when IPs are ready) of hosts in state provisioned
@@ -41,23 +38,13 @@ public class HostResumeProvisioner extends NodeRepositoryMaintainer {
@Override
protected double maintain() {
NodeList allNodes = nodeRepository().nodes().list();
- Map<String, Set<Node>> nodesByProvisionedParentHostname =
- allNodes.nodeType(NodeType.tenant, NodeType.config, NodeType.controller)
- .asList()
- .stream()
- .filter(node -> node.parentHostname().isPresent())
- .collect(Collectors.groupingBy(node -> node.parentHostname().get(), Collectors.toSet()));
-
NodeList hosts = allNodes.state(Node.State.provisioned).nodeType(NodeType.host, NodeType.confighost, NodeType.controllerhost);
int failures = 0;
for (Node host : hosts) {
- Set<Node> children = nodesByProvisionedParentHostname.getOrDefault(host.hostname(), Set.of());
- // This doesn't actually require unallocated lock, but that is much easier than simultaneously holding
- // the application locks of the host and all it's children.
- try (var lock = nodeRepository().nodes().lockUnallocated()) {
- List<Node> updatedNodes = hostProvisioner.provision(host, children);
- verifyDns(updatedNodes);
- nodeRepository().nodes().write(updatedNodes, lock);
+ NodeList children = allNodes.childrenOf(host);
+ try {
+ HostIpConfig hostIpConfig = hostProvisioner.provision(host, children.asSet());
+ setIpConfig(host, children, hostIpConfig);
} catch (IllegalArgumentException | IllegalStateException e) {
log.log(Level.INFO, "Could not provision " + host.hostname() + " with " + children.size() + " children, will retry in " +
interval() + ": " + Exceptions.toMessageString(e));
@@ -79,13 +66,21 @@ public class HostResumeProvisioner extends NodeRepositoryMaintainer {
return asSuccessFactor(hosts.size(), failures);
}
- /** Verify DNS configuration of given nodes */
- private void verifyDns(List<Node> nodes) {
+ private void setIpConfig(Node host, NodeList children, HostIpConfig hostIpConfig) {
+ if (hostIpConfig.isEmpty()) return;
+ NodeList nodes = NodeList.of(host).and(children);
for (var node : nodes) {
- boolean enclave = node.cloudAccount().isEnclave(nodeRepository().zone());
- for (var ipAddress : node.ipConfig().primary()) {
- IP.verifyDns(node.hostname(), ipAddress, nodeRepository().nameResolver(), !enclave);
- }
+ verifyDns(node, hostIpConfig.require(node.hostname()));
+ }
+ nodeRepository().nodes().setIpConfig(hostIpConfig);
+ }
+
+ /** Verify DNS configuration of given node */
+ private void verifyDns(Node node, IP.Config ipConfig) {
+ boolean enclave = node.cloudAccount().isEnclave(nodeRepository().zone());
+ for (var ipAddress : ipConfig.primary()) {
+ IP.verifyDns(node.hostname(), ipAddress, nodeRepository().nameResolver(), !enclave);
}
}
+
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Address.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Address.java
deleted file mode 100644
index d7ef2228960..00000000000
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Address.java
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.provision.node;
-
-import java.util.Objects;
-
-/**
- * Address info about a container that might run on a host.
- *
- * @author hakon
- */
-public record Address(String hostname) {
- public Address {
- Objects.requireNonNull(hostname, "hostname cannot be null");
- if (hostname.isEmpty())
- throw new IllegalArgumentException("hostname cannot be empty");
- }
-}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/IP.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/IP.java
index b2becc7ecfd..708d84ba655 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/IP.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/IP.java
@@ -1,9 +1,9 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.node;
-import com.google.common.collect.ImmutableSet;
import com.google.common.net.InetAddresses;
import com.google.common.primitives.UnsignedBytes;
+import com.yahoo.config.provision.HostName;
import com.yahoo.vespa.hosted.provision.LockedNodeList;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
@@ -31,7 +31,7 @@ import static com.yahoo.config.provision.NodeType.proxyhost;
*
* @author mpolden
*/
-public class IP {
+public record IP() {
/** Comparator for sorting IP addresses by their natural order */
public static final Comparator<InetAddress> NATURAL_ORDER = (ip1, ip2) -> {
@@ -59,31 +59,26 @@ public class IP {
};
/** IP configuration of a node */
- public static class Config {
+ public record Config(Set<String> primary, Pool pool) {
public static final Config EMPTY = Config.ofEmptyPool(Set.of());
- private final Set<String> primary;
- private final Pool pool;
-
public static Config ofEmptyPool(Set<String> primary) {
- return new Config(primary, Set.of(), List.of());
+ return new Config(primary, Pool.EMPTY);
}
- public static Config of(Set<String> primary, Set<String> ipPool, List<Address> addressPool) {
- return new Config(primary, ipPool, addressPool);
+ public static Config of(Set<String> primary, Set<String> ipPool, List<HostName> hostnames) {
+ return new Config(primary, new Pool(IpAddresses.of(ipPool), hostnames));
}
- /** LEGACY TEST CONSTRUCTOR - use of() variants and/or the with- methods. */
- public Config(Set<String> primary, Set<String> pool) {
- this(primary, pool, List.of());
+ public static Config of(Set<String> primary, Set<String> pool) {
+ return of(primary, pool, List.of());
}
/** DO NOT USE: Public for NodeSerializer. */
- public Config(Set<String> primary, Set<String> pool, List<Address> addresses) {
- this.primary = ImmutableSet.copyOf(Objects.requireNonNull(primary, "primary must be non-null"));
- this.pool = Pool.of(Objects.requireNonNull(pool, "pool must be non-null"),
- Objects.requireNonNull(addresses, "addresses must be non-null"));
+ public Config(Set<String> primary, Pool pool) {
+ this.primary = Collections.unmodifiableSet(new LinkedHashSet<>(Objects.requireNonNull(primary, "primary must be non-null")));
+ this.pool = Objects.requireNonNull(pool, "pool must be non-null");
}
/** The primary addresses of this. These addresses are used when communicating with the node itself */
@@ -98,31 +93,12 @@ public class IP {
/** Returns a copy of this with pool set to given value */
public Config withPool(Pool pool) {
- return new Config(primary, pool.ipSet(), pool.getAddressList());
+ return new Config(primary, pool);
}
/** Returns a copy of this with pool set to given value */
public Config withPrimary(Set<String> primary) {
- return new Config(primary, pool.ipSet(), pool.getAddressList());
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Config config = (Config) o;
- return primary.equals(config.primary) &&
- pool.equals(config.pool);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(primary, pool);
- }
-
- @Override
- public String toString() {
- return String.format("ip config primary=%s pool=%s", primary, pool.ipSet());
+ return new Config(primary, pool);
}
/**
@@ -140,8 +116,8 @@ public class IP {
var addresses = new HashSet<>(node.ipConfig().primary());
var otherAddresses = new HashSet<>(other.ipConfig().primary());
if (node.type().isHost()) { // Addresses of a host can never overlap with any other nodes
- addresses.addAll(node.ipConfig().pool().ipSet());
- otherAddresses.addAll(other.ipConfig().pool().ipSet());
+ addresses.addAll(node.ipConfig().pool().asSet());
+ otherAddresses.addAll(other.ipConfig().pool().asSet());
}
otherAddresses.retainAll(addresses);
if (!otherAddresses.isEmpty())
@@ -158,12 +134,12 @@ public class IP {
if (node.parentHostname().isPresent() == existingNode.parentHostname().isPresent()) return false; // Not a parent-child node
if (node.parentHostname().isEmpty()) return canAssignIpOf(node, existingNode);
if (!node.parentHostname().get().equals(existingNode.hostname())) return false; // Wrong host
- switch (node.type()) {
- case proxy: return existingNode.type() == proxyhost;
- case config: return existingNode.type() == confighost;
- case controller: return existingNode.type() == controllerhost;
- }
- return false;
+ return switch (node.type()) {
+ case proxy -> existingNode.type() == proxyhost;
+ case config -> existingNode.type() == confighost;
+ case controller -> existingNode.type() == controllerhost;
+ default -> false;
+ };
}
public static Node verify(Node node, LockedNodeList allNodes) {
@@ -173,25 +149,13 @@ public class IP {
}
/** A list of IP addresses and their protocol */
- public static class IpAddresses {
+ record IpAddresses(Set<String> addresses, Protocol protocol) {
- private final Set<String> ipAddresses;
- private final Protocol protocol;
-
- private IpAddresses(Set<String> ipAddresses, Protocol protocol) {
- this.ipAddresses = ImmutableSet.copyOf(Objects.requireNonNull(ipAddresses, "addresses must be non-null"));
+ public IpAddresses(Set<String> addresses, Protocol protocol) {
+ this.addresses = Collections.unmodifiableSet(new LinkedHashSet<>(Objects.requireNonNull(addresses, "addresses must be non-null")));
this.protocol = Objects.requireNonNull(protocol, "type must be non-null");
}
- public Set<String> asSet() {
- return ipAddresses;
- }
-
- /** The protocol of addresses in this */
- public Protocol protocol() {
- return protocol;
- }
-
/** Create addresses of the given set */
private static IpAddresses of(Set<String> addresses) {
long ipv6AddrCount = addresses.stream().filter(IP::isV6).count();
@@ -216,6 +180,7 @@ public class IP {
}
public enum Protocol {
+
dualStack("dual-stack"),
ipv4("IPv4-only"),
ipv6("IPv6-only");
@@ -224,21 +189,8 @@ public class IP {
Protocol(String description) { this.description = description; }
- public String getDescription() { return description; }
}
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- IpAddresses that = (IpAddresses) o;
- return ipAddresses.equals(that.ipAddresses) && protocol == that.protocol;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(ipAddresses, protocol);
- }
}
/**
@@ -246,25 +198,23 @@ public class IP {
*
* Addresses in this are available for use by Linux containers.
*/
- public static class Pool {
+ public record Pool(IpAddresses ipAddresses, List<HostName> hostnames) {
- private final IpAddresses ipAddresses;
- private final List<Address> addresses;
-
- /** Creates an empty pool. */
- public static Pool of() {
- return of(Set.of(), List.of());
- }
+ public static final Pool EMPTY = new Pool(IpAddresses.of(Set.of()), List.of());
/** Create a new pool containing given ipAddresses */
- public static Pool of(Set<String> ipAddresses, List<Address> addresses) {
+ public static Pool of(Set<String> ipAddresses, List<HostName> hostnames) {
IpAddresses ips = IpAddresses.of(ipAddresses);
- return new Pool(ips, addresses);
+ return new Pool(ips, hostnames);
}
- private Pool(IpAddresses ipAddresses, List<Address> addresses) {
+ public Pool(IpAddresses ipAddresses, List<HostName> hostnames) {
this.ipAddresses = Objects.requireNonNull(ipAddresses, "ipAddresses must be non-null");
- this.addresses = Objects.requireNonNull(addresses, "addresses must be non-null");
+ this.hostnames = List.copyOf(Objects.requireNonNull(hostnames, "hostnames must be non-null"));
+ }
+
+ public Set<String> asSet() {
+ return ipAddresses.addresses;
}
/**
@@ -274,16 +224,15 @@ public class IP {
* @return an allocation from the pool, if any can be made
*/
public Optional<Allocation> findAllocation(LockedNodeList nodes, NameResolver resolver, boolean hasPtr) {
- if (ipAddresses.asSet().isEmpty()) {
+ if (ipAddresses.addresses.isEmpty()) {
// IP addresses have not yet been resolved and should be done later.
- return findUnusedAddressStream(nodes)
- .map(Allocation::ofAddress)
- .findFirst();
+ return findUnusedHostnames(nodes).map(Allocation::ofHostname)
+ .findFirst();
}
if (!hasPtr) {
// Without PTR records (reverse IP mapping): Ensure only forward resolving from hostnames.
- return findUnusedAddressStream(nodes).findFirst().map(address -> Allocation.fromAddress(address, resolver, ipAddresses.protocol));
+ return findUnusedHostnames(nodes).findFirst().map(address -> Allocation.fromHostname(address, resolver, ipAddresses.protocol));
}
if (ipAddresses.protocol == IpAddresses.Protocol.ipv4) {
@@ -313,64 +262,34 @@ public class IP {
* @param nodes a list of all nodes in the repository
*/
public Set<String> findUnusedIpAddresses(NodeList nodes) {
- var unusedAddresses = new LinkedHashSet<>(ipSet());
- nodes.matching(node -> node.ipConfig().primary().stream().anyMatch(ip -> ipSet().contains(ip)))
+ Set<String> unusedAddresses = new LinkedHashSet<>(asSet());
+ nodes.matching(node -> node.ipConfig().primary().stream().anyMatch(ip -> asSet().contains(ip)))
.forEach(node -> unusedAddresses.removeAll(node.ipConfig().primary()));
return Collections.unmodifiableSet(unusedAddresses);
}
- private Stream<Address> findUnusedAddressStream(NodeList nodes) {
- Set<String> hostnames = nodes.stream().map(Node::hostname).collect(Collectors.toSet());
- return addresses.stream().filter(address -> !hostnames.contains(address.hostname()));
- }
-
- public IpAddresses.Protocol getProtocol() {
- return ipAddresses.protocol;
- }
-
- /** Returns the IP addresses in this pool as a set */
- public Set<String> ipSet() {
- return ipAddresses.asSet();
- }
-
- public List<Address> getAddressList() {
- return addresses;
+ private Stream<HostName> findUnusedHostnames(NodeList nodes) {
+ Set<String> usedHostnames = nodes.stream().map(Node::hostname).collect(Collectors.toSet());
+ return hostnames.stream().filter(hostname -> !usedHostnames.contains(hostname.value()));
}
public Pool withIpAddresses(Set<String> ipAddresses) {
- return Pool.of(ipAddresses, addresses);
+ return Pool.of(ipAddresses, hostnames);
}
- public Pool withAddresses(List<Address> addresses) {
- return Pool.of(ipAddresses.ipAddresses, addresses);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Pool pool = (Pool) o;
- return ipAddresses.equals(pool.ipAddresses) && addresses.equals(pool.addresses);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(ipAddresses, addresses);
+ public Pool withHostnames(List<HostName> hostnames) {
+ return Pool.of(ipAddresses.addresses, hostnames);
}
}
/** An address allocation from a pool */
- public static class Allocation {
-
- private final String hostname;
- private final Optional<String> ipv4Address;
- private final Optional<String> ipv6Address;
+ public record Allocation(String hostname, Optional<String> ipv4Address, Optional<String> ipv6Address) {
- private Allocation(String hostname, Optional<String> ipv4Address, Optional<String> ipv6Address) {
- this.hostname = Objects.requireNonNull(hostname, "hostname must be non-null");
- this.ipv4Address = Objects.requireNonNull(ipv4Address, "ipv4Address must be non-null");
- this.ipv6Address = Objects.requireNonNull(ipv6Address, "ipv6Address must be non-null");
+ public Allocation {
+ Objects.requireNonNull(hostname, "hostname must be non-null");
+ Objects.requireNonNull(ipv4Address, "ipv4Address must be non-null");
+ Objects.requireNonNull(ipv6Address, "ipv6Address must be non-null");
}
/**
@@ -427,22 +346,22 @@ public class IP {
return new Allocation(hostname4, Optional.of(addresses.get(0)), Optional.empty());
}
- private static Allocation fromAddress(Address address, NameResolver resolver, IpAddresses.Protocol protocol) {
+ private static Allocation fromHostname(HostName hostname, NameResolver resolver, IpAddresses.Protocol protocol) {
// Resolve both A and AAAA to verify they match the protocol and to avoid surprises later on.
- Optional<String> ipv4Address = resolveOptional(address.hostname(), resolver, RecordType.A);
+ Optional<String> ipv4Address = resolveOptional(hostname.value(), resolver, RecordType.A);
if (protocol != IpAddresses.Protocol.ipv6 && ipv4Address.isEmpty())
- throw new IllegalArgumentException(protocol.description + " hostname " + address.hostname() + " did not resolve to an IPv4 address");
+ throw new IllegalArgumentException(protocol.description + " hostname " + hostname.value() + " did not resolve to an IPv4 address");
if (protocol == IpAddresses.Protocol.ipv6 && ipv4Address.isPresent())
- throw new IllegalArgumentException(protocol.description + " hostname " + address.hostname() + " has an IPv4 address: " + ipv4Address.get());
+ throw new IllegalArgumentException(protocol.description + " hostname " + hostname.value() + " has an IPv4 address: " + ipv4Address.get());
- Optional<String> ipv6Address = resolveOptional(address.hostname(), resolver, RecordType.AAAA);
+ Optional<String> ipv6Address = resolveOptional(hostname.value(), resolver, RecordType.AAAA);
if (protocol != IpAddresses.Protocol.ipv4 && ipv6Address.isEmpty())
- throw new IllegalArgumentException(protocol.description + " hostname " + address.hostname() + " did not resolve to an IPv6 address");
+ throw new IllegalArgumentException(protocol.description + " hostname " + hostname.value() + " did not resolve to an IPv6 address");
if (protocol == IpAddresses.Protocol.ipv4 && ipv6Address.isPresent())
- throw new IllegalArgumentException(protocol.description + " hostname " + address.hostname() + " has an IPv6 address: " + ipv6Address.get());
+ throw new IllegalArgumentException(protocol.description + " hostname " + hostname.value() + " has an IPv6 address: " + ipv6Address.get());
- return new Allocation(address.hostname(), ipv4Address, ipv6Address);
+ return new Allocation(hostname.value(), ipv4Address, ipv6Address);
}
private static Optional<String> resolveOptional(String hostname, NameResolver resolver, RecordType recordType) {
@@ -454,37 +373,14 @@ public class IP {
};
}
- private static Allocation ofAddress(Address address) {
- return new Allocation(address.hostname(), Optional.empty(), Optional.empty());
- }
-
- /** Hostname pointing to the IP addresses in this */
- public String hostname() {
- return hostname;
- }
-
- /** IPv4 address of this allocation */
- public Optional<String> ipv4Address() {
- return ipv4Address;
- }
-
- /** IPv6 address of this allocation */
- public Optional<String> ipv6Address() {
- return ipv6Address;
+ private static Allocation ofHostname(HostName hostName) {
+ return new Allocation(hostName.value(), Optional.empty(), Optional.empty());
}
/** All IP addresses in this */
public Set<String> addresses() {
- ImmutableSet.Builder<String> builder = ImmutableSet.builder();
- ipv4Address.ifPresent(builder::add);
- ipv6Address.ifPresent(builder::add);
- return builder.build();
- }
-
- @Override
- public String toString() {
- return String.format("Address allocation [hostname=%s, IPv4=%s, IPv6=%s]",
- hostname, ipv4Address.orElse("<none>"), ipv6Address.orElse("<none>"));
+ return Stream.concat(ipv4Address.stream(), ipv6Address.stream())
+ .collect(Collectors.toUnmodifiableSet());
}
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java
index 10f0c8aa554..6dc3b2b3193 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java
@@ -21,6 +21,7 @@ import com.yahoo.vespa.hosted.provision.applications.Applications;
import com.yahoo.vespa.hosted.provision.maintenance.NodeFailer;
import com.yahoo.vespa.hosted.provision.node.filter.NodeFilter;
import com.yahoo.vespa.hosted.provision.persistence.CuratorDb;
+import com.yahoo.vespa.hosted.provision.provisioning.HostIpConfig;
import com.yahoo.vespa.orchestrator.HostNameNotFoundException;
import com.yahoo.vespa.orchestrator.Orchestrator;
@@ -349,6 +350,15 @@ public class Nodes {
}
}
+ /** Update IP config for nodes in given config */
+ public void setIpConfig(HostIpConfig hostIpConfig) {
+ Predicate<Node> nodeInConfig = (node) -> hostIpConfig.contains(node.hostname());
+ performOn(nodeInConfig, (node, lock) -> {
+ IP.Config ipConfig = hostIpConfig.require(node.hostname());
+ return write(node.with(ipConfig), lock);
+ });
+ }
+
/**
* Parks this node and returns it in its new state.
*
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 39cccafb8ef..23ea14da4cc 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
@@ -14,6 +14,7 @@ import com.yahoo.config.provision.ClusterMembership;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.DockerImage;
import com.yahoo.config.provision.Flavor;
+import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.NodeFlavors;
import com.yahoo.config.provision.NodeResources;
@@ -28,7 +29,6 @@ import com.yahoo.slime.Inspector;
import com.yahoo.slime.Slime;
import com.yahoo.slime.SlimeUtils;
import com.yahoo.vespa.hosted.provision.Node;
-import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.Allocation;
import com.yahoo.vespa.hosted.provision.node.Generation;
@@ -167,8 +167,8 @@ public class NodeSerializer {
object.setString(hostnameKey, node.hostname());
object.setString(stateKey, toString(node.state()));
toSlime(node.ipConfig().primary(), object.setArray(ipAddressesKey));
- toSlime(node.ipConfig().pool().ipSet(), object.setArray(ipAddressPoolKey));
- toSlime(node.ipConfig().pool().getAddressList(), object);
+ toSlime(node.ipConfig().pool().asSet(), object.setArray(ipAddressPoolKey));
+ toSlime(node.ipConfig().pool().hostnames(), object);
object.setString(idKey, node.id());
node.parentHostname().ifPresent(hostname -> object.setString(parentHostnameKey, hostname));
toSlime(node.flavor(), object);
@@ -247,11 +247,11 @@ public class NodeSerializer {
ipAddresses.stream().map(IP::parse).sorted(IP.NATURAL_ORDER).map(IP::asString).forEach(array::addString);
}
- private void toSlime(List<Address> addresses, Cursor object) {
- if (addresses.isEmpty()) return;
- Cursor addressCursor = object.setArray(containersKey);
- addresses.forEach(address -> {
- addressCursor.addObject().setString(containerHostnameKey, address.hostname());
+ private void toSlime(List<HostName> hostnames, Cursor object) {
+ if (hostnames.isEmpty()) return;
+ Cursor containersArray = object.setArray(containersKey);
+ hostnames.forEach(hostname -> {
+ containersArray.addObject().setString(containerHostnameKey, hostname.value());
});
}
@@ -277,9 +277,9 @@ public class NodeSerializer {
private Node nodeFromSlime(Inspector object) {
Flavor flavor = flavorFromSlime(object);
return new Node(object.field(idKey).asString(),
- new IP.Config(ipAddressesFromSlime(object, ipAddressesKey),
- ipAddressesFromSlime(object, ipAddressPoolKey),
- addressesFromSlime(object)),
+ IP.Config.of(ipAddressesFromSlime(object, ipAddressesKey),
+ ipAddressesFromSlime(object, ipAddressPoolKey),
+ hostnamesFromSlime(object)),
object.field(hostnameKey).asString(),
SlimeUtils.optionalString(object.field(parentHostnameKey)),
flavor,
@@ -394,9 +394,9 @@ public class NodeSerializer {
return ipAddresses.build();
}
- private List<Address> addressesFromSlime(Inspector object) {
+ private List<HostName> hostnamesFromSlime(Inspector object) {
return SlimeUtils.entriesStream(object.field(containersKey))
- .map(elem -> new Address(elem.field(containerHostnameKey).asString()))
+ .map(elem -> HostName.of(elem.field(containerHostnameKey).asString()))
.toList();
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostIpConfig.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostIpConfig.java
new file mode 100644
index 00000000000..891251fc892
--- /dev/null
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/HostIpConfig.java
@@ -0,0 +1,40 @@
+// Copyright Yahoo. 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.vespa.hosted.provision.node.IP;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * IP config of a host and its children.
+ *
+ * @author mpolden
+ */
+public record HostIpConfig(Map<String, IP.Config> ipConfigByHostname) {
+
+ public static final HostIpConfig EMPTY = new HostIpConfig(Map.of());
+
+ public HostIpConfig(Map<String, IP.Config> ipConfigByHostname) {
+ this.ipConfigByHostname = Map.copyOf(Objects.requireNonNull(ipConfigByHostname));
+ }
+
+ public Map<String, IP.Config> asMap() {
+ return ipConfigByHostname;
+ }
+
+ public boolean contains(String hostname) {
+ return ipConfigByHostname.containsKey(hostname);
+ }
+
+ public IP.Config require(String hostname) {
+ IP.Config ipConfig = this.ipConfigByHostname.get(hostname);
+ if (ipConfig == null) throw new IllegalArgumentException("No IP config exists for node '" + hostname + "'");
+ return ipConfig;
+ }
+
+ public boolean isEmpty() {
+ return this.equals(EMPTY);
+ }
+
+}
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 f07185cbe60..ce48c5adab8 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
@@ -10,7 +10,6 @@ import com.yahoo.config.provision.NodeAllocationException;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.hosted.provision.Node;
-import com.yahoo.vespa.hosted.provision.NodeRepository;
import java.util.List;
import java.util.Optional;
@@ -75,12 +74,11 @@ public interface HostProvisioner {
*
* @param host the host to provision
* @param children list of all the nodes that run on the given host
- * @return a subset of {@code host} and {@code children} where the values have been modified and should
- * be written back to node-repository.
+ * @return IP config for the provisioned host and its children
* @throws FatalProvisioningException if the provisioning has irrecoverably failed and the input nodes
* should be deleted from node-repo.
*/
- List<Node> provision(Node host, Set<Node> children) throws FatalProvisioningException;
+ HostIpConfig provision(Node host, Set<Node> children) throws FatalProvisioningException;
/**
* Deprovisions a given host and resources associated with it and its children (such as DNS entries).
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java
index fa07782057b..b194730727f 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidate.java
@@ -181,11 +181,11 @@ public abstract class NodeCandidate implements Nodelike, Comparable<NodeCandidat
if ( ! lessThanHalfTheHost(this) && lessThanHalfTheHost(other)) return 1;
}
- // Prefer host with least skew
+ // Prefer host with the least skew
int hostPriority = hostPriority(other);
if (hostPriority != 0) return hostPriority;
- // Prefer node with cheapest flavor
+ // Prefer node with the cheapest flavor
if (this.flavor().cost() < other.flavor().cost()) return -1;
if (other.flavor().cost() < this.flavor().cost()) return 1;
@@ -199,7 +199,7 @@ public abstract class NodeCandidate implements Nodelike, Comparable<NodeCandidat
return Integer.compare(this.allocation().get().membership().index(),
other.allocation().get().membership().index());
- // Prefer host with latest OS version
+ // Prefer host with the latest OS version
Version thisHostOsVersion = this.parent.flatMap(host -> host.status().osVersion().current()).orElse(Version.emptyVersion);
Version otherHostOsVersion = other.parent.flatMap(host -> host.status().osVersion().current()).orElse(Version.emptyVersion);
if (thisHostOsVersion.isAfter(otherHostOsVersion)) return -1;
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 15a6b6ba523..d083d81c196 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
@@ -6,10 +6,10 @@ import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Flavor;
+import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.hosted.provision.Node;
-import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.node.OsVersion;
import com.yahoo.vespa.hosted.provision.node.Status;
@@ -32,38 +32,38 @@ public class ProvisionedHost {
private final NodeType hostType;
private final Optional<ApplicationId> exclusiveToApplicationId;
private final Optional<ClusterSpec.Type> exclusiveToClusterType;
- private final List<Address> nodeAddresses;
+ private final List<HostName> nodeHostnames;
private final NodeResources nodeResources;
private final Version osVersion;
private final CloudAccount cloudAccount;
public ProvisionedHost(String id, String hostHostname, Flavor hostFlavor, NodeType hostType,
Optional<ApplicationId> exclusiveToApplicationId, Optional<ClusterSpec.Type> exclusiveToClusterType,
- List<Address> nodeAddresses, NodeResources nodeResources, Version osVersion, CloudAccount cloudAccount) {
+ List<HostName> nodeHostnames, NodeResources nodeResources, Version osVersion, CloudAccount cloudAccount) {
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.exclusiveToClusterType = Objects.requireNonNull(exclusiveToClusterType, "exclusiveToClusterType must be set");
- this.nodeAddresses = validateNodeAddresses(nodeAddresses);
+ 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<Address> validateNodeAddresses(List<Address> nodeAddresses) {
- Objects.requireNonNull(nodeAddresses, "Node addresses must be set");
- if (nodeAddresses.isEmpty()) {
- throw new IllegalArgumentException("There must be at least one node address");
+ private static List<HostName> validateNodeAddresses(List<HostName> nodeHostnames) {
+ Objects.requireNonNull(nodeHostnames, "Node hostnames must be set");
+ if (nodeHostnames.isEmpty()) {
+ throw new IllegalArgumentException("There must be at least one node hostname");
}
- return nodeAddresses;
+ return nodeHostnames;
}
/** 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(), nodeAddresses), hostHostname, hostFlavor,
+ 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);
@@ -85,12 +85,12 @@ public class ProvisionedHost {
public NodeType hostType() { return hostType; }
public Optional<ApplicationId> exclusiveToApplicationId() { return exclusiveToApplicationId; }
public Optional<ClusterSpec.Type> exclusiveToClusterType() { return exclusiveToClusterType; }
- public List<Address> nodeAddresses() { return nodeAddresses; }
+ public List<HostName> nodeHostnames() { return nodeHostnames; }
public NodeResources nodeResources() { return nodeResources; }
public Version osVersion() { return osVersion; }
public CloudAccount cloudAccount() { return cloudAccount; }
- public String nodeHostname() { return nodeAddresses.get(0).hostname(); }
+ public String nodeHostname() { return nodeHostnames.get(0).value(); }
@Override
public boolean equals(Object o) {
@@ -103,7 +103,7 @@ public class ProvisionedHost {
hostType == that.hostType &&
exclusiveToApplicationId.equals(that.exclusiveToApplicationId) &&
exclusiveToClusterType.equals(that.exclusiveToClusterType) &&
- nodeAddresses.equals(that.nodeAddresses) &&
+ nodeHostnames.equals(that.nodeHostnames) &&
nodeResources.equals(that.nodeResources) &&
osVersion.equals(that.osVersion) &&
cloudAccount.equals(that.cloudAccount);
@@ -111,7 +111,7 @@ public class ProvisionedHost {
@Override
public int hashCode() {
- return Objects.hash(id, hostHostname, hostFlavor, hostType, exclusiveToApplicationId, exclusiveToClusterType, nodeAddresses, nodeResources, osVersion, cloudAccount);
+ return Objects.hash(id, hostHostname, hostFlavor, hostType, exclusiveToApplicationId, exclusiveToClusterType, nodeHostnames, nodeResources, osVersion, cloudAccount);
}
@Override
@@ -123,7 +123,7 @@ public class ProvisionedHost {
", hostType=" + hostType +
", exclusiveToApplicationId=" + exclusiveToApplicationId +
", exclusiveToClusterType=" + exclusiveToClusterType +
- ", nodeAddresses=" + nodeAddresses +
+ ", nodeAddresses=" + nodeHostnames +
", nodeResources=" + nodeResources +
", osVersion=" + osVersion +
", cloudAccount=" + cloudAccount +
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java
index 582d5963cfd..dfe01f5f1c3 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java
@@ -6,6 +6,7 @@ import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.DockerImage;
+import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeFlavors;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.TenantName;
@@ -19,7 +20,6 @@ import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeMutex;
import com.yahoo.vespa.hosted.provision.NodeRepository;
-import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.Allocation;
import com.yahoo.vespa.hosted.provision.node.IP;
@@ -245,12 +245,15 @@ public class NodePatcher {
private Node applyIpconfigField(Node node, String name, Inspector value, LockedNodeList nodes) {
switch (name) {
- case "ipAddresses":
+ case "ipAddresses" -> {
return IP.Config.verify(node.with(node.ipConfig().withPrimary(asStringSet(value))), nodes);
- case "additionalIpAddresses":
+ }
+ case "additionalIpAddresses" -> {
return IP.Config.verify(node.with(node.ipConfig().withPool(node.ipConfig().pool().withIpAddresses(asStringSet(value)))), nodes);
- case "additionalHostnames":
- return IP.Config.verify(node.with(node.ipConfig().withPool(node.ipConfig().pool().withAddresses(asAddressList(value)))), nodes);
+ }
+ case "additionalHostnames" -> {
+ return IP.Config.verify(node.with(node.ipConfig().withPool(node.ipConfig().pool().withHostnames(asHostnames(value)))), nodes);
+ }
}
throw new IllegalArgumentException("Could not apply field '" + name + "' on a node: No such modifiable field");
}
@@ -317,20 +320,19 @@ public class NodePatcher {
return strings;
}
- private List<Address> asAddressList(Inspector field) {
+ private List<HostName> asHostnames(Inspector field) {
if ( ! field.type().equals(Type.ARRAY))
throw new IllegalArgumentException("Expected an ARRAY value, got a " + field.type());
- List<Address> addresses = new ArrayList<>(field.entries());
+ List<HostName> hostnames = new ArrayList<>(field.entries());
for (int i = 0; i < field.entries(); i++) {
Inspector entry = field.entry(i);
if ( ! entry.type().equals(Type.STRING))
throw new IllegalArgumentException("Expected a STRING value, got a " + entry.type());
- Address address = new Address(entry.asString());
- addresses.add(address);
+ hostnames.add(HostName.of(entry.asString()));
}
- return addresses;
+ return hostnames;
}
private Node patchRequiredDiskSpeed(Node node, String value) {
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java
index 6bf07077c81..0e33e3461e7 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesResponse.java
@@ -6,18 +6,17 @@ import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.ClusterMembership;
import com.yahoo.config.provision.DockerImage;
+import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.serialization.NetworkPortsSerializer;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.restapi.SlimeJsonResponse;
import com.yahoo.slime.Cursor;
-import com.yahoo.vespa.applicationmodel.HostName;
import com.yahoo.vespa.flags.FetchVector;
import com.yahoo.vespa.flags.PermanentFlags;
import com.yahoo.vespa.flags.StringFlag;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
-import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.Allocation;
import com.yahoo.vespa.hosted.provision.node.History;
import com.yahoo.vespa.hosted.provision.node.TrustStoreItem;
@@ -52,7 +51,7 @@ class NodesResponse extends SlimeJsonResponse {
private final NodeFilter filter;
private final boolean recursive;
- private final Function<HostName, Optional<HostInfo>> orchestrator;
+ private final Function<com.yahoo.vespa.applicationmodel.HostName, Optional<HostInfo>> orchestrator;
private final NodeRepository nodeRepository;
private final StringFlag wantedDockerTagFlag;
@@ -151,7 +150,7 @@ class NodesResponse extends SlimeJsonResponse {
object.setString("wantedVespaVersion", allocation.membership().cluster().vespaVersion().toFullString());
NodeResourcesSerializer.toSlime(allocation.requestedResources(), object.setObject("requestedResources"));
allocation.networkPorts().ifPresent(ports -> NetworkPortsSerializer.toSlime(ports, object.setArray("networkPorts")));
- orchestrator.apply(new HostName(node.hostname()))
+ orchestrator.apply(new com.yahoo.vespa.applicationmodel.HostName(node.hostname()))
.ifPresent(info -> {
if (info.status() != HostStatus.NO_REMARKS) {
object.setString("orchestratorStatus", info.status().asString());
@@ -180,8 +179,8 @@ class NodesResponse extends SlimeJsonResponse {
toSlime(node.history().events(), object.setArray("history"));
toSlime(node.history().log(), object.setArray("log"));
ipAddressesToSlime(node.ipConfig().primary(), object.setArray("ipAddresses"));
- ipAddressesToSlime(node.ipConfig().pool().ipSet(), object.setArray("additionalIpAddresses"));
- addressesToSlime(node.ipConfig().pool().getAddressList(), object);
+ ipAddressesToSlime(node.ipConfig().pool().asSet(), object.setArray("additionalIpAddresses"));
+ hostnamesToSlime(node.ipConfig().pool().hostnames(), object);
node.reports().toSlime(object, "reports");
node.modelName().ifPresent(modelName -> object.setString("modelName", modelName));
node.switchHostname().ifPresent(switchHostname -> object.setString("switchHostname", switchHostname));
@@ -249,11 +248,11 @@ class NodesResponse extends SlimeJsonResponse {
ipAddresses.forEach(array::addString);
}
- private void addressesToSlime(List<Address> addresses, Cursor object) {
- if (addresses.isEmpty()) return;
+ private void hostnamesToSlime(List<HostName> hostnames, Cursor object) {
+ if (hostnames.isEmpty()) return;
// When/if Address becomes richer: add another field (e.g. "addresses") and expand to array of objects
Cursor addressesArray = object.setArray("additionalHostnames");
- addresses.forEach(address -> addressesArray.addString(address.hostname()));
+ hostnames.forEach(hostname -> addressesArray.addString(hostname.value()));
}
private void trustedCertsToSlime(List<TrustStoreItem> trustStoreItems, Cursor object) {
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java
index d0eb95e2d72..eb935ba6a5c 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java
@@ -7,6 +7,7 @@ import com.yahoo.config.provision.ApplicationLockException;
import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.HostFilter;
+import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeFlavors;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
@@ -33,7 +34,6 @@ import com.yahoo.vespa.hosted.provision.NodeMutex;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.applications.Application;
import com.yahoo.vespa.hosted.provision.autoscale.Load;
-import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.node.filter.ApplicationFilter;
@@ -281,12 +281,12 @@ public class NodesV2ApiHandler extends ThreadedHttpRequestHandler {
Set<String> ipAddressPool = new HashSet<>();
inspector.field("additionalIpAddresses").traverse((ArrayTraverser) (i, item) -> ipAddressPool.add(item.asString()));
- List<Address> addressPool = new ArrayList<>();
+ List<HostName> hostnames = new ArrayList<>();
inspector.field("additionalHostnames").traverse((ArrayTraverser) (i, item) ->
- addressPool.add(new Address(item.asString())));
+ hostnames.add(HostName.of(item.asString())));
Node.Builder builder = Node.create(inspector.field("id").asString(),
- IP.Config.of(ipAddresses, ipAddressPool, addressPool),
+ IP.Config.of(ipAddresses, ipAddressPool, hostnames),
inspector.field("hostname").asString(),
flavorFromSlime(inspector),
nodeTypeFromSlime(inspector.field("type")))
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 7e83e265496..24ea9361823 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
@@ -7,14 +7,15 @@ import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.HostEvent;
+import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeAllocationException;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.hosted.provision.Node;
-import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.provisioning.FatalProvisioningException;
+import com.yahoo.vespa.hosted.provision.provisioning.HostIpConfig;
import com.yahoo.vespa.hosted.provision.provisioning.HostProvisioner;
import com.yahoo.vespa.hosted.provision.provisioning.HostResourcesCalculator;
import com.yahoo.vespa.hosted.provision.provisioning.ProvisionedHost;
@@ -84,7 +85,7 @@ public class MockHostProvisioner implements HostProvisioner {
hostType,
sharing == HostSharing.exclusive ? Optional.of(applicationId) : Optional.empty(),
Optional.empty(),
- createAddressesForHost(hostType, hostFlavor, index),
+ createHostnames(hostType, hostFlavor, index),
resources,
osVersion,
cloudAccount));
@@ -94,16 +95,16 @@ public class MockHostProvisioner implements HostProvisioner {
}
@Override
- public List<Node> provision(Node host, Set<Node> children) throws FatalProvisioningException {
+ public HostIpConfig provision(Node host, Set<Node> children) throws FatalProvisioningException {
if (behaviours.contains(Behaviour.failProvisioning)) throw new FatalProvisioningException("Failed to provision node(s)");
if (host.state() != Node.State.provisioned) throw new IllegalStateException("Host to provision must be in " + Node.State.provisioned);
- List<Node> result = new ArrayList<>();
- result.add(withIpAssigned(host));
+ Map<String, IP.Config> result = new HashMap<>();
+ result.put(host.hostname(), createIpConfig(host));
for (var child : children) {
if (child.state() != Node.State.reserved) throw new IllegalStateException("Child to provisioned must be in " + Node.State.reserved);
- result.add(withIpAssigned(child));
+ result.put(child.hostname(), createIpConfig(child));
}
- return result;
+ return new HostIpConfig(result);
}
@Override
@@ -195,21 +196,21 @@ public class MockHostProvisioner implements HostProvisioner {
return flavor.resources().compatibleWith(resourcesToVerify);
}
- private List<Address> createAddressesForHost(NodeType hostType, Flavor flavor, int hostIndex) {
+ private List<HostName> createHostnames(NodeType hostType, Flavor flavor, int hostIndex) {
long numAddresses = Math.max(2, Math.round(flavor.resources().bandwidthGbps()));
return IntStream.range(1, (int) numAddresses)
.mapToObj(i -> {
String hostname = hostType == NodeType.host
? "host" + hostIndex + "-" + i
: hostType.childNodeType().name() + i;
- return new Address(hostname);
+ return HostName.of(hostname);
})
.toList();
}
- public Node withIpAssigned(Node node) {
+ public IP.Config createIpConfig(Node node) {
if (!node.type().isHost()) {
- return node.with(node.ipConfig().withPrimary(nameResolver.resolveAll(node.hostname())));
+ return node.ipConfig().withPrimary(nameResolver.resolveAll(node.hostname()));
}
int hostIndex = Integer.parseInt(node.hostname().replaceAll("^[a-z]+|-\\d+$", ""));
Set<String> addresses = Set.of("::" + hostIndex + ":0");
@@ -223,7 +224,7 @@ public class MockHostProvisioner implements HostProvisioner {
}
}
IP.Pool pool = node.ipConfig().pool().withIpAddresses(ipAddressPool);
- return node.with(node.ipConfig().withPrimary(addresses).withPool(pool));
+ return node.ipConfig().withPrimary(addresses).withPool(pool);
}
public enum Behaviour {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTester.java
index b964bf871c1..4d0b3e75740 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/NodeRepositoryTester.java
@@ -74,7 +74,7 @@ public class NodeRepositoryTester {
private Node addNode(String id, String hostname, String parentHostname, Flavor flavor, NodeType type) {
Set<String> ips = nodeRepository.nameResolver().resolveAll(hostname);
- IP.Config ipConfig = new IP.Config(ips, type.isHost() ? ips : Set.of());
+ IP.Config ipConfig = IP.Config.of(ips, type.isHost() ? ips : Set.of());
Node node = Node.create(id, ipConfig, hostname, flavor, type).parentHostname(parentHostname).build();
return nodeRepository.nodes().addNodes(List.of(node), Agent.system).get(0);
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTester.java
index 606bc55fdd2..f8ec271ce5f 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTester.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityCheckerTester.java
@@ -156,8 +156,8 @@ public class CapacityCheckerTester {
.collect(Collectors.toSet());
NodeResources nr = containingNodeResources(childResources, excessCapacity);
- Node node = Node.create(hostname, new IP.Config(Set.of("::"), availableIps), hostname,
- new Flavor(nr), NodeType.host).build();
+ Node node = Node.create(hostname, IP.Config.of(Set.of("::"), availableIps), hostname,
+ new Flavor(nr), NodeType.host).build();
hosts.computeIfAbsent(tenantHostApp, (ignored) -> new ArrayList<>())
.add(node);
}
@@ -175,8 +175,8 @@ public class CapacityCheckerTester {
Set<String> availableIps = IntStream.range(2000, 2000 + ips)
.mapToObj(n -> String.format("%04X::%04X", hostId, n))
.collect(Collectors.toSet());
- Node node = Node.create(hostname, new IP.Config(Set.of("::" + (1000 + hostId)), availableIps), hostname,
- new Flavor(capacity), NodeType.host).build();
+ Node node = Node.create(hostname, IP.Config.of(Set.of("::" + (1000 + hostId)), availableIps), hostname,
+ new Flavor(capacity), NodeType.host).build();
hosts.add(node);
}
return hosts;
@@ -290,7 +290,7 @@ public class CapacityCheckerTester {
Flavor f = new Flavor(nr);
Node.Builder builder = Node.create(nodeModel.id, nodeModel.hostname, f, nodeModel.state, nodeModel.type)
- .ipConfig(new IP.Config(nodeModel.ipAddresses, nodeModel.additionalIpAddresses));
+ .ipConfig(IP.Config.of(nodeModel.ipAddresses, nodeModel.additionalIpAddresses));
nodeModel.parentHostname.ifPresent(builder::parentHostname);
Node node = builder.build();
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainerTest.java
index e075995c89e..0a1bdb7b400 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostCapacityMaintainerTest.java
@@ -27,7 +27,6 @@ import com.yahoo.vespa.flags.custom.ClusterCapacity;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.NodeRepository;
-import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.Allocation;
import com.yahoo.vespa.hosted.provision.node.Generation;
@@ -651,9 +650,9 @@ public class HostCapacityMaintainerTest {
flavor.resources(),
Generation.initial(),
false));
- List<Address> addresses = Stream.of(additionalHostnames).map(Address::new).toList();
+ List<com.yahoo.config.provision.HostName> hostnames = Stream.of(additionalHostnames).map(com.yahoo.config.provision.HostName::of).toList();
Node.Builder builder = Node.create("fake-id-" + hostname, hostname, flavor, state, nodeType)
- .ipConfig(new IP.Config(state == Node.State.active ? Set.of("::1") : Set.of(), Set.of(), addresses));
+ .ipConfig(IP.Config.of(state == Node.State.active ? Set.of("::1") : Set.of(), Set.of(), hostnames));
parentHostname.ifPresent(builder::parentHostname);
allocation.ifPresent(builder::allocation);
if (hostname.equals("host2-1"))
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisionerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisionerTest.java
index 5e507d447ab..8280c0e33fc 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisionerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostResumeProvisionerTest.java
@@ -78,12 +78,12 @@ public class HostResumeProvisionerTest {
hostResumeProvisioner.maintain();
assertTrue("No IP addresses written as DNS updates are failing",
- provisioning.get().stream().allMatch(host -> host.ipConfig().pool().ipSet().isEmpty()));
+ provisioning.get().stream().allMatch(host -> host.ipConfig().pool().asSet().isEmpty()));
hostProvisioner.without(MockHostProvisioner.Behaviour.failDnsUpdate);
hostResumeProvisioner.maintain();
assertTrue("IP addresses written as DNS updates are succeeding",
- provisioning.get().stream().noneMatch(host -> host.ipConfig().pool().ipSet().isEmpty()));
+ provisioning.get().stream().noneMatch(host -> host.ipConfig().pool().asSet().isEmpty()));
}
@Test
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 d7ffda542ff..065c6e015b6 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
@@ -186,7 +186,7 @@ public class MetricsReporterTest {
// Allow 4 containers
Set<String> ipAddressPool = Set.of("::2", "::3", "::4", "::5");
- Node dockerHost = Node.create("node-id-1", new IP.Config(Set.of("::1"), ipAddressPool), "dockerHost",
+ Node dockerHost = Node.create("node-id-1", IP.Config.of(Set.of("::1"), ipAddressPool), "dockerHost",
nodeFlavors.getFlavorOrThrow("host"), NodeType.host).build();
nodeRepository.nodes().addNodes(List.of(dockerHost), Agent.system);
nodeRepository.nodes().deallocateRecursively("dockerHost", Agent.system, getClass().getSimpleName());
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 1b0826a8323..a07a4f2c72a 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
@@ -244,8 +244,8 @@ public class NodeFailTester {
for (int i = startIndex; i < startIndex + count; i++) {
String hostname = "host" + i;
Set<String> ipPool = nodeType.isHost() ? Set.of("127.0." + i + "." + (++lastOctetOfPoolAddress)) : Set.of();
- IP.Config ipConfig = new IP.Config(nodeRepository.nameResolver().resolveAll(hostname),
- ipPool);
+ IP.Config ipConfig = IP.Config.of(nodeRepository.nameResolver().resolveAll(hostname),
+ ipPool);
Node.Builder builder = Node.create("node" + i, ipConfig, hostname, flavor, nodeType);
parentHostname.ifPresent(builder::parentHostname);
nodes.add(builder.build());
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java
index f5e524a90cc..58f4be18992 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/RetiredExpirerTest.java
@@ -239,8 +239,8 @@ public class RetiredExpirerTest {
MockNameResolver nameResolver = (MockNameResolver) tester.nodeRepository().nameResolver();
String ipv4 = "127.0.1.4";
nameResolver.addRecord(retiredNode.hostname(), ipv4);
- Node node = Node.create(retiredNode.hostname(), new IP.Config(Set.of(ipv4), Set.of()), retiredNode.hostname(),
- tester.asFlavor("default", NodeType.config), NodeType.config).build();
+ Node node = Node.create(retiredNode.hostname(), IP.Config.of(Set.of(ipv4), Set.of()), retiredNode.hostname(),
+ tester.asFlavor("default", NodeType.config), NodeType.config).build();
var nodes = List.of(node);
nodes = nodeRepository.nodes().addNodes(nodes, Agent.system);
nodes = nodeRepository.nodes().deallocate(nodes, Agent.system, getClass().getSimpleName());
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainerTest.java
index c9421f098e7..b54975cbf41 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/SpareCapacityMaintainerTest.java
@@ -327,8 +327,8 @@ public class SpareCapacityMaintainerTest {
}
private IP.Config ipConfig(int id, boolean host) {
- return new IP.Config(Set.of(String.format("%04X::%04X", id, 0)),
- host ? IntStream.range(0, 10)
+ return IP.Config.of(Set.of(String.format("%04X::%04X", id, 0)),
+ host ? IntStream.range(0, 10)
.mapToObj(n -> String.format("%04X::%04X", id, n))
.collect(Collectors.toSet())
: Set.of());
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/node/IPTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/node/IPTest.java
index c26ffdaa023..88fe88dbaad 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/node/IPTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/node/IPTest.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.provision.node;
import com.google.common.collect.ImmutableSet;
+import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeFlavors;
import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.hosted.provision.LockedNodeList;
@@ -14,7 +15,6 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
-import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -156,7 +156,7 @@ public class IPTest {
IP.Config config = IP.Config.of(Set.of("2600:1f10:::1"),
Set.of("2600:1f10:::2", "2600:1f10:::3"),
- List.of(new Address("node1"), new Address("node2")));
+ List.of(HostName.of("node1"), HostName.of("node2")));
IP.Pool pool = config.pool();
Optional<IP.Allocation> allocation = pool.findAllocation(emptyList, resolver, false);
}
@@ -193,12 +193,12 @@ public class IPTest {
}
IP.Pool pool = node.ipConfig().pool();
- assertNotEquals(dualStack, pool.getProtocol() == IP.IpAddresses.Protocol.ipv4);
+ assertNotEquals(dualStack, pool.ipAddresses().protocol() == IP.IpAddresses.Protocol.ipv4);
return pool;
}
private static Node createNode(Set<String> ipAddresses) {
- return Node.create("id1", new IP.Config(Set.of("127.0.0.1"), ipAddresses),
+ return Node.create("id1", IP.Config.of(Set.of("127.0.0.1"), ipAddresses),
"host1", nodeFlavors.getFlavorOrThrow("default"), NodeType.host).build();
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializerTest.java
index d61a3d95a65..1086f2026a8 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializerTest.java
@@ -9,6 +9,7 @@ import com.yahoo.config.provision.ApplicationName;
import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.ClusterMembership;
import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.NetworkPorts;
import com.yahoo.config.provision.NodeFlavors;
@@ -23,7 +24,6 @@ import com.yahoo.test.ManualClock;
import com.yahoo.text.Utf8;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.Node.State;
-import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.Generation;
import com.yahoo.vespa.hosted.provision.node.History;
@@ -249,7 +249,7 @@ public class NodeSerializerTest {
@Test
public void serialize_parent_hostname() {
final String parentHostname = "parent.yahoo.com";
- Node node = Node.create("myId", new IP.Config(Set.of("127.0.0.1"), Set.of()), "myHostname", nodeFlavors.getFlavorOrThrow("default"), NodeType.tenant)
+ Node node = Node.create("myId", IP.Config.of(Set.of("127.0.0.1"), Set.of()), "myHostname", nodeFlavors.getFlavorOrThrow("default"), NodeType.tenant)
.parentHostname(parentHostname)
.build();
@@ -272,16 +272,14 @@ public class NodeSerializerTest {
// Test round-trip with address pool
node = node.with(node.ipConfig().withPool(IP.Pool.of(
Set.of("::1", "::2", "::3"),
- List.of(new Address("a"), new Address("b"), new Address("c")))));
+ List.of(HostName.of("a"), HostName.of("b"), HostName.of("c")))));
Node copy = nodeSerializer.fromJson(nodeSerializer.toJson(node));
- assertEquals(node.ipConfig().pool().ipSet(), copy.ipConfig().pool().ipSet());
- assertEquals(Set.copyOf(node.ipConfig().pool().getAddressList()), Set.copyOf(copy.ipConfig().pool().getAddressList()));
+ assertEquals(node.ipConfig(), copy.ipConfig());
// Test round-trip without address pool (handle empty pool)
node = createNode();
copy = nodeSerializer.fromJson(nodeSerializer.toJson(node));
- assertEquals(node.ipConfig().pool().ipSet(), copy.ipConfig().pool().ipSet());
- assertEquals(Set.copyOf(node.ipConfig().pool().getAddressList()), Set.copyOf(copy.ipConfig().pool().getAddressList()));
+ assertEquals(node.ipConfig(), copy.ipConfig());
}
@Test
@@ -529,7 +527,7 @@ public class NodeSerializerTest {
private Node createNode() {
return Node.create("myId",
- new IP.Config(Set.of("127.0.0.1"), Set.of()),
+ IP.Config.of(Set.of("127.0.0.1"), Set.of()),
"myHostname",
nodeFlavors.getFlavorOrThrow("default"),
NodeType.tenant).build();
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicAllocationTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicAllocationTest.java
index 23c2d0fc47a..47d34a76dd6 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicAllocationTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicAllocationTest.java
@@ -534,7 +534,7 @@ public class DynamicAllocationTest {
}
private void addAndAssignNode(ApplicationId id, String hostname, String parentHostname, ClusterSpec clusterSpec, NodeResources flavor, int index, ProvisioningTester tester) {
- Node node1a = Node.create("open1", new IP.Config(Set.of("127.0.233." + index), Set.of()), hostname,
+ Node node1a = Node.create("open1", IP.Config.of(Set.of("127.0.233." + index), Set.of()), hostname,
new Flavor(flavor), NodeType.tenant).parentHostname(parentHostname).build();
ClusterMembership clusterMembership1 = ClusterMembership.from(
clusterSpec.with(Optional.of(ClusterSpec.Group.from(0))), index); // Need to add group here so that group is serialized in node allocation
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicProvisioningTest.java
index 0e19c48591d..382d2840377 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
@@ -453,7 +453,7 @@ public class DynamicProvisioningTest {
if (!provisionedHosts.isEmpty()) {
List<Node> hosts = provisionedHosts.asList()
.stream()
- .map(h -> ((MockHostProvisioner)tester.hostProvisioner()).withIpAssigned(h))
+ .map(h -> h.with(((MockHostProvisioner) tester.hostProvisioner()).createIpConfig(h)))
.toList();
tester.move(Node.State.ready, hosts);
tester.activateTenantHosts();
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 a24eb61bb79..0ab7040bdb4 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
@@ -124,9 +124,9 @@ public class DynamicProvisioningTester {
public void makeReady(String hostname) {
Node node = nodeRepository().nodes().node(hostname).get();
- provisioningTester.patchNode(node, (n) -> n.with(new IP.Config(Set.of("::" + 0 + ":0"), Set.of())));
+ provisioningTester.patchNode(node, (n) -> n.with(IP.Config.of(Set.of("::" + 0 + ":0"), Set.of())));
Node host = nodeRepository().nodes().node(node.parentHostname().get()).get();
- host = host.with(new IP.Config(Set.of("::" + 0 + ":0"), Set.of("::" + 0 + ":2")));
+ host = host.with(IP.Config.of(Set.of("::" + 0 + ":0"), Set.of("::" + 0 + ":2")));
if (host.state() == Node.State.provisioned)
provisioningTester.move(Node.State.ready, host);
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacityTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacityTest.java
index ea2af5f3fca..12a8e4d9386 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacityTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/HostCapacityTest.java
@@ -2,12 +2,12 @@
package com.yahoo.vespa.hosted.provision.provisioning;
import com.yahoo.config.provision.Flavor;
+import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeFlavors;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.hosted.provision.LockedNodeList;
import com.yahoo.vespa.hosted.provision.Node;
-import com.yahoo.vespa.hosted.provision.node.Address;
import com.yahoo.vespa.hosted.provision.node.IP;
import org.junit.Before;
import org.junit.Test;
@@ -16,7 +16,6 @@ import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
-import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.junit.Assert.assertEquals;
@@ -167,7 +166,7 @@ public class HostCapacityTest {
}
private Node setupHostWithAdditionalHostnames(String hostHostname, String... additionalHostnames) {
- List<Address> addresses = Stream.of(additionalHostnames).map(Address::new).toList();
+ List<HostName> hostnames = Stream.of(additionalHostnames).map(HostName::of).toList();
doAnswer(invocation -> ((Flavor)invocation.getArguments()[0]).resources())
.when(hostResourcesCalculator).advertisedResourcesOf(any());
@@ -176,7 +175,7 @@ public class HostCapacityTest {
"host", // 7-100-120-5
"docker"); // 2- 40- 40-0.5 = resources1
- return Node.create(hostHostname, IP.Config.of(Set.of("::1"), Set.of(), addresses), hostHostname,
+ return Node.create(hostHostname, IP.Config.of(Set.of("::1"), Set.of(), hostnames), hostHostname,
nodeFlavors.getFlavorOrThrow("host"), NodeType.host).build();
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidateTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidateTest.java
index 20819daf356..32db213c445 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidateTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidateTest.java
@@ -146,7 +146,7 @@ public class NodeCandidateTest {
.parentHostname(hostname + "parent")
.ipConfigWithEmptyPool(Set.of("::1")).build();
Node parent = Node.create(hostname + "parent", hostname, new Flavor(totalHostResources), Node.State.ready, NodeType.host)
- .ipConfig(new IP.Config(Set.of("::1"), Set.of("::2")))
+ .ipConfig(IP.Config.of(Set.of("::1"), Set.of("::2")))
.build();
return new NodeCandidate.ConcreteNodeCandidate(node, totalHostResources.subtract(allocatedHostResources), Optional.of(parent),
false, exclusiveSwitch, false, true, false);
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 4c404e3030c..978edf3f7e4 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
@@ -919,10 +919,10 @@ public class ProvisioningTest {
// Add 2 config server hosts and 2 config servers
Flavor flavor = tester.nodeRepository().flavors().getFlavorOrThrow("default");
List<Node> nodes = List.of(
- Node.create("cfghost1", new IP.Config(Set.of("::1:0"), Set.of("::1:1")), "cfghost1", flavor, NodeType.confighost).build(),
- Node.create("cfghost2", new IP.Config(Set.of("::2:0"), Set.of("::2:1")), "cfghost2", flavor, NodeType.confighost).ipConfig(IP.Config.of(Set.of("::2:0"), Set.of("::2:1"), List.of())).build(),
- Node.create("cfg1", new IP.Config(Set.of("::1:1"), Set.of()), "cfg1", flavor, NodeType.config).parentHostname("cfghost1").build(),
- Node.create("cfg2", new IP.Config(Set.of("::2:1"), Set.of()), "cfg2", flavor, NodeType.config).parentHostname("cfghost2").build());
+ Node.create("cfghost1", IP.Config.of(Set.of("::1:0"), Set.of("::1:1")), "cfghost1", flavor, NodeType.confighost).build(),
+ Node.create("cfghost2", IP.Config.of(Set.of("::2:0"), Set.of("::2:1")), "cfghost2", flavor, NodeType.confighost).ipConfig(IP.Config.of(Set.of("::2:0"), Set.of("::2:1"), List.of())).build(),
+ Node.create("cfg1", IP.Config.of(Set.of("::1:1"), Set.of()), "cfg1", flavor, NodeType.config).parentHostname("cfghost1").build(),
+ Node.create("cfg2", IP.Config.of(Set.of("::2:1"), Set.of()), "cfg2", flavor, NodeType.config).parentHostname("cfghost2").build());
tester.move(Node.State.ready, tester.nodeRepository().nodes().addNodes(nodes, Agent.system));
InfraApplication cfgHostApp = new ConfigServerHostApplication();
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 06f26bc1d8b..d0ff11fde0c 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
@@ -201,7 +201,7 @@ public class ProvisioningTester {
try (var lock = nodeRepository.nodes().lockAndGetRequired(prepared.hostname())) {
Node node = lock.node();
if (node.ipConfig().primary().isEmpty()) {
- node = node.with(new IP.Config(Set.of("::" + 0 + ":0"), Set.of()));
+ node = node.with(IP.Config.of(Set.of("::" + 0 + ":0"), Set.of()));
nodeRepository.nodes().write(node, lock);
}
if (node.parentHostname().isEmpty()) continue;
@@ -209,7 +209,7 @@ public class ProvisioningTester {
if (parent.state() == Node.State.active) continue;
NestedTransaction t = new NestedTransaction();
if (parent.ipConfig().primary().isEmpty())
- parent = parent.with(new IP.Config(Set.of("::" + 0 + ":0"), Set.of("::" + 0 + ":2")));
+ parent = parent.with(IP.Config.of(Set.of("::" + 0 + ":0"), Set.of("::" + 0 + ":2")));
nodeRepository.nodes().activate(List.of(parent), t);
t.commit();
}
@@ -447,7 +447,7 @@ public class ProvisioningTester {
nameResolver.addRecord(nodeHostname, ipv4Addr);
}
}
- Node.Builder builder = Node.create(hostname, new IP.Config(hostIps, ipAddressPool), hostname, flavor, type);
+ Node.Builder builder = Node.create(hostname, IP.Config.of(hostIps, ipAddressPool), hostname, flavor, type);
reservedTo.ifPresent(builder::reservedTo);
nodes.add(builder.build());
}
@@ -464,8 +464,8 @@ public class ProvisioningTester {
String ipv4 = "127.0.1." + i;
nameResolver.addRecord(hostname, ipv4);
- Node node = Node.create(hostname, new IP.Config(Set.of(ipv4), Set.of()), hostname,
- nodeFlavors.getFlavorOrThrow(flavor), NodeType.config).build();
+ Node node = Node.create(hostname, IP.Config.of(Set.of(ipv4), Set.of()), hostname,
+ nodeFlavors.getFlavorOrThrow(flavor), NodeType.config).build();
nodes.add(node);
}
@@ -532,7 +532,7 @@ public class ProvisioningTester {
List<Node> nodes = new ArrayList<>(count);
for (int i = startIndex; i < count + startIndex; i++) {
String hostname = nodeNamer.apply(i);
- IP.Config ipConfig = new IP.Config(nodeRepository.nameResolver().resolveAll(hostname), Set.of());
+ IP.Config ipConfig = IP.Config.of(nodeRepository.nameResolver().resolveAll(hostname), Set.of());
Node node = Node.create("node-id", ipConfig, hostname, new Flavor(resources), nodeType)
.parentHostname(parentHostname)
.build();