diff options
author | jonmv <venstad@gmail.com> | 2023-11-08 17:07:03 +0100 |
---|---|---|
committer | jonmv <venstad@gmail.com> | 2023-11-08 17:07:03 +0100 |
commit | 033d5f765fa2d17d9309c89d7e62ce64d0be7d58 (patch) | |
tree | 682e258c8faf95c7030148036b4543c437c3886c /node-repository | |
parent | 59ecf3b33f29ab82a61bd888b1a4f1dc60d60027 (diff) |
Support an ID seed for LB resource ID hashes
Diffstat (limited to 'node-repository')
8 files changed, 69 insertions, 34 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerInstance.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerInstance.java index c0931ecbc70..04ea9d20edf 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerInstance.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerInstance.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.UUID; /** * Represents a load balancer instance. This contains the fields that are owned by a {@link LoadBalancerService} and is @@ -20,6 +21,7 @@ import java.util.Set; */ public class LoadBalancerInstance { + private final Optional<UUID> idSeed; private final Optional<DomainName> hostname; private final Optional<String> ip4Address; private final Optional<String> ip6Address; @@ -31,9 +33,10 @@ public class LoadBalancerInstance { private final List<PrivateServiceId> serviceIds; private final CloudAccount cloudAccount; - public LoadBalancerInstance(Optional<DomainName> hostname, Optional<String> ip4Address, Optional<String> ip6Address, + public LoadBalancerInstance(Optional<UUID> idSeed, Optional<DomainName> hostname, Optional<String> ip4Address, Optional<String> ip6Address, Optional<DnsZone> dnsZone, Set<Integer> ports, Set<String> networks, Set<Real> reals, ZoneEndpoint settings, List<PrivateServiceId> serviceIds, CloudAccount cloudAccount) { + this.idSeed = Objects.requireNonNull(idSeed, "idSeed must be non-null"); this.hostname = Objects.requireNonNull(hostname, "hostname must be non-null"); this.ip4Address = Objects.requireNonNull(ip4Address, "ip4Address must be non-null"); this.ip6Address = Objects.requireNonNull(ip6Address, "ip6Address must be non-null"); @@ -51,6 +54,11 @@ public class LoadBalancerInstance { } } + /** A unique seed to use when generating cloud-specific resource IDs for this load balancer instance. */ + public Optional<UUID> idSeed() { + return idSeed; + } + /** Fully-qualified domain name of this load balancer. This hostname can be used for query and feed */ public Optional<DomainName> hostname() { return hostname; @@ -121,7 +129,7 @@ public class LoadBalancerInstance { public LoadBalancerInstance with(Set<Real> reals, ZoneEndpoint settings, Optional<PrivateServiceId> serviceId) { List<PrivateServiceId> ids = new ArrayList<>(serviceIds); serviceId.filter(id -> ! ids.contains(id)).ifPresent(ids::add); - return new LoadBalancerInstance(hostname, ip4Address, ip6Address, dnsZone, ports, networks, + return new LoadBalancerInstance(idSeed, hostname, ip4Address, ip6Address, dnsZone, ports, networks, reals, settings, ids, cloudAccount); } @@ -130,7 +138,7 @@ public class LoadBalancerInstance { public LoadBalancerInstance withServiceIds(List<PrivateServiceId> serviceIds) { List<PrivateServiceId> ids = new ArrayList<>(serviceIds); for (PrivateServiceId id : this.serviceIds) if ( ! ids.contains(id)) ids.add(id); - return new LoadBalancerInstance(hostname, ip4Address, ip6Address, dnsZone, ports, networks, reals, settings, ids, cloudAccount); + return new LoadBalancerInstance(idSeed, hostname, ip4Address, ip6Address, dnsZone, ports, networks, reals, settings, ids, cloudAccount); } } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerService.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerService.java index 6d41f664fad..40323a4f2d3 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerService.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerService.java @@ -2,15 +2,20 @@ package com.yahoo.vespa.hosted.provision.lb; import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.EndpointsChecker.Availability; +import com.yahoo.config.provision.EndpointsChecker.Endpoint; import com.yahoo.config.provision.EndpointsChecker.HealthChecker; import com.yahoo.config.provision.NodeType; +import java.util.Optional; +import java.util.UUID; + /** * A managed load balance service. * * @author mpolden */ -public interface LoadBalancerService extends HealthChecker { +public interface LoadBalancerService { /** * Provisions load balancers from the given specification. Implementations are expected to be idempotent @@ -40,6 +45,9 @@ public interface LoadBalancerService extends HealthChecker { /** Returns whether load balancers created by this service can forward traffic to given node and cluster type */ boolean supports(NodeType nodeType, ClusterSpec.Type clusterType); + /** See {@link HealthChecker#healthy(Endpoint)}. */ + Availability healthy(Endpoint endpoint, Optional<UUID> idSeed); + /** Load balancer protocols */ enum Protocol { ipv4, diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerServiceMock.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerServiceMock.java index df4b83d1543..3b444312b14 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerServiceMock.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerServiceMock.java @@ -14,6 +14,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; /** * @author mpolden @@ -23,6 +25,7 @@ public class LoadBalancerServiceMock implements LoadBalancerService { private final Map<LoadBalancerId, LoadBalancerInstance> instances = new HashMap<>(); private boolean throwOnCreate = false; private boolean supportsProvisioning = true; + private final AtomicBoolean uuid = new AtomicBoolean(true); public Map<LoadBalancerId, LoadBalancerInstance> instances() { return Collections.unmodifiableMap(instances); @@ -53,8 +56,10 @@ public class LoadBalancerServiceMock implements LoadBalancerService { @Override public LoadBalancerInstance provision(LoadBalancerSpec spec) { if (throwOnCreate) throw new IllegalStateException("Did not expect a new load balancer to be created"); + Optional<UUID> idSeed = uuid.getAndSet(false) ? Optional.of(UUID.fromString("c11272ab-d20e-4c86-b808-ffedaa00c480")) : Optional.empty(); var id = new LoadBalancerId(spec.application(), spec.cluster()); var instance = new LoadBalancerInstance( + idSeed, Optional.of(DomainName.of("lb-" + spec.application().toShortString() + "-" + spec.cluster().value())), Optional.empty(), Optional.empty(), @@ -89,7 +94,7 @@ public class LoadBalancerServiceMock implements LoadBalancerService { } @Override - public Availability healthy(Endpoint endpoint) { + public Availability healthy(Endpoint endpoint, Optional<UUID> idSeed) { return Availability.ready; } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/SharedLoadBalancerService.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/SharedLoadBalancerService.java index e463f5aabe6..eb99e5647df 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/SharedLoadBalancerService.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/SharedLoadBalancerService.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.UUID; /** * This implementation of {@link LoadBalancerService} returns the load balancer(s) that exist by default in the shared @@ -42,7 +43,8 @@ public class SharedLoadBalancerService implements LoadBalancerService { private LoadBalancerInstance create(LoadBalancerSpec spec) { if ( ! spec.settings().isPublicEndpoint()) throw new IllegalArgumentException("non-public endpoints is not supported with " + getClass()); - return new LoadBalancerInstance(Optional.of(DomainName.of(vipHostname)), + return new LoadBalancerInstance(Optional.empty(), + Optional.of(DomainName.of(vipHostname)), Optional.empty(), Optional.empty(), Optional.empty(), @@ -72,7 +74,7 @@ public class SharedLoadBalancerService implements LoadBalancerService { } @Override - public Availability healthy(Endpoint endpoint) { + public Availability healthy(Endpoint endpoint, Optional<UUID> idSeed) { return Availability.ready; } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializer.java index 7262daf758b..9e81cd3d3b7 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializer.java @@ -26,7 +26,9 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.UUID; import java.util.function.Function; +import java.util.function.Predicate; /** * Serializer for load balancers. @@ -43,6 +45,7 @@ public class LoadBalancerSerializer { // - CHANGING THE FORMAT OF A FIELD: Don't do it bro. private static final String idField = "id"; + private static final String idSeedField = "idSeed"; private static final String hostnameField = "hostname"; private static final String lbIpAddressField = "ipAddress"; private static final String lbIp6AddressField = "ip6Address"; @@ -69,6 +72,7 @@ public class LoadBalancerSerializer { Cursor root = slime.setObject(); root.setString(idField, loadBalancer.id().serializedForm()); + loadBalancer.instance().flatMap(LoadBalancerInstance::idSeed).ifPresent(idSeed -> root.setString(idSeedField, idSeed.toString())); loadBalancer.instance().flatMap(LoadBalancerInstance::hostname).ifPresent(hostname -> root.setString(hostnameField, hostname.value())); loadBalancer.instance().flatMap(LoadBalancerInstance::ip4Address).ifPresent(ip -> root.setString(lbIpAddressField, ip)); loadBalancer.instance().flatMap(LoadBalancerInstance::ip6Address).ifPresent(ip -> root.setString(lbIp6AddressField, ip)); @@ -124,9 +128,10 @@ public class LoadBalancerSerializer { Set<String> networks = new LinkedHashSet<>(); object.field(networksField).traverse((ArrayTraverser) (i, network) -> networks.add(network.asString())); - Optional<DomainName> hostname = optionalString(object.field(hostnameField), Function.identity()).filter(s -> !s.isEmpty()).map(DomainName::of); - Optional<String> ip4Address = optionalString(object.field(lbIpAddressField), Function.identity()).filter(s -> !s.isEmpty()); - Optional<String> ip6Address = optionalString(object.field(lbIp6AddressField), Function.identity()).filter(s -> !s.isEmpty()); + Optional<UUID> idSeed = SlimeUtils.optionalString(object.field(idSeedField)).map(UUID::fromString); + Optional<DomainName> hostname = SlimeUtils.optionalString(object.field(hostnameField)).map(DomainName::of); + Optional<String> ip4Address = SlimeUtils.optionalString(object.field(lbIpAddressField)); + Optional<String> ip6Address = SlimeUtils.optionalString(object.field(lbIp6AddressField)); Optional<DnsZone> dnsZone = optionalString(object.field(dnsZoneField), DnsZone::new); ZoneEndpoint settings = zoneEndpoint(object.field(settingsField)); Optional<PrivateServiceId> serviceId = optionalString(object.field(serviceIdField), PrivateServiceId::of); @@ -136,7 +141,7 @@ public class LoadBalancerSerializer { CloudAccount cloudAccount = optionalString(object.field(cloudAccountField), CloudAccount::from).orElse(CloudAccount.empty); Optional<LoadBalancerInstance> instance = hostname.isEmpty() && ip4Address.isEmpty() && ip6Address.isEmpty() ? Optional.empty() - : Optional.of(new LoadBalancerInstance(hostname, ip4Address, ip6Address, dnsZone, ports, networks, reals, settings, serviceIds, cloudAccount)); + : Optional.of(new LoadBalancerInstance(idSeed, hostname, ip4Address, ip6Address, dnsZone, ports, networks, reals, settings, serviceIds, cloudAccount)); return new LoadBalancer(LoadBalancerId.fromSerializedForm(object.field(idField).asString()), instance, diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersResponse.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersResponse.java index 175ea8d294e..e40e1ba5951 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersResponse.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersResponse.java @@ -90,6 +90,7 @@ public class LoadBalancersResponse extends SlimeJsonResponse { } instance.serviceId().ifPresent(serviceId -> lbObject.setString("serviceId", serviceId.value())); lbObject.setBool("public", instance.settings().isPublicEndpoint()); + instance.idSeed().ifPresent(idSeed -> lbObject.setString("idSeed", idSeed.toString())); }); lb.instance() .map(LoadBalancerInstance::cloudAccount) diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializerTest.java index 17bb7502484..146b9df1722 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializerTest.java @@ -20,6 +20,7 @@ import java.time.Instant; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.UUID; import static java.time.temporal.ChronoUnit.MILLIS; import static org.junit.Assert.assertEquals; @@ -38,6 +39,7 @@ public class LoadBalancerSerializerTest { { var loadBalancer = new LoadBalancer(loadBalancerId, Optional.of(new LoadBalancerInstance( + Optional.of(UUID.randomUUID()), Optional.of(DomainName.of("lb-host")), Optional.empty(), Optional.empty(), @@ -58,6 +60,7 @@ public class LoadBalancerSerializerTest { var serialized = LoadBalancerSerializer.fromJson(LoadBalancerSerializer.toJson(loadBalancer)); assertEquals(loadBalancer.id(), serialized.id()); + assertEquals(loadBalancer.instance().get().idSeed(), serialized.instance().get().idSeed()); assertEquals(loadBalancer.instance().get().hostname(), serialized.instance().get().hostname()); assertEquals(loadBalancer.instance().get().dnsZone(), serialized.instance().get().dnsZone()); assertEquals(loadBalancer.instance().get().ports(), serialized.instance().get().ports()); @@ -73,6 +76,7 @@ public class LoadBalancerSerializerTest { var loadBalancer = new LoadBalancer(loadBalancerId, Optional.of(new LoadBalancerInstance( Optional.empty(), + Optional.empty(), Optional.of("1.2.3.4"), Optional.of("fd00::1"), Optional.of(new DnsZone("zone-id-1")), @@ -87,6 +91,7 @@ public class LoadBalancerSerializerTest { var serialized = LoadBalancerSerializer.fromJson(LoadBalancerSerializer.toJson(loadBalancer)); assertEquals(loadBalancer.id(), serialized.id()); + assertEquals(loadBalancer.instance().get().idSeed(), serialized.instance().get().idSeed()); assertEquals(loadBalancer.instance().get().hostname(), serialized.instance().get().hostname()); assertEquals(loadBalancer.instance().get().ip4Address(), serialized.instance().get().ip4Address()); assertEquals(loadBalancer.instance().get().ip6Address(), serialized.instance().get().ip6Address()); diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/load-balancers.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/load-balancers.json index 96213dea0a7..e0c370fc376 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/load-balancers.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/load-balancers.json @@ -1,15 +1,13 @@ { "loadBalancers": [ { - "id": "tenant1:application1:instance1:id1", - "state": "active", - "changedAt": 123, "application": "application1", - "tenant": "tenant1", - "instance": "instance1", + "changedAt": 123, "cluster": "id1", - "hostname": "lb-tenant1.application1.instance1-id1", "dnsZone": "zone-id-1", + "hostname": "lb-tenant1.application1.instance1-id1", + "id": "tenant1:application1:instance1:id1", + "instance": "instance1", "networks": [ "10.2.3.0/24", "10.4.5.0/24" @@ -17,6 +15,7 @@ "ports": [ 4443 ], + "public": false, "reals": [ { "hostname": "host1.yahoo.com", @@ -29,6 +28,7 @@ "port": 4443 } ], + "serviceId": "service", "settings": { "allowedUrns": [ { @@ -37,19 +37,18 @@ } ] }, - "serviceId": "service", - "public": false + "state": "active", + "tenant": "tenant1" }, { - "id": "hosted-vespa:zone-config-servers:default:zone-config-servers", - "state": "active", - "changedAt": 123, "application": "zone-config-servers", - "tenant": "hosted-vespa", - "instance": "default", + "changedAt": 123, "cluster": "zone-config-servers", - "hostname": "lb-hosted-vespa.zone-config-servers-zone-config-servers", "dnsZone": "zone-id-1", + "hostname": "lb-hosted-vespa.zone-config-servers-zone-config-servers", + "id": "hosted-vespa:zone-config-servers:default:zone-config-servers", + "idSeed": "c11272ab-d20e-4c86-b808-ffedaa00c480", + "instance": "default", "networks": [ "10.2.3.0/24", "10.4.5.0/24" @@ -57,6 +56,7 @@ "ports": [ 4443 ], + "public": true, "reals": [ { "hostname": "cfg1.yahoo.com", @@ -69,18 +69,17 @@ "port": 4443 } ], - "public": true + "state": "active", + "tenant": "hosted-vespa" }, { - "id": "tenant4:application4:instance4:id4", - "state": "active", - "changedAt": 123, "application": "application4", - "tenant": "tenant4", - "instance": "instance4", + "changedAt": 123, "cluster": "id4", - "hostname": "lb-tenant4.application4.instance4-id4", "dnsZone": "zone-id-1", + "hostname": "lb-tenant4.application4.instance4-id4", + "id": "tenant4:application4:instance4:id4", + "instance": "instance4", "networks": [ "10.2.3.0/24", "10.4.5.0/24" @@ -88,6 +87,7 @@ "ports": [ 4443 ], + "public": true, "reals": [ { "hostname": "host13.yahoo.com", @@ -100,7 +100,8 @@ "port": 4443 } ], - "public": true + "state": "active", + "tenant": "tenant4" } ] -} +}
\ No newline at end of file |