aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server/src/main
diff options
context:
space:
mode:
authorMorten Tokle <mortent@verizonmedia.com>2021-11-05 10:38:14 +0100
committerGitHub <noreply@github.com>2021-11-05 10:38:14 +0100
commit0c959c5ebb2687b63b85c10561be3778d911258b (patch)
tree8d4f9ac6949713948f067ec7dda0b5ff5cc3007c /controller-server/src/main
parent9c4689e1094bba36a28299f04fbbed1fea5b88ee (diff)
parentb47bd195bd82ffedea5d628f076884e1d84b7abd (diff)
Merge pull request #19883 from vespa-engine/mpolden/application-endpoint-format
Finalize application endpoint names
Diffstat (limited to 'controller-server/src/main')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java81
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java171
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointId.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java14
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializer.java27
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiHandler.java18
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingId.java22
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java17
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java53
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) {