diff options
author | Morten Tokle <mortent@verizonmedia.com> | 2021-11-05 10:38:14 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-11-05 10:38:14 +0100 |
commit | 0c959c5ebb2687b63b85c10561be3778d911258b (patch) | |
tree | 8d4f9ac6949713948f067ec7dda0b5ff5cc3007c /controller-server/src/main | |
parent | 9c4689e1094bba36a28299f04fbbed1fea5b88ee (diff) | |
parent | b47bd195bd82ffedea5d628f076884e1d84b7abd (diff) |
Merge pull request #19883 from vespa-engine/mpolden/application-endpoint-format
Finalize application endpoint names
Diffstat (limited to 'controller-server/src/main')
10 files changed, 238 insertions, 173 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 605f9f63724..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 @@ -6,8 +6,8 @@ import com.google.common.base.Suppliers; import com.google.common.hash.HashCode; import com.google.common.hash.Hashing; import com.google.common.io.BaseEncoding; -import com.yahoo.component.Version; import com.yahoo.config.application.api.DeploymentInstanceSpec; +import com.yahoo.config.application.api.DeploymentSpec; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; @@ -21,7 +21,6 @@ import com.yahoo.vespa.flags.Flags; import com.yahoo.vespa.hosted.controller.api.application.v4.model.EndpointStatus; import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; import com.yahoo.vespa.hosted.controller.api.integration.configserver.ContainerEndpoint; -import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.dns.Record; import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordData; import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName; @@ -59,15 +58,12 @@ 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 */ public class RoutingController { - /** The minimum Vespa version that supports directly routed endpoints */ - public static final Version DIRECT_ROUTING_MIN_VERSION = new Version(7, 196, 4); - private final Controller controller; private final RoutingPolicies routingPolicies; private final RotationRepository rotationRepository; @@ -95,14 +91,14 @@ public class RoutingController { Set<Endpoint> endpoints = new LinkedHashSet<>(); boolean isSystemApplication = SystemApplication.matching(deployment.applicationId()).isPresent(); // Avoid reading application more than once per call to this - Supplier<Application> application = Suppliers.memoize(() -> controller.applications().requireApplication(TenantAndApplicationId.from(deployment.applicationId()))); + Supplier<DeploymentSpec> deploymentSpec = Suppliers.memoize(() -> controller.applications().requireApplication(TenantAndApplicationId.from(deployment.applicationId())).deploymentSpec()); // To discover the cluster name for a zone-scoped endpoint, we need to read routing policies for (var policy : routingPolicies.get(deployment).values()) { if (!policy.status().isActive()) continue; for (var routingMethod : controller.zoneRegistry().routingMethods(policy.id().zone())) { - if (routingMethod.isDirect() && !isSystemApplication && !canRouteDirectlyTo(deployment, application.get())) continue; - endpoints.addAll(policy.endpointsIn(controller.system(), routingMethod, controller.zoneRegistry())); - endpoints.addAll(policy.regionEndpointsIn(controller.system(), routingMethod)); + if (routingMethod.isDirect() && !isSystemApplication && !canRouteDirectlyTo(deployment, deploymentSpec.get())) continue; + endpoints.addAll(policy.zoneEndpointsIn(controller.system(), routingMethod, controller.zoneRegistry())); + endpoints.add(policy.regionEndpointIn(controller.system(), routingMethod)); } } return EndpointList.copyOf(endpoints); @@ -119,7 +115,8 @@ public class RoutingController { public EndpointList endpointsOf(Application application, InstanceName instanceName) { Set<Endpoint> endpoints = new LinkedHashSet<>(); Instance instance = application.require(instanceName); - Optional<DeploymentInstanceSpec> spec = application.deploymentSpec().instance(instanceName); + DeploymentSpec deploymentSpec = application.deploymentSpec(); + Optional<DeploymentInstanceSpec> spec = deploymentSpec.instance(instanceName); if (spec.isEmpty()) return EndpointList.EMPTY; // Add endpoint declared with legacy syntax spec.get().globalServiceId().ifPresent(clusterId -> { @@ -128,7 +125,7 @@ public class RoutingController { .map(zone -> new DeploymentId(instance.id(), ZoneId.from(Environment.prod, zone.region().get()))) .collect(Collectors.toList()); RoutingId routingId = RoutingId.of(instance.id(), EndpointId.defaultId()); - endpoints.addAll(computeGlobalEndpoints(routingId, ClusterSpec.Id.from(clusterId), application, deployments)); + endpoints.addAll(computeGlobalEndpoints(routingId, ClusterSpec.Id.from(clusterId), deployments, deploymentSpec)); }); // Add endpoints declared with current syntax spec.get().endpoints().forEach(declaredEndpoint -> { @@ -137,7 +134,7 @@ public class RoutingController { .map(region -> new DeploymentId(instance.id(), ZoneId.from(Environment.prod, region))) .collect(Collectors.toList()); - endpoints.addAll(computeGlobalEndpoints(routingId, ClusterSpec.Id.from(declaredEndpoint.containerId()), application, deployments)); + endpoints.addAll(computeGlobalEndpoints(routingId, ClusterSpec.Id.from(declaredEndpoint.containerId()), deployments, deploymentSpec)); }); return EndpointList.copyOf(endpoints); } @@ -171,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) { @@ -224,7 +221,7 @@ public class RoutingController { /** Returns the global endpoints for given deployment as container endpoints */ public Set<ContainerEndpoint> containerEndpointsOf(Application application, InstanceName instanceName, ZoneId zone) { Instance instance = application.require(instanceName); - boolean registerLegacyNames = legacyNamesAvailable(application, instanceName); + boolean registerLegacyNames = legacyNamesAvailable(application.deploymentSpec(), instanceName); Set<ContainerEndpoint> containerEndpoints = new HashSet<>(); EndpointList endpoints = endpointsOf(application, instanceName); // Add endpoints backed by a rotation, and register them in DNS if necessary @@ -259,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(), @@ -280,7 +278,7 @@ public class RoutingController { .map(region -> new DeploymentId(instance.id(), ZoneId.from(Environment.prod, region))) .collect(Collectors.toList()); endpointsToRemove.addAll(computeGlobalEndpoints(RoutingId.of(instance.id(), rotation.endpointId()), - rotation.clusterId(), application, deployments)); + rotation.clusterId(), deployments, application.deploymentSpec())); } endpointsToRemove.forEach(endpoint -> controller.nameServiceForwarder() .removeRecords(Record.Type.CNAME, @@ -289,7 +287,7 @@ public class RoutingController { } /** Returns the routing methods that are available across all given deployments */ - private List<RoutingMethod> routingMethodsOfAll(List<DeploymentId> deployments, Application application) { + private List<RoutingMethod> routingMethodsOfAll(List<DeploymentId> deployments, DeploymentSpec deploymentSpec) { var deploymentsByMethod = new HashMap<RoutingMethod, Set<DeploymentId>>(); for (var deployment : deployments) { for (var method : controller.zoneRegistry().routingMethods(deployment.zoneId())) { @@ -300,7 +298,7 @@ public class RoutingController { var routingMethods = new ArrayList<RoutingMethod>(); deploymentsByMethod.forEach((method, supportedDeployments) -> { if (supportedDeployments.containsAll(deployments)) { - if (method.isDirect() && !canRouteDirectlyTo(deployments, application)) return; + if (method.isDirect() && !canRouteDirectlyTo(deployments, deploymentSpec)) return; routingMethods.add(method); } }); @@ -308,58 +306,53 @@ public class RoutingController { } /** Returns whether traffic can be directly routed to all given deployments */ - private boolean canRouteDirectlyTo(List<DeploymentId> deployments, Application application) { - return deployments.stream().allMatch(deployment -> canRouteDirectlyTo(deployment, application)); + private boolean canRouteDirectlyTo(List<DeploymentId> deployments, DeploymentSpec deploymentSpec) { + return deployments.stream().allMatch(deployment -> canRouteDirectlyTo(deployment, deploymentSpec)); } /** Returns whether traffic can be directly routed to given deployment */ - private boolean canRouteDirectlyTo(DeploymentId deploymentId, Application application) { + private boolean canRouteDirectlyTo(DeploymentId deploymentId, DeploymentSpec deploymentSpec) { if (controller.system().isPublic()) return true; // Public always supports direct routing if (controller.system().isCd()) return true; // CD deploys directly so we cannot enforce all requirements below if (deploymentId.zoneId().environment().isManuallyDeployed()) return true; // Manually deployed zones always support direct routing // Check Athenz service presence. The test framework uses this identity when sending requests to the // deployment's container(s). - var athenzService = application.deploymentSpec().instance(deploymentId.applicationId().instance()) - .flatMap(instance -> instance.athenzService(deploymentId.zoneId().environment(), - deploymentId.zoneId().region())); + var athenzService = deploymentSpec.instance(deploymentId.applicationId().instance()) + .flatMap(instance -> instance.athenzService(deploymentId.zoneId().environment(), + deploymentId.zoneId().region())); if (athenzService.isEmpty()) return false; - - // Check minimum required compile-version - var compileVersion = application.latestVersion().flatMap(ApplicationVersion::compileVersion); - if (compileVersion.isEmpty()) return false; - if (compileVersion.get().isBefore(DIRECT_ROUTING_MIN_VERSION)) return false; return true; } /** Compute global endpoints for given routing ID, application and deployments */ - private List<Endpoint> computeGlobalEndpoints(RoutingId routingId, ClusterSpec.Id cluster, Application application, List<DeploymentId> deployments) { + private List<Endpoint> computeGlobalEndpoints(RoutingId routingId, ClusterSpec.Id cluster, List<DeploymentId> deployments, DeploymentSpec deploymentSpec) { var endpoints = new ArrayList<Endpoint>(); var directMethods = 0; var zones = deployments.stream().map(DeploymentId::zoneId).collect(Collectors.toList()); - var availableRoutingMethods = routingMethodsOfAll(deployments, application); - boolean legacyNamesAvailable = legacyNamesAvailable(application, routingId.application().instance()); + var availableRoutingMethods = routingMethodsOfAll(deployments, deploymentSpec); + 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) @@ -370,10 +363,10 @@ public class RoutingController { } /** Whether legacy global DNS names should be available for given application */ - private static boolean legacyNamesAvailable(Application application, InstanceName instanceName) { - return application.deploymentSpec().instance(instanceName) - .flatMap(DeploymentInstanceSpec::globalServiceId) - .isPresent(); + private static boolean legacyNamesAvailable(DeploymentSpec deploymentSpec, InstanceName instanceName) { + return deploymentSpec.instance(instanceName) + .flatMap(DeploymentInstanceSpec::globalServiceId) + .isPresent(); } /** Create a common name based on a hash of given application. This must be less than 64 characters long. */ 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..3698c794e8f 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.multiDeployment()) { + if (id == null) throw new IllegalArgumentException("Endpoint ID must be set for multi-deployment 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.region && instanceName.isEmpty()) { + throw new IllegalArgumentException("Instance must be set for scope " + scope); + } + for (var target : targets) { + if (scope == Scope.region) { + 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 */ @@ -140,7 +172,6 @@ public class Endpoint { /** Returns the upstream ID of given deployment. This *must* match what the routing layer generates */ public String upstreamIdOf(DeploymentId deployment) { - if (!scope.multiRegion()) throw new IllegalArgumentException("Scope " + scope + " does not have upstream name"); if (!routingMethod.isShared()) throw new IllegalArgumentException("Routing method " + routingMethod + " does not have upstream name"); return upstreamIdOf(cluster.value(), deployment.applicationId(), deployment.zoneId()); } @@ -168,7 +199,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 +212,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 +234,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; + if (scope == Scope.global) 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(); @@ -221,16 +252,16 @@ public class Endpoint { if (system.isPublic()) { switch (scope) { case zone: return "z"; - case region: return "w"; + case weighted: return "w"; case global: return "g"; - case application: return "a"; + case region: return "r"; } } switch (scope) { case zone: return ""; - case region: return "w"; + case weighted: return "w"; case global: return "global"; - case application: return "a"; + case region: return "r"; } throw new IllegalArgumentException("No scope symbol defined for " + scope + " in " + system); } @@ -318,31 +349,28 @@ public class Endpoint { public enum Scope { /** - * Endpoint points to a multiple instances of an application. + * Endpoint points to a multiple instances of an application, in the same region. * * Traffic is routed across instances according to weights specified in deployment.xml */ - application, + region, /** Endpoint points to one or more zones. Traffic is routed to the zone closest to the client */ global, /** - * Endpoint points to one more zones in the same geographical region. Traffic is routed equally across zones. + * Endpoint points to one more zones in the same geographical region. Traffic is routed evenly across zones. * * This is for internal use only. Endpoints with this scope are not exposed directly to tenants. */ - region, + weighted, /** Endpoint points to a single zone */ zone; - /** Returns whether this scope may span multiple regions */ - public boolean multiRegion() { - // application scope doesn't technically support multiple regions in practice, but we assume it does for the - // purposes of building an endpoint name. This allows us to support multiple regions in the future without - // needing to change endpoint names. - return this == application || this == global; + /** Returns whether this scope may span multiple deployments */ + public boolean multiDeployment() { + return this == region || this == global; } } @@ -405,7 +433,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 +478,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 +490,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 +504,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,14 +520,19 @@ 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)); - this.scope = Scope.application; + /** 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.region; return this; } @@ -471,8 +540,8 @@ public class Endpoint { public EndpointBuilder targetRegion(ClusterSpec.Id cluster, ZoneId zone) { checkScope(); this.cluster = cluster; - this.scope = Scope.region; - this.zones = List.of(effectiveZone(zone)); + this.scope = Scope.weighted; + this.targets = List.of(new Target(new DeploymentId(application.instance(instance.get()), effectiveZone(zone)))); return this; } @@ -502,7 +571,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/persistence/RoutingPolicySerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializer.java index 518a6ddea37..d707b769f29 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializer.java @@ -21,6 +21,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; +import java.util.Set; /** * Serializer and deserializer for a {@link RoutingPolicy}. @@ -42,7 +43,8 @@ public class RoutingPolicySerializer { private static final String canonicalNameField = "canonicalName"; private static final String zoneField = "zone"; private static final String dnsZoneField = "dnsZone"; - private static final String rotationsField = "rotations"; + private static final String instanceEndpointsField = "rotations"; + private static final String applicationEndpointsField = "applicationEndpoints"; private static final String loadBalancerActiveField = "active"; private static final String globalRoutingField = "globalRouting"; private static final String agentField = "agent"; @@ -59,10 +61,10 @@ public class RoutingPolicySerializer { policyObject.setString(zoneField, policy.id().zone().value()); policyObject.setString(canonicalNameField, policy.canonicalName().value()); policy.dnsZone().ifPresent(dnsZone -> policyObject.setString(dnsZoneField, dnsZone)); - var rotationArray = policyObject.setArray(rotationsField); - policy.endpoints().forEach(endpointId -> { - rotationArray.addString(endpointId.id()); - }); + var instanceEndpointsArray = policyObject.setArray(instanceEndpointsField); + policy.instanceEndpoints().forEach(endpointId -> instanceEndpointsArray.addString(endpointId.id())); + var applicationEndpointsArray = policyObject.setArray(applicationEndpointsField); + policy.applicationEndpoints().forEach(endpointId -> applicationEndpointsArray.addString(endpointId.id())); policyObject.setBool(loadBalancerActiveField, policy.status().isActive()); globalRoutingToSlime(policy.status().globalRouting(), policyObject.setObject(globalRoutingField)); }); @@ -74,15 +76,18 @@ public class RoutingPolicySerializer { var root = slime.get(); var field = root.field(routingPoliciesField); field.traverse((ArrayTraverser) (i, inspect) -> { - var endpointIds = new LinkedHashSet<EndpointId>(); - inspect.field(rotationsField).traverse((ArrayTraverser) (j, endpointId) -> endpointIds.add(EndpointId.of(endpointId.asString()))); - var id = new RoutingPolicyId(owner, - ClusterSpec.Id.from(inspect.field(clusterField).asString()), - ZoneId.from(inspect.field(zoneField).asString())); + Set<EndpointId> instanceEndpoints = new LinkedHashSet<>(); + inspect.field(instanceEndpointsField).traverse((ArrayTraverser) (j, endpointId) -> instanceEndpoints.add(EndpointId.of(endpointId.asString()))); + Set<EndpointId> applicationEndpoints = new LinkedHashSet<>(); + inspect.field(applicationEndpointsField).traverse((ArrayTraverser) (idx, endpointId) -> applicationEndpoints.add(EndpointId.of(endpointId.asString()))); + RoutingPolicyId id = new RoutingPolicyId(owner, + ClusterSpec.Id.from(inspect.field(clusterField).asString()), + ZoneId.from(inspect.field(zoneField).asString())); policies.put(id, new RoutingPolicy(id, HostName.from(inspect.field(canonicalNameField).asString()), SlimeUtils.optionalString(inspect.field(dnsZoneField)), - endpointIds, + instanceEndpoints, + applicationEndpoints, new Status(inspect.field(loadBalancerActiveField).asBool(), globalRoutingFromSlime(inspect.field(globalRoutingField))))); }); 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..2845dd53b24 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(); } @@ -2694,7 +2694,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { private static String endpointScopeString(Endpoint.Scope scope) { switch (scope) { - case region: return "region"; + case weighted: return "region"; case global: return "global"; case zone: return "zone"; } 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 546e0903023..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()); }); }); }); @@ -323,10 +321,10 @@ public class RoutingApiHandler extends AuditLoggingRequestHandler { private List<GlobalRouting> directGlobalRoutingStatus(DeploymentId deploymentId) { return controller.routing().policies().get(deploymentId).values().stream() - .filter(p -> ! p.endpoints().isEmpty()) // This policy does not apply to a global endpoint - .filter(p -> controller.zoneRegistry().routingMethods(p.id().zone()).contains(RoutingMethod.exclusive)) - .map(p -> p.status().globalRouting()) - .collect(Collectors.toList()); + .filter(p -> ! p.instanceEndpoints().isEmpty()) // This policy does not apply to a global endpoint + .filter(p -> controller.zoneRegistry().routingMethods(p.id().zone()).contains(RoutingMethod.exclusive)) + .map(p -> p.status().globalRouting()) + .collect(Collectors.toList()); } /** Returns whether a rotation can route traffic to given zone */ 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 6fbc37768e3..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())); @@ -135,7 +135,7 @@ public class RoutingPolicies { private void updateGlobalDnsOf(Endpoint endpoint, Set<ZoneId> inactiveZones, List<RoutingPolicy> policies) { if (endpoint.scope() != Endpoint.Scope.global) throw new IllegalArgumentException("Endpoint " + endpoint + " is not global"); // Create a weighted ALIAS per region, pointing to all zones within the same region - Collection<RegionEndpoint> regionEndpoints = computeRegionEndpoints(policies, inactiveZones, endpoint.legacy()); + Collection<RegionEndpoint> regionEndpoints = computeRegionEndpoints(policies, inactiveZones); regionEndpoints.forEach(regionEndpoint -> { controller.nameServiceForwarder().createAlias(RecordName.from(regionEndpoint.target().name().value()), Collections.unmodifiableSet(regionEndpoint.zoneTargets()), @@ -169,13 +169,13 @@ public class RoutingPolicies { /** Compute region endpoints and their targets from given policies */ - private Collection<RegionEndpoint> computeRegionEndpoints(List<RoutingPolicy> policies, Set<ZoneId> inactiveZones, boolean legacy) { + private Collection<RegionEndpoint> computeRegionEndpoints(List<RoutingPolicy> policies, Set<ZoneId> inactiveZones) { Map<Endpoint, RegionEndpoint> endpoints = new LinkedHashMap<>(); RoutingMethod routingMethod = RoutingMethod.exclusive; for (var policy : policies) { if (policy.dnsZone().isEmpty()) continue; if (!controller.zoneRegistry().routingMethods(policy.id().zone()).contains(routingMethod)) continue; - Endpoint regionEndpoint = policy.regionEndpointIn(controller.system(), routingMethod, legacy); + Endpoint regionEndpoint = policy.regionEndpointIn(controller.system(), routingMethod); var zonePolicy = db.readZoneRoutingPolicy(policy.id().zone()); long weight = 1; if (isConfiguredOut(policy, zonePolicy, inactiveZones)) { @@ -202,6 +202,7 @@ public class RoutingPolicies { var existingPolicy = policies.get(policyId); var newPolicy = new RoutingPolicy(policyId, loadBalancer.hostname().get(), loadBalancer.dnsZone(), allocation.endpointIdsOf(loadBalancer), + Set.of(), new Status(isActive(loadBalancer), GlobalRouting.DEFAULT_STATUS)); // Preserve global routing status for existing policy if (existingPolicy != null) { @@ -215,7 +216,7 @@ public class RoutingPolicies { /** Update zone DNS record for given policy */ private void updateZoneDnsOf(RoutingPolicy policy) { - for (var endpoint : policy.endpointsIn(controller.system(), RoutingMethod.exclusive, controller.zoneRegistry())) { + for (var endpoint : policy.zoneEndpointsIn(controller.system(), RoutingMethod.exclusive, controller.zoneRegistry())) { var name = RecordName.from(endpoint.dnsName()); var data = RecordData.fqdn(policy.canonicalName().value()); nameServiceForwarderIn(policy.id().zone()).createCname(name, data, Priority.normal); @@ -231,7 +232,7 @@ public class RoutingPolicies { // Leave active load balancers and irrelevant zones alone if (activeIds.contains(policy.id()) || !policy.id().zone().equals(allocation.deployment.zoneId())) continue; - for (var endpoint : policy.endpointsIn(controller.system(), RoutingMethod.exclusive, controller.zoneRegistry())) { + for (var endpoint : policy.zoneEndpointsIn(controller.system(), RoutingMethod.exclusive, controller.zoneRegistry())) { var dnsName = endpoint.dnsName(); nameServiceForwarderIn(allocation.deployment.zoneId()).removeRecords(Record.Type.CNAME, RecordName.from(dnsName), @@ -249,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()); @@ -273,7 +274,7 @@ public class RoutingPolicies { private static Map<RoutingId, List<RoutingPolicy>> routingTableFrom(Collection<RoutingPolicy> routingPolicies) { var routingTable = new LinkedHashMap<RoutingId, List<RoutingPolicy>>(); for (var policy : routingPolicies) { - for (var endpoint : policy.endpoints()) { + for (var endpoint : policy.instanceEndpoints()) { var id = new RoutingId(policy.id().owner(), endpoint); routingTable.computeIfAbsent(id, k -> new ArrayList<>()) .add(policy); 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 b4818be830d..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; @@ -29,16 +30,18 @@ public class RoutingPolicy { private final RoutingPolicyId id; private final HostName canonicalName; private final Optional<String> dnsZone; - private final Set<EndpointId> endpoints; + private final Set<EndpointId> instanceEndpoints; + private final Set<EndpointId> applicationEndpoints; private final Status status; /** DO NOT USE. Public for serialization purposes */ - public RoutingPolicy(RoutingPolicyId id, HostName canonicalName, Optional<String> dnsZone, Set<EndpointId> endpoints, - Status status) { + public RoutingPolicy(RoutingPolicyId id, HostName canonicalName, Optional<String> dnsZone, + Set<EndpointId> instanceEndpoints, Set<EndpointId> applicationEndpoints, Status status) { this.id = Objects.requireNonNull(id, "id must be non-null"); this.canonicalName = Objects.requireNonNull(canonicalName, "canonicalName must be non-null"); this.dnsZone = Objects.requireNonNull(dnsZone, "dnsZone must be non-null"); - this.endpoints = ImmutableSortedSet.copyOf(Objects.requireNonNull(endpoints, "endpoints must be non-null")); + this.instanceEndpoints = ImmutableSortedSet.copyOf(Objects.requireNonNull(instanceEndpoints, "instanceEndpoints must be non-null")); + this.applicationEndpoints = ImmutableSortedSet.copyOf(Objects.requireNonNull(applicationEndpoints, "applicationEndpoints must be non-null")); this.status = Objects.requireNonNull(status, "status must be non-null"); } @@ -57,9 +60,14 @@ public class RoutingPolicy { return dnsZone; } - /** The endpoints of this policy */ - public Set<EndpointId> endpoints() { - return endpoints; + /** The instance-level endpoints this participates in */ + public Set<EndpointId> instanceEndpoints() { + return instanceEndpoints; + } + + /** The application-level endpoints this participates in */ + public Set<EndpointId> applicationEndpoints() { + return applicationEndpoints; } /** Returns the status of this */ @@ -69,25 +77,26 @@ public class RoutingPolicy { /** Returns a copy of this with status set to given status */ public RoutingPolicy with(Status status) { - return new RoutingPolicy(id, canonicalName, dnsZone, endpoints, status); + return new RoutingPolicy(id, canonicalName, dnsZone, instanceEndpoints, applicationEndpoints, status); } /** Returns the zone endpoints of this */ - public List<Endpoint> endpointsIn(SystemName system, RoutingMethod routingMethod, ZoneRegistry zoneRegistry) { + public List<Endpoint> zoneEndpointsIn(SystemName system, RoutingMethod routingMethod, ZoneRegistry zoneRegistry) { Optional<Endpoint> infraEndpoint = SystemApplication.matching(id.owner()) .flatMap(app -> app.endpointIn(id.zone(), zoneRegistry)); 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)); @@ -95,18 +104,9 @@ public class RoutingPolicy { return endpoints; } - /** Returns all region endpoints of this */ - public List<Endpoint> regionEndpointsIn(SystemName system, RoutingMethod routingMethod) { - return List.of(regionEndpointIn(system, routingMethod, false)); - } - /** Returns the region endpoint of this */ - public Endpoint regionEndpointIn(SystemName system, RoutingMethod routingMethod, boolean legacy) { - Endpoint.EndpointBuilder endpoint = endpoint(routingMethod).targetRegion(id.cluster(), id.zone()); - if (legacy) { - endpoint = endpoint.legacy(); - } - return endpoint.in(system); + public Endpoint regionEndpointIn(SystemName system, RoutingMethod routingMethod) { + return endpoint(routingMethod).targetRegion(id.cluster(), id.zone()).in(system); } @Override @@ -124,9 +124,10 @@ public class RoutingPolicy { @Override public String toString() { - return Text.format("%s [endpoints: %s%s], %s owned by %s, in %s", canonicalName, endpoints, - dnsZone.map(z -> ", DNS zone: " + z).orElse(""), id.cluster(), id.owner().toShortString(), - id.zone().value()); + return Text.format("%s [instance endpoints: %s, application endpoints: %s%s], %s owned by %s, in %s", canonicalName, + instanceEndpoints, applicationEndpoints, + dnsZone.map(z -> ", DNS zone: " + z).orElse(""), id.cluster(), id.owner().toShortString(), + id.zone().value()); } private Endpoint.EndpointBuilder endpoint(RoutingMethod routingMethod) { |