diff options
author | Valerij Fredriksen <freva@users.noreply.github.com> | 2023-02-16 15:25:04 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-16 15:25:04 +0100 |
commit | f68b22568ecc5b979b0cca5a2650ad1f26f83f99 (patch) | |
tree | 1e3dee42c3a8d7095a7947335ec8994f05f8d955 | |
parent | e0f320476f2fb4eb219fad0467b189e550ffa0cb (diff) | |
parent | bcdedc66632ebb40a96047d33d656e9290e16a6d (diff) |
Merge pull request #26067 from vespa-engine/mpolden/ip-update-lock
Lock node when updating IP config
34 files changed, 260 insertions, 341 deletions
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidatorTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidatorTest.java index a7947aff283..b4b817da2f0 100644 --- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidatorTest.java +++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidatorTest.java @@ -278,7 +278,7 @@ public class InstanceValidatorTest { MockNodeFlavors flavors = new MockNodeFlavors(); List<Node> nodeList = new ArrayList<>(); for (int i = 0; i < num; i++) { - Node node = Node.create("foo" + i, new IP.Config(Set.of("::1" + i, "::2" + i, "::3" + i), Set.of()), + Node node = Node.create("foo" + i, IP.Config.of(Set.of("::1" + i, "::2" + i, "::3" + i), Set.of()), "foo" + i, flavors.getFlavorOrThrow("default"), NodeType.tenant).build(); nodeList.add(node); } 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 79382a8bf5b..31283998702 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 30bac174629..9134c376f38 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; @@ -354,6 +355,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 38fa1abf8e2..3144f42a92c 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; @@ -72,12 +71,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 64f3ff9f5ba..8c74409d771 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; @@ -244,12 +244,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"); } @@ -316,20 +319,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 49e6265b6da..f98c4ba1199 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 543901621d0..dadef5ce243 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; @@ -280,12 +280,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 20612e2ad66..84856ab310b 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 @@ -3,19 +3,19 @@ package com.yahoo.vespa.hosted.provision.testutils; import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; -import com.yahoo.config.provision.Cloud; 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.ProvisionedHost; @@ -23,12 +23,13 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; -import java.util.stream.Collectors; import java.util.stream.IntStream; /** @@ -80,7 +81,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)); @@ -90,16 +91,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 @@ -175,21 +176,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"); @@ -203,7 +204,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/autoscale/AutoscalingTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java index 19c6ce16674..7c75b07eb47 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/autoscale/AutoscalingTester.java @@ -105,9 +105,9 @@ class AutoscalingTester { 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/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 4a74bca872e..e6d056d126d 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 @@ -26,7 +26,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; @@ -650,9 +649,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 44050fa747c..2187611b702 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 @@ -176,7 +176,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 bead36c5464..f406f44f02f 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/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 471fabb1206..68857719bf0 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 0ab6e31b20f..e1747a910c9 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(); |