Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allocate IP by hostname in dynamically provisioned clouds #32295

Merged
merged 1 commit into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ private List<Node> findCandidates(ApplicationId application, ClusterCapacity clu
ClusterSpec cluster = asSpec(Optional.ofNullable(clusterCapacity.clusterType()), clusterIndex);
NodeSpec nodeSpec = NodeSpec.from(clusterCapacity.count(), 1, nodeResources, false, true,
nodeRepository().zone().cloud().account(), Duration.ZERO);
var allocationContext = IP.Allocation.Context.from(nodeRepository().zone().cloud().name(),
var allocationContext = IP.Allocation.Context.from(nodeRepository().zone().cloud(),
nodeSpec.cloudAccount().isExclave(nodeRepository().zone()),
nodeRepository().nameResolver());
NodePrioritizer prioritizer = new NodePrioritizer(allNodes, application, cluster, nodeSpec,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import ai.vespa.net.InetAddressUtil;
import com.google.common.net.InetAddresses;
import com.yahoo.config.provision.Cloud;
import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.CloudName;
import com.yahoo.config.provision.HostName;
Expand Down Expand Up @@ -312,27 +313,18 @@ public record Allocation(String hostname, Optional<String> ipv4Address, Optional
Objects.requireNonNull(ipv6Address, "ipv6Address must be non-null");
}

public static class Context {
private final CloudName cloudName;
private final boolean exclave;
private final NameResolver resolver;
public record Context(Cloud cloud, boolean exclave, NameResolver resolver) {

private Context(CloudName cloudName, boolean exclave, NameResolver resolver) {
this.cloudName = cloudName;
this.exclave = exclave;
this.resolver = resolver;
}

public static Context from(CloudName cloudName, boolean exclave, NameResolver resolver) {
return new Context(cloudName, exclave, resolver);
public static Context from(Cloud cloud, boolean exclave, NameResolver resolver) {
return new Context(cloud, exclave, resolver);
}

public NameResolver resolver() { return resolver; }

public boolean allocateFromUnusedHostname() { return exclave || cloudName == CloudName.AZURE; }
public boolean allocateFromUnusedHostname() { return exclave || cloud.dynamicProvisioning(); }

public boolean hasIpNotInDns(Version version) {
if (exclave && cloudName == CloudName.GCP && version.is4()) {
if (exclave && cloud.name() == CloudName.GCP && version.is4()) {
// Exclave nodes in GCP have IPv4, because load balancers backends are required to be IPv4,
// but it's private (10.x). The hostname only resolves to the public IPv6 address.
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ static void validateParentHosts(ApplicationId application, NodeList allNodes, No
Set<String> nonActiveApplicationHosts = new HashSet<>(nonActiveHosts);
nonActiveApplicationHosts.removeIf(host -> potentialChildren.childrenOf(host).type(Type.combined, Type.container, Type.content).isEmpty());

if (nonActiveHosts.size() > 0) {
if (!nonActiveHosts.isEmpty()) {
int numActiveApplication = applicationParentHostnames.size() - nonActiveApplicationHosts.size();
int numActiveAdmin = parentHostnames.size() - nonActiveHosts.size() - numActiveApplication;
var messageBuilder = new StringBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ private NodeAllocation prepareAllocation(ApplicationId application, ClusterSpec
Supplier<Integer> nextIndex, LockedNodeList allNodes) {
validateAccount(requested.cloudAccount(), application, allNodes);
NodeAllocation allocation = new NodeAllocation(allNodes, application, cluster, requested, nextIndex, nodeRepository);
IP.Allocation.Context allocationContext = IP.Allocation.Context.from(nodeRepository.zone().cloud().name(),
IP.Allocation.Context allocationContext = IP.Allocation.Context.from(nodeRepository.zone().cloud(),
requested.cloudAccount().isExclave(nodeRepository.zone()),
nodeRepository.nameResolver());
NodePrioritizer prioritizer = new NodePrioritizer(allNodes,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright Vespa.ai. 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.yahoo.config.provision.Cloud;
import com.yahoo.config.provision.CloudName;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeFlavors;
Expand Down Expand Up @@ -30,8 +31,8 @@ public class IPTest {
private static final LockedNodeList emptyList = new LockedNodeList(List.of(), () -> {});
private final MockNameResolver resolver = new MockNameResolver().explicitReverseRecords();

private IP.Allocation.Context contextOf(boolean exclave) {
return IP.Allocation.Context.from(CloudName.AWS, exclave, resolver);
private IP.Allocation.Context contextOf(boolean exclave, boolean dynamicProvisioning) {
return IP.Allocation.Context.from(Cloud.builder().name(CloudName.AWS).dynamicProvisioning(dynamicProvisioning).build(), exclave, resolver);
}

@Test
Expand All @@ -49,7 +50,7 @@ public void test_find_allocation_ipv6_only() {
resolver.addReverseRecord("::1", "host3");
resolver.addReverseRecord("::2", "host1");

var context = contextOf(false);
var context = contextOf(false, false);
Optional<IP.Allocation> allocation = pool.findAllocation(context, emptyList);
assertEquals(Optional.of("::1"), allocation.get().ipv6Address());
assertFalse(allocation.get().ipv4Address().isPresent());
Expand All @@ -68,7 +69,7 @@ public void test_find_allocation_ipv6_only() {
@Test
public void test_find_allocation_ipv4_only() {
var pool = testPool(false);
var allocation = pool.findAllocation(contextOf(false), emptyList);
var allocation = pool.findAllocation(contextOf(false, false), emptyList);
assertFalse("Found allocation", allocation.isEmpty());
assertEquals(Optional.of("127.0.0.1"), allocation.get().ipv4Address());
assertTrue("No IPv6 address", allocation.get().ipv6Address().isEmpty());
Expand All @@ -77,7 +78,7 @@ public void test_find_allocation_ipv4_only() {
@Test
public void test_find_allocation_dual_stack() {
IP.Pool pool = testPool(true);
Optional<IP.Allocation> allocation = pool.findAllocation(contextOf(false), emptyList);
Optional<IP.Allocation> allocation = pool.findAllocation(contextOf(false, false), emptyList);
assertEquals(Optional.of("::1"), allocation.get().ipv6Address());
assertEquals("127.0.0.2", allocation.get().ipv4Address().get());
assertEquals("host3", allocation.get().hostname());
Expand All @@ -88,7 +89,7 @@ public void test_find_allocation_multiple_ipv4_addresses() {
IP.Pool pool = testPool(true);
resolver.addRecord("host3", "127.0.0.127");
try {
pool.findAllocation(contextOf(false), emptyList);
pool.findAllocation(contextOf(false, false), emptyList);
fail("Expected exception");
} catch (IllegalArgumentException e) {
assertEquals("Hostname host3 resolved to more than 1 IPv4 address: [127.0.0.2, 127.0.0.127]",
Expand All @@ -102,7 +103,7 @@ public void test_find_allocation_invalid_ipv4_reverse_record() {
resolver.removeRecord("127.0.0.2")
.addReverseRecord("127.0.0.2", "host5");
try {
pool.findAllocation(contextOf(false), emptyList);
pool.findAllocation(contextOf(false, false), emptyList);
fail("Expected exception");
} catch (IllegalArgumentException e) {
assertEquals("Hostnames resolved from each IP address do not point to the same hostname " +
Expand All @@ -123,7 +124,7 @@ public void test_enclave() {
List.of("2600:1f10:::2", "2600:1f10:::3"),
List.of(HostName.of("node1"), HostName.of("node2")));
IP.Pool pool = config.pool();
Optional<IP.Allocation> allocation = pool.findAllocation(contextOf(true), emptyList);
Optional<IP.Allocation> allocation = pool.findAllocation(contextOf(true, true), emptyList);
}

private IP.Pool testPool(boolean dualStack) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import com.yahoo.config.provision.NodeResources.StorageType;
import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.SharedHosts;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.flags.InMemoryFlagSource;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.HostFilter;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.HostSpec;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.NodeFlavors;
Expand Down Expand Up @@ -447,14 +448,16 @@ public List<Node> makeProvisionedNodes(int n, Function<Integer, String> nodeName
String ipv6 = String.format("::%x", nextIP);

nameResolver.addRecord(hostname, ipv4, ipv6);
var hostIps = new ArrayList<String>();
List<String> hostIps = new ArrayList<>();
hostIps.add(ipv4);
hostIps.add(ipv6);

var ipAddressPool = new ArrayList<String>();
List<String> ipAddressPool = new ArrayList<>();
List<HostName> nodeHostnames = new ArrayList<>();
for (int poolIp = 1; poolIp <= ipAddressPoolSize; poolIp++) {
nextIP++;
String nodeHostname = hostnameParts[0] + "-" + poolIp + (hostnameParts.length > 1 ? "." + hostnameParts[1] : "");
nodeHostnames.add(HostName.of(nodeHostname));
String ipv6Addr = String.format("::%x", nextIP);
ipAddressPool.add(ipv6Addr);
nameResolver.addRecord(nodeHostname, ipv6Addr);
Expand All @@ -464,7 +467,7 @@ public List<Node> makeProvisionedNodes(int n, Function<Integer, String> nodeName
nameResolver.addRecord(nodeHostname, ipv4Addr);
}
}
Node.Builder builder = Node.create(hostname, IP.Config.of(hostIps, ipAddressPool), hostname, flavor, type)
Node.Builder builder = Node.create(hostname, IP.Config.of(hostIps, ipAddressPool, nodeHostnames), hostname, flavor, type)
.cloudAccount(cloudAccount);
reservedTo.ifPresent(builder::reservedTo);
nodes.add(builder.build());
Expand Down