summaryrefslogtreecommitdiffstats
path: root/node-repository
diff options
context:
space:
mode:
authorjonmv <venstad@gmail.com>2023-11-08 17:07:03 +0100
committerjonmv <venstad@gmail.com>2023-11-08 17:07:03 +0100
commit033d5f765fa2d17d9309c89d7e62ce64d0be7d58 (patch)
tree682e258c8faf95c7030148036b4543c437c3886c /node-repository
parent59ecf3b33f29ab82a61bd888b1a4f1dc60d60027 (diff)
Support an ID seed for LB resource ID hashes
Diffstat (limited to 'node-repository')
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerInstance.java14
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerService.java10
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/LoadBalancerServiceMock.java7
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/lb/SharedLoadBalancerService.java6
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializer.java13
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/LoadBalancersResponse.java1
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/persistence/LoadBalancerSerializerTest.java5
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/load-balancers.json47
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