aboutsummaryrefslogtreecommitdiffstats
path: root/node-repository
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2019-05-15 10:29:05 +0200
committerMartin Polden <mpolden@mpolden.no>2019-05-15 14:41:23 +0200
commit3906d228fc5204b34e105b91aa5aad68b8d813ce (patch)
tree6c23ab1ea3f818f59c6b4c4aa47fc580168a88d8 /node-repository
parent7834918c224fce3ab427810ce298917b3729f37b (diff)
Move all IP configuration to IP.Config
Diffstat (limited to 'node-repository')
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/Node.java69
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/IP.java156
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java2
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/NodeSerializer.java10
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodePatcher.java5
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostProvisionMaintainerTest.java13
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTest.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/node/IPTest.java16
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/SerializationTest.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java9
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java5
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java2
12 files changed, 177 insertions, 118 deletions
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 188829a24ba..803939c33c3 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
@@ -1,7 +1,6 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision;
-import com.google.common.collect.ImmutableSet;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterMembership;
import com.yahoo.config.provision.Flavor;
@@ -30,9 +29,8 @@ import java.util.Set;
*/
public final class Node {
- private final Set<String> ipAddresses;
- private final IP.AddressPool ipAddressPool;
private final String hostname;
+ private final IP.Config ipConfig;
private final String id;
private final Optional<String> parentHostname;
private final Flavor flavor;
@@ -50,22 +48,23 @@ public final class Node {
/** Temporary method until we can merge it with the other create method */
public static Node createDockerNode(Set<String> ipAddresses, Set<String> ipAddressPool, String hostname, Optional<String> parentHostname, NodeResources resources, NodeType type) {
- return new Node("fake-" + hostname, ipAddresses, ipAddressPool, hostname, parentHostname, new Flavor(resources), Status.initial(), State.reserved,
+ return new Node("fake-" + hostname, new IP.Config(ipAddresses, ipAddressPool), hostname, parentHostname, new Flavor(resources), Status.initial(), State.reserved,
Optional.empty(), History.empty(), type, new Reports(), Optional.empty());
}
/** Creates a node in the initial state (provisioned) */
public static Node create(String openStackId, Set<String> ipAddresses, Set<String> ipAddressPool, String hostname, Optional<String> parentHostname, Optional<String> modelName, Flavor flavor, NodeType type) {
- return new Node(openStackId, ipAddresses, ipAddressPool, hostname, parentHostname, flavor, Status.initial(), State.provisioned,
+ return new Node(openStackId, new IP.Config(ipAddresses, ipAddressPool), hostname, parentHostname, flavor, Status.initial(), State.provisioned,
Optional.empty(), History.empty(), type, new Reports(), modelName);
}
/** Creates a node. See also the {@code create} helper methods. */
- public Node(String id, Set<String> ipAddresses, Set<String> ipAddressPool, String hostname, Optional<String> parentHostname,
+ public Node(String id, IP.Config ipConfig, String hostname, Optional<String> parentHostname,
Flavor flavor, Status status, State state, Optional<Allocation> allocation, History history, NodeType type,
Reports reports, Optional<String> modelName) {
Objects.requireNonNull(id, "A node must have an ID");
requireNonEmptyString(hostname, "A node must have a hostname");
+ Objects.requireNonNull(ipConfig, "A node must a have an IP config");
requireNonEmptyString(parentHostname, "A parent host name must be a proper value");
Objects.requireNonNull(flavor, "A node must have a flavor");
Objects.requireNonNull(status, "A node must have a status");
@@ -77,13 +76,12 @@ public final class Node {
Objects.requireNonNull(modelName, "A null modelName is not permitted");
if (state == State.active)
- requireNonEmpty(ipAddresses, "An active node must have at least one valid IP address");
- if (parentHostname.isPresent() && !ipAddressPool.isEmpty())
+ requireNonEmpty(ipConfig.primary(), "An active node must have at least one valid IP address");
+ if (parentHostname.isPresent() && !ipConfig.pool().asSet().isEmpty())
throw new IllegalArgumentException("A child node cannot have an IP address pool");
- this.ipAddresses = ImmutableSet.copyOf(ipAddresses);
- this.ipAddressPool = new IP.AddressPool(this, ipAddressPool);
this.hostname = hostname;
+ this.ipConfig = ipConfig;
this.parentHostname = parentHostname;
this.id = id;
this.flavor = flavor;
@@ -97,11 +95,16 @@ public final class Node {
}
/** Returns the IP addresses of this node */
- public Set<String> ipAddresses() { return ipAddresses; }
+ // TODO: Remove and make callers access this through ipConfig()
+ public Set<String> ipAddresses() { return ipConfig.primary(); }
/** Returns the IP address pool available on this node. These IP addresses are available for use by containers
* running on this node */
- public IP.AddressPool ipAddressPool() { return ipAddressPool; }
+ // TODO: Remove and make callers access this through ipConfig()
+ public IP.Pool ipAddressPool() { return ipConfig.pool(); }
+
+ /** Returns the IP config of this node */
+ public IP.Config ipConfig() { return ipConfig; }
/** Returns the host name of this node */
public String hostname() { return hostname; }
@@ -196,31 +199,31 @@ public final class Node {
/** Returns a node with the status assigned to the given value */
public Node with(Status status) {
- return new Node(id, ipAddresses, ipAddressPool.asSet(), hostname, parentHostname, flavor, status, state, allocation, history, type, reports, modelName);
+ return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history, type, reports, modelName);
}
/** Returns a node with the type assigned to the given value */
public Node with(NodeType type) {
- return new Node(id, ipAddresses, ipAddressPool.asSet(), hostname, parentHostname, flavor, status, state, allocation, history, type, reports, modelName);
+ return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history, type, reports, modelName);
}
/** Returns a node with the flavor assigned to the given value */
public Node with(Flavor flavor) {
- return new Node(id, ipAddresses, ipAddressPool.asSet(), hostname, parentHostname, flavor, status, state, allocation, history, type, reports, modelName);
+ return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history, type, reports, modelName);
}
/** Returns a copy of this with the reboot generation set to generation */
public Node withReboot(Generation generation) {
- return new Node(id, ipAddresses, ipAddressPool.asSet(), hostname, parentHostname, flavor, status.withReboot(generation), state, allocation, history, type, reports, modelName);
+ return new Node(id, ipConfig, hostname, parentHostname, flavor, status.withReboot(generation), state, allocation, history, type, reports, modelName);
}
/** Returns a copy of this with the openStackId set */
public Node withOpenStackId(String openStackId) {
- return new Node(openStackId, ipAddresses, ipAddressPool.asSet(), hostname, parentHostname, flavor, status, state, allocation, history, type, reports, modelName);
+ return new Node(openStackId, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history, type, reports, modelName);
}
public Node withModelName(String modelName) {
- return new Node(id, ipAddresses, ipAddressPool.asSet(), hostname, parentHostname, flavor, status, state, allocation, history, type, reports, Optional.of(modelName));
+ return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history, type, reports, Optional.of(modelName));
}
/** Returns a copy of this with a history record saying it was detected to be down at this instant */
@@ -244,33 +247,27 @@ public final class Node {
* Do not use this to allocate a node.
*/
public Node with(Allocation allocation) {
- return new Node(id, ipAddresses, ipAddressPool.asSet(), hostname, parentHostname, flavor, status, state,
- Optional.of(allocation), history, type, reports, modelName);
+ return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state,
+ Optional.of(allocation), history, type, reports, modelName);
}
/** Returns a new Node without an allocation. */
public Node withoutAllocation() {
- return new Node(id, ipAddresses, ipAddressPool.asSet(), hostname, parentHostname, flavor, status, state,
- Optional.empty(), history, type, reports, modelName);
+ return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state,
+ Optional.empty(), history, type, reports, modelName);
}
- /** Returns a copy of this node with the IP addresses set to the given value. */
- public Node withIpAddresses(Set<String> ipAddresses) {
- return new Node(id, ipAddresses, ipAddressPool.asSet(), hostname, parentHostname, flavor, status, state,
- allocation, history, type, reports, modelName);
- }
-
- /** Returns a copy of this node with IP address pool set to the given value. */
- public Node withIpAddressPool(Set<String> ipAddressPool) {
- return new Node(id, ipAddresses, ipAddressPool, hostname, parentHostname, flavor, status, state,
- allocation, history, type, reports, modelName);
+ /** Returns a copy of this node with IP config set to the given value. */
+ public Node with(IP.Config ipConfig) {
+ return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state,
+ allocation, history, type, reports, modelName);
}
/** Returns a copy of this node with the parent hostname assigned to the given value. */
public Node withParentHostname(String parentHostname) {
- return new Node(id, ipAddresses, ipAddressPool.asSet(), hostname, Optional.of(parentHostname), flavor, status, state,
- allocation, history, type, reports, modelName);
+ return new Node(id, ipConfig, hostname, Optional.of(parentHostname), flavor, status, state,
+ allocation, history, type, reports, modelName);
}
/** Returns a copy of this node with the current reboot generation set to the given number at the given instant */
@@ -284,11 +281,11 @@ public final class Node {
/** Returns a copy of this node with the given history. */
public Node with(History history) {
- return new Node(id, ipAddresses, ipAddressPool.asSet(), hostname, parentHostname, flavor, status, state, allocation, history, type, reports, modelName);
+ return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history, type, reports, modelName);
}
public Node with(Reports reports) {
- return new Node(id, ipAddresses, ipAddressPool.asSet(), hostname, parentHostname, flavor, status, state, allocation, history, type, reports, modelName);
+ return new Node(id, ipConfig, hostname, parentHostname, flavor, status, state, allocation, history, type, reports, modelName);
}
private static void requireNonEmptyString(Optional<String> value, String message) {
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 7433b92e1fa..221f40239eb 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
@@ -4,7 +4,6 @@ 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.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.persistence.NameResolver;
@@ -20,14 +19,14 @@ import java.util.Set;
import java.util.stream.Collectors;
/**
- * Represents IP addresses owned by a node.
+ * This handles IP address configuration and allocation.
*
* @author mpolden
*/
public class IP {
/** Comparator for sorting IP addresses by their natural order */
- public static final Comparator<String> naturalOrder = (ip1, ip2) -> {
+ public static final Comparator<String> NATURAL_ORDER = (ip1, ip2) -> {
byte[] address1 = InetAddresses.forString(ip1).getAddress();
byte[] address2 = InetAddresses.forString(ip2).getAddress();
@@ -51,14 +50,77 @@ public class IP {
return 0;
};
- /** A pool of available IP addresses */
- public static class AddressPool {
+ /** IP configuration of a node */
+ public static class Config {
+
+ public static final Config EMPTY = new Config(Set.of(), Set.of());
+
+ private final Set<String> primary;
+ private final Pool pool;
+
+ /** DO NOT USE in non-test code. Public for serialization purposes. */
+ public Config(Set<String> primary, Set<String> pool) {
+ this.primary = ImmutableSet.copyOf(Objects.requireNonNull(primary, "primary must be non-null"));
+ this.pool = new Pool(Objects.requireNonNull(pool, "pool must be non-null"));
+ }
+
+ /** The primary addresses of this. These addresses are used when communicating with the node itself */
+ public Set<String> primary() {
+ return primary;
+ }
+
+ /** Returns the IP address pool available on a node */
+ public Pool pool() {
+ return pool;
+ }
+
+ /** Returns a copy of this with pool set to given value */
+ public Config with(Pool pool) {
+ return new Config(primary, pool.asSet());
+ }
+
+ /** Returns a copy of this with pool set to given value */
+ public Config with(Set<String> primary) {
+ return new Config(require(primary), pool.asSet());
+ }
+
+ @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.asSet());
+ }
+
+ /** Validates and returns the given addresses */
+ public static Set<String> require(Set<String> addresses) {
+ try {
+ addresses.forEach(InetAddresses::forString);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Found one or more invalid addresses in " + addresses, e);
+ }
+ return addresses;
+ }
+
+ }
+
+ /** A pool of IP addresses. Addresses in this are destined for use by Docker containers */
+ public static class Pool {
- private final Node owner;
private final Set<String> addresses;
- public AddressPool(Node owner, Set<String> addresses) {
- this.owner = Objects.requireNonNull(owner, "owner must be non-null");
+ private Pool(Set<String> addresses) {
this.addresses = ImmutableSet.copyOf(Objects.requireNonNull(addresses, "addresses must be non-null"));
}
@@ -69,18 +131,17 @@ public class IP {
* @return An allocation from the pool, if any can be made
*/
public Optional<Allocation> findAllocation(NodeList nodes, NameResolver resolver) {
- Set<String> unusedAddresses = findUnused(nodes);
- Optional<Allocation> allocation = unusedAddresses.stream()
- .filter(IP::isV6)
- .findFirst()
- .map(addr -> Allocation.resolveFrom(addr, resolver));
+ var unusedAddresses = findUnused(nodes);
+ var allocation = unusedAddresses.stream()
+ .filter(IP::isV6)
+ .findFirst()
+ .map(addr -> Allocation.resolveFrom(addr, resolver));
allocation.flatMap(Allocation::ipv4Address).ifPresent(ipv4Address -> {
- if (!unusedAddresses.contains(ipv4Address)) {
- throw new IllegalArgumentException("Allocation resolved " + ipv4Address + " from hostname " +
- allocation.get().hostname +
- ", but that address is not available in the address pool of " +
- owner.hostname());
- }
+ if (!unusedAddresses.contains(ipv4Address)) {
+ throw new IllegalArgumentException("Allocation resolved " + ipv4Address + " from hostname " +
+ allocation.get().hostname +
+ ", but that address is not owned by this node");
+ }
});
return allocation;
}
@@ -91,8 +152,9 @@ public class IP {
* @param nodes All nodes in the repository
*/
public Set<String> findUnused(NodeList nodes) {
- Set<String> unusedAddresses = new LinkedHashSet<>(addresses);
- nodes.childrenOf(owner).asList().forEach(node -> unusedAddresses.removeAll(node.ipAddresses()));
+ var unusedAddresses = new LinkedHashSet<>(addresses);
+ nodes.filter(node -> node.ipConfig().primary().stream().anyMatch(addresses::contains))
+ .forEach(node -> unusedAddresses.removeAll(node.ipConfig().primary()));
return Collections.unmodifiableSet(unusedAddresses);
}
@@ -104,7 +166,7 @@ public class IP {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- AddressPool that = (AddressPool) o;
+ Pool that = (Pool) o;
return Objects.equals(addresses, that.addresses);
}
@@ -113,6 +175,28 @@ public class IP {
return Objects.hash(addresses);
}
+ public static Pool of(Set<String> pool) {
+ return new Pool(require(pool));
+ }
+
+ /** Validates and returns the given IP address pool */
+ public static Set<String> require(Set<String> pool) {
+ long ipv6AddrCount = pool.stream().filter(IP::isV6).count();
+ if (ipv6AddrCount == pool.size()) {
+ return pool; // IPv6-only pool is valid
+ }
+
+ long ipv4AddrCount = pool.stream().filter(IP::isV4).count();
+ if (ipv4AddrCount == ipv6AddrCount) {
+ return pool;
+ }
+
+ throw new IllegalArgumentException(String.format("Dual-stacked IP address list must have an " +
+ "equal number of addresses of each version " +
+ "[IPv6 address count = %d, IPv4 address count = %d]",
+ ipv6AddrCount, ipv4AddrCount));
+ }
+
}
/** An IP address allocation from a pool */
@@ -206,32 +290,4 @@ public class IP {
return InetAddresses.forString(ipAddress) instanceof Inet6Address;
}
- /** Validates and returns the given set of IP addresses */
- public static Set<String> requireAddresses(Set<String> addresses) {
- try {
- addresses.forEach(InetAddresses::forString);
- } catch (IllegalArgumentException e) {
- throw new IllegalArgumentException("A node must have at least one valid IP address", e);
- }
- return addresses;
- }
-
- /** Validates and returns the given IP address pool */
- public static Set<String> requireAddressPool(Set<String> addresses) {
- long ipv6AddrCount = addresses.stream().filter(IP::isV6).count();
- if (ipv6AddrCount == addresses.size()) {
- return addresses; // IPv6-only pool is valid
- }
-
- long ipv4AddrCount = addresses.stream().filter(IP::isV4).count();
- if (ipv4AddrCount == ipv6AddrCount) {
- return addresses;
- }
-
- throw new IllegalArgumentException(String.format("Dual-stacked IP address list must have an " +
- "equal number of addresses of each version " +
- "[IPv6 address count = %d, IPv4 address count = %d]",
- ipv6AddrCount, ipv4AddrCount));
- }
-
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java
index 0d86abeb736..371ed4d2496 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabaseClient.java
@@ -202,7 +202,7 @@ public class CuratorDatabaseClient {
CuratorTransaction curatorTransaction = curatorDatabase.newCuratorTransactionIn(transaction);
for (Node node : nodes) {
- Node newNode = new Node(node.id(), node.ipAddresses(), node.ipAddressPool().asSet(), node.hostname(),
+ Node newNode = new Node(node.id(), node.ipConfig(), node.hostname(),
node.parentHostname(), node.flavor(),
newNodeStatus(node, toState),
toState,
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 c6e72fc591e..cbc84f44a48 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
@@ -124,8 +124,8 @@ public class NodeSerializer {
private void toSlime(Node node, Cursor object) {
object.setString(hostnameKey, node.hostname());
- toSlime(node.ipAddresses(), object.setArray(ipAddressesKey), IP::requireAddresses);
- toSlime(node.ipAddressPool().asSet(), object.setArray(ipAddressPoolKey), IP::requireAddressPool);
+ toSlime(node.ipConfig().primary(), object.setArray(ipAddressesKey), IP.Config::require);
+ toSlime(node.ipConfig().pool().asSet(), object.setArray(ipAddressPoolKey), IP.Pool::require);
object.setString(idKey, node.id());
node.parentHostname().ifPresent(hostname -> object.setString(parentHostnameKey, hostname));
toSlime(node.flavor(), object);
@@ -186,7 +186,7 @@ public class NodeSerializer {
private void toSlime(Set<String> ipAddresses, Cursor array, UnaryOperator<Set<String>> validator) {
// Validating IP address format expensive, so we do it at serialization time instead of Node construction time
- validator.apply(ipAddresses).stream().sorted(IP.naturalOrder).forEach(array::addString);
+ validator.apply(ipAddresses).stream().sorted(IP.NATURAL_ORDER).forEach(array::addString);
}
// ---------------- Deserialization --------------------------------------------------
@@ -197,8 +197,8 @@ public class NodeSerializer {
private Node nodeFromSlime(Node.State state, Inspector object) {
return new Node(object.field(idKey).asString(),
- ipAddressesFromSlime(object, ipAddressesKey),
- ipAddressesFromSlime(object, ipAddressPoolKey),
+ new IP.Config(ipAddressesFromSlime(object, ipAddressesKey),
+ ipAddressesFromSlime(object, ipAddressPoolKey)),
object.field(hostnameKey).asString(),
parentHostnameFromSlime(object),
flavorFromSlime(object),
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodePatcher.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodePatcher.java
index f9234af7fb4..2d9d22794cf 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodePatcher.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/NodePatcher.java
@@ -14,6 +14,7 @@ import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.Allocation;
+import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.node.Report;
import com.yahoo.vespa.hosted.provision.node.Reports;
@@ -130,9 +131,9 @@ public class NodePatcher {
case "parentHostname" :
return node.withParentHostname(asString(value));
case "ipAddresses" :
- return node.withIpAddresses(asStringSet(value));
+ return node.with(node.ipConfig().with(asStringSet(value)));
case "additionalIpAddresses" :
- return node.withIpAddressPool(asStringSet(value));
+ return node.with(node.ipConfig().with(IP.Pool.of(asStringSet(value))));
case WANT_TO_RETIRE :
return node.withWantToRetire(asBoolean(value), Agent.operator, nodeRepository.clock().instant());
case WANT_TO_DEPROVISION :
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostProvisionMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostProvisionMaintainerTest.java
index a943637bade..a70bd323b63 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostProvisionMaintainerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/HostProvisionMaintainerTest.java
@@ -19,6 +19,7 @@ import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.Allocation;
import com.yahoo.vespa.hosted.provision.node.Generation;
import com.yahoo.vespa.hosted.provision.node.History;
+import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.node.Reports;
import com.yahoo.vespa.hosted.provision.node.Status;
import com.yahoo.vespa.hosted.provision.provisioning.FatalProvisioningException;
@@ -69,8 +70,8 @@ public class HostProvisionMaintainerTest {
Node host4 = tester.addNode("host4", Optional.empty(), NodeType.host, Node.State.provisioned, Optional.empty());
Node host41 = tester.addNode("host4-1", Optional.of("host4"), NodeType.tenant, Node.State.reserved, Optional.of(tenantApp));
- Node host4new = host4.withIpAddresses(Set.of("::2"));
- Node host41new = host41.withIpAddresses(Set.of("::4", "10.0.0.1"));
+ Node host4new = host4.with(host4.ipConfig().with(Set.of("::2")));
+ Node host41new = host41.with(host4.ipConfig().with(Set.of("::4", "10.0.0.1")));
assertTrue(Stream.of(host4, host41).map(Node::ipAddresses).allMatch(Set::isEmpty));
when(hostProvisioner.provision(eq(host4), eq(Set.of(host41)))).thenReturn(List.of(host4new, host41new));
@@ -146,13 +147,13 @@ public class HostProvisionMaintainerTest {
ClusterMembership.from("container/default/0/0", Version.fromString("7.3")),
Generation.initial(),
false));
- Set<String> ips = state == Node.State.active ? Set.of("::1") : Set.of();
- return new Node("fake-id-" + hostname, ips, Set.of(), hostname,
- parentHostname, flavor, Status.initial(), state, allocation, History.empty(), nodeType, new Reports(), Optional.empty());
+ var ipConfig = new IP.Config(state == Node.State.active ? Set.of("::1") : Set.of(), Set.of());
+ return new Node("fake-id-" + hostname, ipConfig, hostname, parentHostname, flavor, Status.initial(),
+ state, allocation, History.empty(), nodeType, new Reports(), Optional.empty());
}
NodeRepository nodeRepository() {
return nodeRepository;
}
}
-} \ No newline at end of file
+}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTest.java
index 399ff8582bd..407747785eb 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRetirerTest.java
@@ -78,7 +78,7 @@ public class NodeRetirerTest {
public void testRetireAllocated() {
// Update IP addresses on ready nodes so that when they are deployed to, we wont retire them
tester.nodeRepository.getNodes(Node.State.ready)
- .forEach(node -> tester.nodeRepository.write(node.withIpAddresses(Collections.singleton("::2"))));
+ .forEach(node -> tester.nodeRepository.write(node.with(node.ipConfig().with(Set.of("::2")))));
tester.assertCountsForStateByFlavor(Node.State.active, 9, 4, 8, 11, -1);
@@ -136,7 +136,7 @@ public class NodeRetirerTest {
Node nodeToFail = tester.nodeRepository.getNode("host5.test.yahoo.com").orElseThrow(RuntimeException::new);
tester.nodeRepository.fail(nodeToFail.hostname(), Agent.system, "Failed for unit testing");
Node nodeToUpdate = tester.nodeRepository.getNode("host8.test.yahoo.com").orElseThrow(RuntimeException::new);
- tester.nodeRepository.write(nodeToUpdate.withIpAddresses(Collections.singleton("::2")));
+ tester.nodeRepository.write(nodeToUpdate.with(nodeToUpdate.ipConfig().with(Set.of("::2"))));
nodes = tester.nodeRepository.getNodes(app);
Set<String> excluded = Stream.of(nodeWantToRetire, nodeToFail, nodeToUpdate).map(Node::hostname).collect(Collectors.toSet());
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 a7ffa68d821..36a4737b3e1 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
@@ -67,17 +67,17 @@ public class IPTest {
"2001:db8:0:0:0:0:0:ffff",
"2001:db8:85a3:0:0:8a2e:370:7334",
"2001:db8:95a3:0:0:0:0:7334"),
- new ArrayList<>(ImmutableSortedSet.copyOf(IP.naturalOrder, ipAddresses))
+ new ArrayList<>(ImmutableSortedSet.copyOf(IP.NATURAL_ORDER, ipAddresses))
);
}
@Test
public void test_find_allocation_single_stack() {
- IP.AddressPool pool = createNode(ImmutableSet.of(
+ IP.Pool pool = createNode(ImmutableSet.of(
"::1",
"::2",
"::3"
- )).ipAddressPool();
+ )).ipConfig().pool();
resolver.addRecord("host1", "::2");
resolver.addRecord("host2", "::3");
@@ -103,7 +103,7 @@ public class IPTest {
@Test
public void test_find_allocation_dual_stack() {
- IP.AddressPool pool = dualStackPool();
+ IP.Pool pool = dualStackPool();
Optional<IP.Allocation> allocation = pool.findAllocation(emptyList, resolver);
assertEquals("::1", allocation.get().ipv6Address());
assertEquals("127.0.0.2", allocation.get().ipv4Address().get());
@@ -112,7 +112,7 @@ public class IPTest {
@Test
public void test_find_allocation_multiple_ipv4_addresses() {
- IP.AddressPool pool = dualStackPool();
+ IP.Pool pool = dualStackPool();
resolver.addRecord("host3", "127.0.0.127");
try {
pool.findAllocation(emptyList, resolver);
@@ -125,7 +125,7 @@ public class IPTest {
@Test
public void test_find_allocation_invalid_ipv4_reverse_record() {
- IP.AddressPool pool = dualStackPool();
+ IP.Pool pool = dualStackPool();
resolver.removeRecord("127.0.0.2")
.addReverseRecord("127.0.0.2", "host5");
try {
@@ -137,7 +137,7 @@ public class IPTest {
}
}
- private IP.AddressPool dualStackPool() {
+ private IP.Pool dualStackPool() {
Node node = createNode(ImmutableSet.of(
"127.0.0.1",
"127.0.0.2",
@@ -163,7 +163,7 @@ public class IPTest {
.addReverseRecord("::1", "host3")
.addReverseRecord("::2", "host1");
- return node.ipAddressPool();
+ return node.ipConfig().pool();
}
private static Node createNode(Set<String> ipAddresses) {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/SerializationTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/SerializationTest.java
index 188dd5fcaee..b2966ccb91a 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/SerializationTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/SerializationTest.java
@@ -22,6 +22,7 @@ import com.yahoo.vespa.hosted.provision.Node.State;
import com.yahoo.vespa.hosted.provision.node.Agent;
import com.yahoo.vespa.hosted.provision.node.Generation;
import com.yahoo.vespa.hosted.provision.node.History;
+import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.node.Report;
import com.yahoo.vespa.hosted.provision.node.Reports;
import com.yahoo.vespa.hosted.provision.provisioning.FlavorConfigBuilder;
@@ -36,6 +37,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import java.util.stream.Collectors;
import static java.time.temporal.ChronoUnit.MILLIS;
@@ -252,7 +254,7 @@ public class SerializationTest {
Node node = createNode();
// Test round-trip with IP address pool
- node = node.withIpAddressPool(ImmutableSet.of("::1", "::2", "::3"));
+ node = node.with(node.ipConfig().with(IP.Pool.of(Set.of("::1", "::2", "::3"))));
Node copy = nodeSerializer.fromJson(node.state(), nodeSerializer.toJson(node));
assertEquals(node.ipAddressPool(), copy.ipAddressPool());
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java
index 242bb7df146..1275ad0781a 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/AllocationSimulator.java
@@ -13,6 +13,7 @@ import com.yahoo.vespa.hosted.provision.NodeList;
import com.yahoo.vespa.hosted.provision.node.Allocation;
import com.yahoo.vespa.hosted.provision.node.Generation;
import com.yahoo.vespa.hosted.provision.node.History;
+import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.node.Reports;
import com.yahoo.vespa.hosted.provision.node.Status;
@@ -80,10 +81,10 @@ public class AllocationSimulator {
}
private Node node(String hostname, Flavor flavor, Optional<String> parent, Optional<String> tenant) {
- return new Node("fake", Collections.singleton("127.0.0.1"),
- parent.isPresent() ? Collections.emptySet() : getAdditionalIP(), hostname, parent, flavor, Status.initial(),
- parent.isPresent() ? Node.State.ready : Node.State.active, allocation(tenant), History.empty(),
- parent.isPresent() ? NodeType.tenant : NodeType.host, new Reports(), Optional.empty());
+ var ipConfig = new IP.Config(Set.of("127.0.0.1"), parent.isPresent() ? Set.of() : getAdditionalIP());
+ return new Node("fake", ipConfig, hostname, parent, flavor, Status.initial(),
+ parent.isPresent() ? Node.State.ready : Node.State.active, allocation(tenant), History.empty(),
+ parent.isPresent() ? NodeType.tenant : NodeType.host, new Reports(), Optional.empty());
}
private Set<String> getAdditionalIP() {
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java
index 280eab44db3..a5a6f3e678b 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/DynamicDockerProvisionTest.java
@@ -14,6 +14,7 @@ import com.yahoo.vespa.flags.Flags;
import com.yahoo.vespa.flags.InMemoryFlagSource;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.node.Agent;
+import com.yahoo.vespa.hosted.provision.node.IP;
import com.yahoo.vespa.hosted.provision.testutils.MockNameResolver;
import org.junit.Test;
@@ -85,8 +86,8 @@ public class DynamicDockerProvisionTest {
// Ready the provisioned hosts, add an IP addreses to pool and activate them
for (Integer i : expectedProvisionIndexes) {
String hostname = "host-" + i;
- Node host = tester.nodeRepository().getNode(hostname).orElseThrow()
- .withIpAddressPool(Set.of("::" + i + ":2")).withIpAddresses(Set.of("::" + i + ":0"));
+ var ipConfig = new IP.Config(Set.of("::" + i + ":0"), Set.of("::" + i + ":2"));
+ Node host = tester.nodeRepository().getNode(hostname).orElseThrow().with(ipConfig);
tester.nodeRepository().setReady(List.of(host), Agent.system, getClass().getSimpleName());
nameResolver.addRecord(hostname + "-2", "::" + i + ":2");
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java
index 0c403777f3e..80d8ffb7c60 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/RestApiTest.java
@@ -270,7 +270,7 @@ public class RestApiTest {
("[" + asNodeJson("host-with-ip.yahoo.com", "default", "foo") + "]").
getBytes(StandardCharsets.UTF_8),
Request.Method.POST);
- assertResponse(req, 400, "{\"error-code\":\"BAD_REQUEST\",\"message\":\"A node must have at least one valid IP address: 'foo' is not an IP string literal.\"}");
+ assertResponse(req, 400, "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Found one or more invalid addresses in [foo]: 'foo' is not an IP string literal.\"}");
}
@Test