diff options
author | Martin Polden <mpolden@mpolden.no> | 2021-11-04 11:16:59 +0100 |
---|---|---|
committer | Martin Polden <mpolden@mpolden.no> | 2021-11-04 11:16:59 +0100 |
commit | ca5fd6a2a5c5eb4ffd05810be87628c0082bb9d1 (patch) | |
tree | 2a1161647d07d525b486ac976d9bef3227499a75 /controller-server | |
parent | 4d275793ca055a211f6cb1cc0d88f42c798518bb (diff) |
Replace zones with targets in Endpoint
Diffstat (limited to 'controller-server')
11 files changed, 175 insertions, 100 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java index 53dec79bba9..6be62367407 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java @@ -58,7 +58,7 @@ import java.util.stream.Collectors; * The routing controller encapsulates state and methods for inspecting and manipulating deployment endpoints in a * hosted Vespa system. * - * The one stop shop for all your routing needs! + * The one-stop shop for all your routing needs! * * @author mpolden */ @@ -168,8 +168,8 @@ public class RoutingController { } // Add wildcard names for zone endpoints - builders.add(Endpoint.of(deployment.applicationId()).target(ClusterSpec.Id.from("default"), deployment.zoneId())); - builders.add(Endpoint.of(deployment.applicationId()).wildcard(deployment.zoneId())); + builders.add(Endpoint.of(deployment.applicationId()).target(ClusterSpec.Id.from("default"), deployment)); + builders.add(Endpoint.of(deployment.applicationId()).wildcard(deployment)); // Build all endpoints for (var builder : builders) { @@ -256,8 +256,9 @@ public class RoutingController { containerEndpoints.add(new ContainerEndpoint(assignedRotation.clusterId().value(), names)); } // Add endpoints not backed by a rotation + DeploymentId deployment = new DeploymentId(instance.id(), zone); endpoints.not().requiresRotation() - .targets(zone) + .targets(deployment) .groupingBy(Endpoint::cluster) .forEach((clusterId, clusterEndpoints) -> { containerEndpoints.add(new ContainerEndpoint(clusterId.value(), @@ -330,28 +331,28 @@ public class RoutingController { var directMethods = 0; var zones = deployments.stream().map(DeploymentId::zoneId).collect(Collectors.toList()); var availableRoutingMethods = routingMethodsOfAll(deployments, deploymentSpec); - boolean legacyNamesAvailable = legacyNamesAvailable(deploymentSpec, routingId.application().instance()); + boolean legacyNamesAvailable = legacyNamesAvailable(deploymentSpec, routingId.instance().instance()); for (var method : availableRoutingMethods) { if (method.isDirect() && ++directMethods > 1) { throw new IllegalArgumentException("Invalid routing methods for " + routingId + ": Exceeded maximum " + "direct methods"); } - endpoints.add(Endpoint.of(routingId.application()) - .target(routingId.endpointId(), cluster, zones) + endpoints.add(Endpoint.of(routingId.instance()) + .target(routingId.endpointId(), cluster, deployments) .on(Port.fromRoutingMethod(method)) .routingMethod(method) .in(controller.system())); // Add legacy endpoints if (legacyNamesAvailable && method == RoutingMethod.shared) { - endpoints.add(Endpoint.of(routingId.application()) - .target(routingId.endpointId(), cluster, zones) + endpoints.add(Endpoint.of(routingId.instance()) + .target(routingId.endpointId(), cluster, deployments) .on(Port.plain(4080)) .legacy() .routingMethod(method) .in(controller.system())); - endpoints.add(Endpoint.of(routingId.application()) - .target(routingId.endpointId(), cluster, zones) + endpoints.add(Endpoint.of(routingId.instance()) + .target(routingId.endpointId(), cluster, deployments) .on(Port.tls(4443)) .legacy() .routingMethod(method) diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java index 5e458a051b6..3102c247cca 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java @@ -13,6 +13,7 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; import java.net.URI; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.function.Predicate; @@ -36,28 +37,52 @@ public class Endpoint { private final EndpointId id; private final ClusterSpec.Id cluster; private final URI url; - private final List<ZoneId> zones; + private final List<Target> targets; private final Scope scope; private final boolean legacy; private final RoutingMethod routingMethod; private final boolean tls; - private Endpoint(EndpointId id, ClusterSpec.Id cluster, URI url, List<ZoneId> zones, Scope scope, Port port, boolean legacy, RoutingMethod routingMethod) { + private Endpoint(TenantAndApplicationId application, Optional<InstanceName> instanceName, EndpointId id, + ClusterSpec.Id cluster, URI url, List<Target> targets, Scope scope, Port port, boolean legacy, + RoutingMethod routingMethod) { + Objects.requireNonNull(application, "application must be non-null"); + Objects.requireNonNull(instanceName, "instanceName must be non-null"); Objects.requireNonNull(cluster, "cluster must be non-null"); - Objects.requireNonNull(zones, "zones must be non-null"); + Objects.requireNonNull(targets, "deployment must be non-null"); Objects.requireNonNull(scope, "scope must be non-null"); Objects.requireNonNull(port, "port must be non-null"); Objects.requireNonNull(routingMethod, "routingMethod must be non-null"); - if (scope == Scope.global) { - if (id == null) throw new IllegalArgumentException("Endpoint ID must be set for global endpoints"); + if (scope.multiRegion()) { + if (id == null) throw new IllegalArgumentException("Endpoint ID must be set for multi-region endpoints"); } else { if (scope == Scope.zone && id != null) throw new IllegalArgumentException("Endpoint ID cannot be set for " + scope + " endpoints"); - if (zones.size() != 1) throw new IllegalArgumentException("A single zone must be given for " + scope + " endpoints"); + if (targets.size() != 1) throw new IllegalArgumentException("A single target must be given for " + scope + " endpoints"); + } + if (scope != Scope.application && instanceName.isEmpty()) { + throw new IllegalArgumentException("Instance must be set for scope " + scope); + } + for (var target : targets) { + if (scope == Scope.application) { + TenantAndApplicationId owner = TenantAndApplicationId.from(target.deployment().applicationId()); + if (!owner.equals(application)) { + throw new IllegalArgumentException(id + " has target owned by " + owner + + ", which does not match application of this endpoint: " + + application); + } + } else { + ApplicationId owner = target.deployment.applicationId(); + ApplicationId instance = application.instance(instanceName.get()); + if (!owner.equals(instance)) { + throw new IllegalArgumentException(id + " has target owned by " + owner + + ", which does not match instance of this endpoint: " + instance); + } + } } this.id = id; this.cluster = cluster; this.url = url; - this.zones = List.copyOf(zones); + this.targets = List.copyOf(targets); this.scope = scope; this.legacy = legacy; this.routingMethod = routingMethod; @@ -65,20 +90,22 @@ public class Endpoint { } private Endpoint(EndpointId id, ClusterSpec.Id cluster, TenantAndApplicationId application, - Optional<InstanceName> instance, List<ZoneId> zones, Scope scope, SystemName system, Port port, + Optional<InstanceName> instance, List<Target> targets, Scope scope, SystemName system, Port port, boolean legacy, RoutingMethod routingMethod) { - this(id, + this(application, + instance, + id, cluster, createUrl(endpointOrClusterAsString(id, cluster), Objects.requireNonNull(application, "application must be non-null"), Objects.requireNonNull(instance, "instance must be non-null"), - zones, + targets, scope, Objects.requireNonNull(system, "system must be non-null"), Objects.requireNonNull(port, "port must be non-null"), legacy, routingMethod), - zones, scope, port, legacy, routingMethod); + targets, scope, port, legacy, routingMethod); } /** @@ -108,9 +135,14 @@ public class Endpoint { return url.getAuthority().replaceAll(":.*", ""); } - /** Returns the zone(s) to which this routes traffic */ - public List<ZoneId> zones() { - return zones; + /** Returns the target(s) to which this routes traffic */ + public List<Target> targets() { + return targets; + } + + /** Returns the deployments(s) to which this routes traffic */ + public List<DeploymentId> deployments() { + return targets.stream().map(Target::deployment).collect(Collectors.toUnmodifiableList()); } /** Returns the scope of this */ @@ -168,7 +200,7 @@ public class Endpoint { } private static URI createUrl(String name, TenantAndApplicationId application, Optional<InstanceName> instance, - List<ZoneId> zones, Scope scope, SystemName system, Port port, boolean legacy, + List<Target> targets, Scope scope, SystemName system, Port port, boolean legacy, RoutingMethod routingMethod) { String scheme = port.tls ? "https" : "http"; String separator = separator(system, routingMethod, port.tls); @@ -181,7 +213,7 @@ public class Endpoint { separator + sanitize(application.tenant().value()) + "." + - scopePart(scope, zones, system, legacy) + + scopePart(scope, targets, system, legacy) + dnsSuffix(system, legacy) + portPart + "/"); @@ -203,11 +235,11 @@ public class Endpoint { return name + separator; } - private static String scopePart(Scope scope, List<ZoneId> zones, SystemName system, boolean legacy) { + private static String scopePart(Scope scope, List<Target> targets, SystemName system, boolean legacy) { String scopeSymbol = scopeSymbol(scope, system); if (scope.multiRegion()) return scopeSymbol; - ZoneId zone = zones.get(0); + ZoneId zone = targets.get(0).deployment().zoneId(); String region = zone.region().value(); boolean skipEnvironment = zone.environment().isProduction() && (system.isPublic() || !legacy); String environment = skipEnvironment ? "" : "." + zone.environment().value(); @@ -405,7 +437,43 @@ public class Endpoint { if (!systemApplication.hasEndpoint()) throw new IllegalArgumentException(systemApplication + " has no endpoint"); RoutingMethod routingMethod = RoutingMethod.exclusive; Port port = url.getPort() == -1 ? Port.tls() : Port.tls(url.getPort()); // System application endpoints are always TLS - return new Endpoint(null, ClusterSpec.Id.from("admin"), url, List.of(zone), Scope.zone, port, false, routingMethod); + return new Endpoint(TenantAndApplicationId.from(systemApplication.id()), + Optional.of(systemApplication.id().instance()), + null, + ClusterSpec.Id.from("admin"), + url, + List.of(new Target(new DeploymentId(systemApplication.id(), zone))), + Scope.zone, port, false, routingMethod); + } + + /** A target of an endpoint */ + public static class Target { + + private final DeploymentId deployment; + private final int weight; + + private Target(DeploymentId deployment, int weight) { + this.deployment = Objects.requireNonNull(deployment); + this.weight = weight; + if (weight < 0 || weight > 100) { + throw new IllegalArgumentException("Endpoint target weight must be in range [0, 100], got " + weight); + } + } + + private Target(DeploymentId deployment) { + this(deployment, 1); + } + + /** Returns the deployment of this */ + public DeploymentId deployment() { + return deployment; + } + + /** Returns the assigned weight of this */ + public int weight() { + return weight; + } + } public static class EndpointBuilder { @@ -414,7 +482,7 @@ public class Endpoint { private final Optional<InstanceName> instance; private Scope scope; - private List<ZoneId> zones; + private List<Target> targets; private ClusterSpec.Id cluster; private EndpointId endpointId; private Port port; @@ -426,12 +494,12 @@ public class Endpoint { this.instance = Objects.requireNonNull(instance); } - /** Sets the zone target for this */ - public EndpointBuilder target(ClusterSpec.Id cluster, ZoneId zone) { + /** Sets the deployment target for this */ + public EndpointBuilder target(ClusterSpec.Id cluster, DeploymentId deployment) { checkScope(); this.cluster = cluster; this.scope = Scope.zone; - this.zones = List.of(zone); + this.targets = List.of(new Target(deployment)); return this; } @@ -440,12 +508,12 @@ public class Endpoint { return target(endpointId, ClusterSpec.Id.from("default"), List.of()); } - /** Sets the global target with given ID, zones and cluster (as defined in deployments.xml) */ - public EndpointBuilder target(EndpointId endpointId, ClusterSpec.Id cluster, List<ZoneId> zones) { + /** Sets the global target with given ID, deployments and cluster (as defined in deployments.xml) */ + public EndpointBuilder target(EndpointId endpointId, ClusterSpec.Id cluster, List<DeploymentId> deployments) { checkScope(); this.endpointId = endpointId; this.cluster = cluster; - this.zones = zones; + this.targets = deployments.stream().map(Target::new).collect(Collectors.toUnmodifiableList()); this.scope = Scope.global; return this; } @@ -456,13 +524,18 @@ public class Endpoint { } /** Sets the zone wildcard target for this */ - public EndpointBuilder wildcard(ZoneId zone) { - return target(ClusterSpec.Id.from("*"), zone); + public EndpointBuilder wildcard(DeploymentId deployment) { + return target(ClusterSpec.Id.from("*"), deployment); } - /** Sets the application target with given ID, zones and cluster (as defined in deployments.xml) */ - public EndpointBuilder targetApplication(EndpointId endpointId, ClusterSpec.Id cluster, ZoneId zone) { - target(endpointId, cluster, List.of(zone)); + /** Sets the application target with given ID, cluster, deployments and their weights */ + public EndpointBuilder targetApplication(EndpointId endpointId, ClusterSpec.Id cluster, Map<DeploymentId, Integer> deployments) { + checkScope(); + this.endpointId = endpointId; + this.cluster = cluster; + this.targets = deployments.entrySet().stream() + .map(kv -> new Target(kv.getKey(), kv.getValue())) + .collect(Collectors.toUnmodifiableList()); this.scope = Scope.application; return this; } @@ -472,7 +545,7 @@ public class Endpoint { checkScope(); this.cluster = cluster; this.scope = Scope.region; - this.zones = List.of(effectiveZone(zone)); + this.targets = List.of(new Target(new DeploymentId(application.instance(instance.get()), effectiveZone(zone)))); return this; } @@ -502,7 +575,7 @@ public class Endpoint { if (routingMethod.isDirect() && !port.isDefault()) { throw new IllegalArgumentException("Routing method " + routingMethod + " can only use default port"); } - return new Endpoint(endpointId, cluster, application, instance, zones, scope, system, port, legacy, routingMethod); + return new Endpoint(endpointId, cluster, application, instance, targets, scope, system, port, legacy, routingMethod); } private void checkScope() { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointId.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointId.java index 1f70b33fee4..70b011feb88 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointId.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointId.java @@ -25,9 +25,7 @@ public class EndpointId implements Comparable<EndpointId> { @Override public String toString() { - return "EndpointId{" + - "id='" + id + '\'' + - '}'; + return "endpoint id '" + id + "'"; } @Override diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java index 28f6cb8e0d7..1ad315545e3 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java @@ -3,7 +3,7 @@ package com.yahoo.vespa.hosted.controller.application; import com.yahoo.collections.AbstractFilteringList; import com.yahoo.config.provision.ClusterSpec; -import com.yahoo.config.provision.zone.ZoneId; +import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; import java.util.Collection; import java.util.List; @@ -41,14 +41,14 @@ public class EndpointList extends AbstractFilteringList<Endpoint, EndpointList> return matching(endpoint -> endpoint.cluster().equals(cluster)); } - /** Returns the subset of endpoints which target all of the given zones */ - public EndpointList targets(List<ZoneId> zones) { - return matching(endpoint -> endpoint.zones().containsAll(zones)); + /** Returns the subset of endpoints which target all of the given deployments */ + public EndpointList targets(List<DeploymentId> deployments) { + return matching(endpoint -> endpoint.deployments().containsAll(deployments)); } - /** Returns the subset of endpoints which target the given zones */ - public EndpointList targets(ZoneId zone) { - return targets(List.of(zone)); + /** Returns the subset of endpoints which target the given deployments */ + public EndpointList targets(DeploymentId deployment) { + return targets(List.of(deployment)); } /** Returns the subset of endpoints that are considered legacy */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java index b1371989216..87a7d6674b6 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java @@ -1397,7 +1397,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { } // Add global endpoints EndpointList globalEndpoints = controller.routing().endpointsOf(application, deploymentId.applicationId().instance()) - .targets(deploymentId.zoneId()); + .targets(deploymentId); if (!legacyEndpoints) { globalEndpoints = globalEndpoints.not().legacy(); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiHandler.java index f72c911f369..34fcda3bff8 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiHandler.java @@ -101,9 +101,8 @@ public class RoutingApiHandler extends AuditLoggingRequestHandler { .asList(); var deployments = endpoints.stream() - .flatMap(e -> e.zones().stream()) + .flatMap(e -> e.deployments().stream()) .distinct() - .map(zoneId -> new DeploymentId(instanceId, zoneId)) .sorted(Comparator.comparing(DeploymentId::dottedString)) .collect(Collectors.toList()); @@ -123,10 +122,9 @@ public class RoutingApiHandler extends AuditLoggingRequestHandler { var endpointRoot = endpointsRoot.addObject(); endpointToSlime(endpointRoot, endpoint); var zonesRoot = endpointRoot.setArray("zones"); - endpoint.zones().stream().sorted(Comparator.comparing(ZoneId::value)).forEach(zoneId -> { - var deploymentId = new DeploymentId(instanceId, zoneId); - deploymentsStatus.getOrDefault(deploymentId, List.of()).forEach(status -> { - deploymentStatusToSlime(zonesRoot.addObject(), deploymentId, status, endpoint.routingMethod()); + endpoint.deployments().stream().sorted(Comparator.comparing(d -> d.zoneId().value())).forEach(deployment -> { + deploymentsStatus.getOrDefault(deployment, List.of()).forEach(status -> { + deploymentStatusToSlime(zonesRoot.addObject(), deployment, status, endpoint.routingMethod()); }); }); }); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingId.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingId.java index 63829e340d0..343fa5417ce 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingId.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingId.java @@ -7,22 +7,22 @@ import com.yahoo.vespa.hosted.controller.application.EndpointId; import java.util.Objects; /** - * Unique identifier for a global routing table entry (application x endpoint ID). + * Unique identifier for a global routing table entry (instance x endpoint ID). * * @author mpolden */ public class RoutingId { - private final ApplicationId application; + private final ApplicationId instance; private final EndpointId endpointId; - public RoutingId(ApplicationId application, EndpointId endpointId) { - this.application = Objects.requireNonNull(application, "application must be non-null"); + public RoutingId(ApplicationId instance, EndpointId endpointId) { + this.instance = Objects.requireNonNull(instance, "instance must be non-null"); this.endpointId = Objects.requireNonNull(endpointId, "endpointId must be non-null"); } - public ApplicationId application() { - return application; + public ApplicationId instance() { + return instance; } public EndpointId endpointId() { @@ -34,22 +34,22 @@ public class RoutingId { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RoutingId that = (RoutingId) o; - return application.equals(that.application) && + return instance.equals(that.instance) && endpointId.equals(that.endpointId); } @Override public int hashCode() { - return Objects.hash(application, endpointId); + return Objects.hash(instance, endpointId); } @Override public String toString() { - return "routing id for " + endpointId + " of " + application; + return "routing id for " + endpointId + " of " + instance; } - public static RoutingId of(ApplicationId application, EndpointId endpoint) { - return new RoutingId(application, endpoint); + public static RoutingId of(ApplicationId instance, EndpointId endpoint) { + return new RoutingId(instance, endpoint); } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java index 1fda681e0a6..e0c0df5234e 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java @@ -124,7 +124,7 @@ public class RoutingPolicies { Map<RoutingId, List<RoutingPolicy>> routingTable = routingTableFrom(routingPolicies); for (Map.Entry<RoutingId, List<RoutingPolicy>> routeEntry : routingTable.entrySet()) { RoutingId routingId = routeEntry.getKey(); - controller.routing().endpointsOf(routingId.application()) + controller.routing().endpointsOf(routingId.instance()) .named(routingId.endpointId()) .not().requiresRotation() .forEach(endpoint -> updateGlobalDnsOf(endpoint, inactiveZones, routeEntry.getValue())); @@ -250,7 +250,7 @@ public class RoutingPolicies { var activeRoutingIds = routingIdsFrom(allocation); removalCandidates.removeAll(activeRoutingIds); for (var id : removalCandidates) { - var endpoints = controller.routing().endpointsOf(id.application()) + var endpoints = controller.routing().endpointsOf(id.instance()) .not().requiresRotation() .named(id.endpointId()); var forwarder = nameServiceForwarderIn(allocation.deployment.zoneId()); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java index 0faf209ff29..5653b51f6c9 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java @@ -6,6 +6,7 @@ import com.yahoo.config.provision.HostName; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.zone.RoutingMethod; import com.yahoo.text.Text; +import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry; import com.yahoo.vespa.hosted.controller.application.Endpoint; import com.yahoo.vespa.hosted.controller.application.Endpoint.Port; @@ -86,15 +87,16 @@ public class RoutingPolicy { if (infraEndpoint.isPresent()) { return List.of(infraEndpoint.get()); } + DeploymentId deployment = new DeploymentId(id.owner(), id.zone()); List<Endpoint> endpoints = new ArrayList<>(); - endpoints.add(endpoint(routingMethod).target(id.cluster(), id.zone()).in(system)); + endpoints.add(endpoint(routingMethod).target(id.cluster(), deployment).in(system)); // Add legacy endpoints if (routingMethod == RoutingMethod.shared) { - endpoints.add(endpoint(routingMethod).target(id.cluster(), id.zone()) + endpoints.add(endpoint(routingMethod).target(id.cluster(), deployment) .on(Port.plain(4080)) .legacy() .in(system)); - endpoints.add(endpoint(routingMethod).target(id.cluster(), id.zone()) + endpoints.add(endpoint(routingMethod).target(id.cluster(), deployment) .on(Port.tls(4443)) .legacy() .in(system)); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java index e2fb70e5338..f8a19a66cd4 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java @@ -150,8 +150,9 @@ public class EndpointTest { @Test public void zone_endpoints() { var cluster = ClusterSpec.Id.from("default"); // Always default for non-direct routing - var prodZone = ZoneId.from("prod", "us-north-1"); - var testZone = ZoneId.from("test", "us-north-2"); + var prodZone = new DeploymentId(instance1, ZoneId.from("prod", "us-north-1")); + var prodZone2 = new DeploymentId(instance2, ZoneId.from("prod", "us-north-1")); + var testZone = new DeploymentId(instance1, ZoneId.from("test", "us-north-2")); Map<String, Endpoint> tests = Map.of( // Legacy endpoint (always contains environment) @@ -180,7 +181,7 @@ public class EndpointTest { // Non-default instance in main "https://i2--a2--t2.us-north-1.vespa.oath.cloud:4443/", - Endpoint.of(instance2).target(cluster, prodZone).on(Port.tls(4443)).in(SystemName.main), + Endpoint.of(instance2).target(cluster, prodZone2).on(Port.tls(4443)).in(SystemName.main), // Non-default cluster in public "https://c1.a1.t1.us-north-1.z.vespa-app.cloud/", @@ -188,7 +189,7 @@ public class EndpointTest { // Non-default cluster and instance in public "https://c2.i2.a2.t2.us-north-1.z.vespa-app.cloud/", - Endpoint.of(instance2).target(ClusterSpec.Id.from("c2"), prodZone).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public), + Endpoint.of(instance2).target(ClusterSpec.Id.from("c2"), prodZone2).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public), // Endpoint in main using shared layer 4 "https://a1.t1.us-north-1.vespa.oath.cloud/", @@ -199,7 +200,7 @@ public class EndpointTest { Map<String, Endpoint> tests2 = Map.of( // Non-default cluster and instance in public CD (legacy) "https://c2.i2.a2.t2.us-north-1.z.cd.vespa-app.cloud/", - Endpoint.of(instance2).target(ClusterSpec.Id.from("c2"), prodZone).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.PublicCd), + Endpoint.of(instance2).target(ClusterSpec.Id.from("c2"), prodZone2).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.PublicCd), // Custom cluster name in public "https://c1.a1.t1.us-north-1.z.vespa-app.cloud/", @@ -219,8 +220,8 @@ public class EndpointTest { @Test public void wildcard_endpoints() { var defaultCluster = ClusterSpec.Id.from("default"); - var prodZone = ZoneId.from("prod", "us-north-1"); - var testZone = ZoneId.from("test", "us-north-2"); + var prodZone = new DeploymentId(instance1, ZoneId.from("prod", "us-north-1")); + var testZone = new DeploymentId(instance1, ZoneId.from("test", "us-north-2")); var tests = Map.of( // Default rotation @@ -307,7 +308,7 @@ public class EndpointTest { .in(SystemName.main); assertEquals("Availability zone is removed from region", "us-north-1", - endpoint.zones().get(0).region().value()); + endpoint.targets().get(0).deployment().zoneId().region().value()); } @Test @@ -316,28 +317,28 @@ public class EndpointTest { "https://weighted.a1.t1.a.vespa-app.cloud/", Endpoint.of(app1) .targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"), - ZoneId.from("prod", "us-west-1")) + Map.of(new DeploymentId(app1.instance("i1"), ZoneId.from("prod", "us-west-1")), 1)) .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) .in(SystemName.Public), "https://weighted.a1.t1.a.cd.vespa-app.cloud/", Endpoint.of(app1) .targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"), - ZoneId.from("prod", "us-west-1")) + Map.of(new DeploymentId(app1.instance("i1"), ZoneId.from("prod", "us-west-1")), 1)) .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) .in(SystemName.PublicCd), "https://a2.t2.a.vespa.oath.cloud/", Endpoint.of(app2) .targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"), - ZoneId.from("prod", "us-east-3")) + Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1)) .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) .in(SystemName.main), "https://cd.a2.t2.a.vespa.oath.cloud/", Endpoint.of(app2) .targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"), - ZoneId.from("prod", "us-east-3")) + Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1)) .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) .in(SystemName.cd) @@ -347,7 +348,8 @@ public class EndpointTest { @Test public void upstream_name() { - var zone = ZoneId.from("prod", "us-north-1"); + var zone = new DeploymentId(instance1, ZoneId.from("prod", "us-north-1")); + var zone2 = new DeploymentId(instance2, ZoneId.from("prod", "us-north-1")); var tests1 = Map.of( // With default cluster "a1.t1.us-north-1.prod", @@ -359,7 +361,7 @@ public class EndpointTest { // With application endpoint "c2.a1.t1.us-north-1.prod", - Endpoint.of(app1).targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("c2"), zone) + Endpoint.of(app1).targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("c2"), Map.of(new DeploymentId(app1.instance("i1"), zone.zoneId()), 1)) .routingMethod(RoutingMethod.sharedLayer4) .on(Port.tls()) .in(SystemName.main) @@ -367,14 +369,14 @@ public class EndpointTest { var tests2 = Map.of( // With non-default instance and default cluster "i2.a2.t2.us-north-1.prod", - Endpoint.of(instance2).target(EndpointId.defaultId(), ClusterSpec.Id.from("default"), List.of(zone)).on(Port.tls(4443)).in(SystemName.main), + Endpoint.of(instance2).target(EndpointId.defaultId(), ClusterSpec.Id.from("default"), List.of(zone2)).on(Port.tls(4443)).in(SystemName.main), // With non-default instance and cluster "c2.i2.a2.t2.us-north-1.prod", - Endpoint.of(instance2).target(EndpointId.of("ignored2"), ClusterSpec.Id.from("c2"), List.of(zone)).on(Port.tls(4443)).in(SystemName.main) + Endpoint.of(instance2).target(EndpointId.of("ignored2"), ClusterSpec.Id.from("c2"), List.of(zone2)).on(Port.tls(4443)).in(SystemName.main) ); - tests1.forEach((expected, endpoint) -> assertEquals(expected, endpoint.upstreamIdOf(new DeploymentId(instance1, zone)))); - tests2.forEach((expected, endpoint) -> assertEquals(expected, endpoint.upstreamIdOf(new DeploymentId(instance2, zone)))); + tests1.forEach((expected, endpoint) -> assertEquals(expected, endpoint.upstreamIdOf(zone))); + tests2.forEach((expected, endpoint) -> assertEquals(expected, endpoint.upstreamIdOf(zone2))); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java index b303a404ec2..e8bc286ed37 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java @@ -819,11 +819,11 @@ public class RoutingPoliciesTest { .collect(Collectors.toList()); } - private void assertTargets(ApplicationId application, EndpointId endpointId, ClusterSpec.Id cluster, int loadBalancerId, Map<ZoneId, Long> zoneWeights) { + private void assertTargets(ApplicationId instance, EndpointId endpointId, ClusterSpec.Id cluster, int loadBalancerId, Map<ZoneId, Long> zoneWeights) { Set<String> latencyTargets = new HashSet<>(); Map<String, List<ZoneId>> zonesByRegionEndpoint = new HashMap<>(); for (var zone : zoneWeights.keySet()) { - DeploymentId deployment = new DeploymentId(application, zone); + DeploymentId deployment = new DeploymentId(instance, zone); EndpointList regionEndpoints = tester.controller().routing().endpointsOf(deployment) .cluster(cluster) .scope(Endpoint.Scope.region); @@ -834,7 +834,7 @@ public class RoutingPoliciesTest { zonesByRegionEndpoint.forEach((regionEndpoint, zonesInRegion) -> { Set<String> weightedTargets = zonesInRegion.stream() .map(z -> "weighted/lb-" + loadBalancerId + "--" + - application.serializedForm() + "--" + z.value() + + instance.serializedForm() + "--" + z.value() + "/dns-zone-1/" + z.value() + "/" + zoneWeights.get(z)) .collect(Collectors.toSet()); assertEquals("Region endpoint " + regionEndpoint + " points to load balancer", @@ -844,9 +844,10 @@ public class RoutingPoliciesTest { String latencyTarget = "latency/" + regionEndpoint + "/dns-zone-1/" + zone.value(); latencyTargets.add(latencyTarget); }); - String globalEndpoint = tester.controller().routing().endpointsOf(application) + List<DeploymentId> deployments = zoneWeights.keySet().stream().map(z -> new DeploymentId(instance, z)).collect(Collectors.toList()); + String globalEndpoint = tester.controller().routing().endpointsOf(instance) .named(endpointId) - .targets(List.copyOf(zoneWeights.keySet())) + .targets(deployments) .primary() .map(Endpoint::dnsName) .orElse("<none>"); |