summaryrefslogtreecommitdiffstats
path: root/controller-server
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
parent9c4689e1094bba36a28299f04fbbed1fea5b88ee (diff)
parentb47bd195bd82ffedea5d628f076884e1d84b7abd (diff)
Merge pull request #19883 from vespa-engine/mpolden/application-endpoint-format
Finalize application endpoint names
Diffstat (limited to 'controller-server')
-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
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java14
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java46
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java1
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java28
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java5
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java30
18 files changed, 296 insertions, 247 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) {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
index 887e51367dc..e2bc1fa305d 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
@@ -698,7 +698,6 @@ public class ControllerTest {
var testZone = ZoneId.from("test", "us-east-1");
tester.controllerTester().zoneRegistry().exclusiveRoutingIn(ZoneApiMock.from(prodZone));
var applicationPackage = new ApplicationPackageBuilder().athenzIdentity(AthenzDomain.from("domain"), AthenzService.from("service"))
- .compileVersion(RoutingController.DIRECT_ROUTING_MIN_VERSION)
.region(prodZone.region())
.build();
// Deploy app1 in production
@@ -786,7 +785,6 @@ public class ControllerTest {
var zone2 = ZoneId.from("prod", "us-east-3");
var applicationPackage = new ApplicationPackageBuilder()
.athenzIdentity(AthenzDomain.from("domain"), AthenzService.from("service"))
- .compileVersion(RoutingController.DIRECT_ROUTING_MIN_VERSION)
.endpoint("default", "default", zone1.region().value(), zone2.region().value())
.endpoint("east", "default", zone2.region().value())
.region(zone1.region())
@@ -849,8 +847,7 @@ public class ControllerTest {
.endpoint("default", "default")
.endpoint("foo", "qrs")
.endpoint("us", "default", zone1.region().value(), zone2.region().value())
- .athenzIdentity(AthenzDomain.from("domain"), AthenzService.from("service"))
- .compileVersion(RoutingController.DIRECT_ROUTING_MIN_VERSION);
+ .athenzIdentity(AthenzDomain.from("domain"), AthenzService.from("service"));
context.submit(applicationPackageBuilder.build()).deploy();
// Deployment passes container endpoints to config server
@@ -884,17 +881,12 @@ public class ControllerTest {
.map(Endpoint::routingMethod)
.collect(Collectors.toSet());
- // Without satisfying any requirement
- context.submit(applicationPackageBuilder.build()).deploy();
- assertEquals(Set.of(RoutingMethod.shared), routingMethods.get());
-
- // Without satisfying version requirement
- applicationPackageBuilder = applicationPackageBuilder.athenzIdentity(AthenzDomain.from("domain"), AthenzService.from("service"));
+ // Without satisfying requirements
context.submit(applicationPackageBuilder.build()).deploy();
assertEquals(Set.of(RoutingMethod.shared), routingMethods.get());
// Package satisfying all requirements is submitted, but not deployed yet
- applicationPackageBuilder = applicationPackageBuilder.compileVersion(RoutingController.DIRECT_ROUTING_MIN_VERSION);
+ applicationPackageBuilder = applicationPackageBuilder.athenzIdentity(AthenzDomain.from("domain"), AthenzService.from("service"));
var context2 = context.submit(applicationPackageBuilder.build());
assertEquals("Direct routing endpoint is available after submission and before deploy",
Set.of(RoutingMethod.shared, RoutingMethod.sharedLayer4), routingMethods.get());
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
index e2fb70e5338..c1f9137ecfa 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
@@ -150,8 +150,9 @@ public class EndpointTest {
@Test
public void zone_endpoints() {
var cluster = ClusterSpec.Id.from("default"); // Always default for non-direct routing
- var prodZone = ZoneId.from("prod", "us-north-1");
- var testZone = ZoneId.from("test", "us-north-2");
+ var prodZone = new DeploymentId(instance1, ZoneId.from("prod", "us-north-1"));
+ var prodZone2 = new DeploymentId(instance2, ZoneId.from("prod", "us-north-1"));
+ var testZone = new DeploymentId(instance1, ZoneId.from("test", "us-north-2"));
Map<String, Endpoint> tests = Map.of(
// Legacy endpoint (always contains environment)
@@ -180,7 +181,7 @@ public class EndpointTest {
// Non-default instance in main
"https://i2--a2--t2.us-north-1.vespa.oath.cloud:4443/",
- Endpoint.of(instance2).target(cluster, prodZone).on(Port.tls(4443)).in(SystemName.main),
+ Endpoint.of(instance2).target(cluster, prodZone2).on(Port.tls(4443)).in(SystemName.main),
// Non-default cluster in public
"https://c1.a1.t1.us-north-1.z.vespa-app.cloud/",
@@ -188,7 +189,7 @@ public class EndpointTest {
// Non-default cluster and instance in public
"https://c2.i2.a2.t2.us-north-1.z.vespa-app.cloud/",
- Endpoint.of(instance2).target(ClusterSpec.Id.from("c2"), prodZone).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public),
+ Endpoint.of(instance2).target(ClusterSpec.Id.from("c2"), prodZone2).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public),
// Endpoint in main using shared layer 4
"https://a1.t1.us-north-1.vespa.oath.cloud/",
@@ -199,7 +200,7 @@ public class EndpointTest {
Map<String, Endpoint> tests2 = Map.of(
// Non-default cluster and instance in public CD (legacy)
"https://c2.i2.a2.t2.us-north-1.z.cd.vespa-app.cloud/",
- Endpoint.of(instance2).target(ClusterSpec.Id.from("c2"), prodZone).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.PublicCd),
+ Endpoint.of(instance2).target(ClusterSpec.Id.from("c2"), prodZone2).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.PublicCd),
// Custom cluster name in public
"https://c1.a1.t1.us-north-1.z.vespa-app.cloud/",
@@ -219,8 +220,8 @@ public class EndpointTest {
@Test
public void wildcard_endpoints() {
var defaultCluster = ClusterSpec.Id.from("default");
- var prodZone = ZoneId.from("prod", "us-north-1");
- var testZone = ZoneId.from("test", "us-north-2");
+ var prodZone = new DeploymentId(instance1, ZoneId.from("prod", "us-north-1"));
+ var testZone = new DeploymentId(instance1, ZoneId.from("test", "us-north-2"));
var tests = Map.of(
// Default rotation
@@ -307,37 +308,37 @@ public class EndpointTest {
.in(SystemName.main);
assertEquals("Availability zone is removed from region",
"us-north-1",
- endpoint.zones().get(0).region().value());
+ endpoint.targets().get(0).deployment().zoneId().region().value());
}
@Test
public void application_endpoints() {
Map<String, Endpoint> tests = Map.of(
- "https://weighted.a1.t1.a.vespa-app.cloud/",
+ "https://weighted.a1.t1.us-west-1.r.vespa-app.cloud/",
Endpoint.of(app1)
.targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"),
- ZoneId.from("prod", "us-west-1"))
+ Map.of(new DeploymentId(app1.instance("i1"), ZoneId.from("prod", "us-west-1")), 1))
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.Public),
- "https://weighted.a1.t1.a.cd.vespa-app.cloud/",
+ "https://weighted.a1.t1.us-west-1.r.cd.vespa-app.cloud/",
Endpoint.of(app1)
.targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"),
- ZoneId.from("prod", "us-west-1"))
+ Map.of(new DeploymentId(app1.instance("i1"), ZoneId.from("prod", "us-west-1")), 1))
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.PublicCd),
- "https://a2.t2.a.vespa.oath.cloud/",
+ "https://a2.t2.us-east-3-r.vespa.oath.cloud/",
Endpoint.of(app2)
.targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"),
- ZoneId.from("prod", "us-east-3"))
+ Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1))
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.main),
- "https://cd.a2.t2.a.vespa.oath.cloud/",
+ "https://cd.a2.t2.us-east-3-r.vespa.oath.cloud/",
Endpoint.of(app2)
.targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"),
- ZoneId.from("prod", "us-east-3"))
+ Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1))
.routingMethod(RoutingMethod.exclusive)
.on(Port.tls())
.in(SystemName.cd)
@@ -347,7 +348,8 @@ public class EndpointTest {
@Test
public void upstream_name() {
- var zone = ZoneId.from("prod", "us-north-1");
+ var zone = new DeploymentId(instance1, ZoneId.from("prod", "us-north-1"));
+ var zone2 = new DeploymentId(instance2, ZoneId.from("prod", "us-north-1"));
var tests1 = Map.of(
// With default cluster
"a1.t1.us-north-1.prod",
@@ -359,7 +361,7 @@ public class EndpointTest {
// With application endpoint
"c2.a1.t1.us-north-1.prod",
- Endpoint.of(app1).targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("c2"), zone)
+ Endpoint.of(app1).targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("c2"), Map.of(new DeploymentId(app1.instance("i1"), zone.zoneId()), 1))
.routingMethod(RoutingMethod.sharedLayer4)
.on(Port.tls())
.in(SystemName.main)
@@ -367,14 +369,14 @@ public class EndpointTest {
var tests2 = Map.of(
// With non-default instance and default cluster
"i2.a2.t2.us-north-1.prod",
- Endpoint.of(instance2).target(EndpointId.defaultId(), ClusterSpec.Id.from("default"), List.of(zone)).on(Port.tls(4443)).in(SystemName.main),
+ Endpoint.of(instance2).target(EndpointId.defaultId(), ClusterSpec.Id.from("default"), List.of(zone2)).on(Port.tls(4443)).in(SystemName.main),
// With non-default instance and cluster
"c2.i2.a2.t2.us-north-1.prod",
- Endpoint.of(instance2).target(EndpointId.of("ignored2"), ClusterSpec.Id.from("c2"), List.of(zone)).on(Port.tls(4443)).in(SystemName.main)
+ Endpoint.of(instance2).target(EndpointId.of("ignored2"), ClusterSpec.Id.from("c2"), List.of(zone2)).on(Port.tls(4443)).in(SystemName.main)
);
- tests1.forEach((expected, endpoint) -> assertEquals(expected, endpoint.upstreamIdOf(new DeploymentId(instance1, zone))));
- tests2.forEach((expected, endpoint) -> assertEquals(expected, endpoint.upstreamIdOf(new DeploymentId(instance2, zone))));
+ tests1.forEach((expected, endpoint) -> assertEquals(expected, endpoint.upstreamIdOf(zone)));
+ tests2.forEach((expected, endpoint) -> assertEquals(expected, endpoint.upstreamIdOf(zone2)));
}
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
index 1ec5d31e99d..ba6ab4b9152 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
@@ -240,6 +240,7 @@ public class DeploymentContext {
policies.put(id, new RoutingPolicy(id, HostName.from("lb-host"),
Optional.empty(),
Set.of(EndpointId.of("default")),
+ Set.of(),
new Status(false, GlobalRouting.DEFAULT_STATUS)));
tester.controller().curator().writeRoutingPolicies(instanceId, policies);
return this;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
index 374ec10d72e..11086ff7663 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
@@ -13,7 +13,6 @@ import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.slime.Inspector;
import com.yahoo.slime.SlimeUtils;
-import com.yahoo.vespa.hosted.controller.RoutingController;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.configserverbindings.ConfigChangeActions;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.configserverbindings.RestartAction;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.configserverbindings.ServiceInfo;
@@ -25,8 +24,8 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterCloud;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockMailer;
-import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.SystemApplication;
+import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.config.ControllerConfig;
import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock;
import org.junit.Before;
@@ -248,7 +247,6 @@ public class InternalStepRunnerTest {
.upgradePolicy("default")
.region("us-central-1")
.parallel("us-west-1", "us-east-3")
- .compileVersion(RoutingController.DIRECT_ROUTING_MIN_VERSION)
.build();
app.submit(applicationPackage)
.triggerJobs();
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java
index 49eb4eb2165..f4475038b17 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java
@@ -30,7 +30,8 @@ public class RoutingPolicySerializerTest {
@Test
public void serialization() {
var owner = ApplicationId.defaultId();
- var endpoints = Set.of(EndpointId.of("r1"), EndpointId.of("r2"));
+ var instanceEndpoints = Set.of(EndpointId.of("r1"), EndpointId.of("r2"));
+ var applicationEndpoints = Set.of(EndpointId.of("a1"));
var id1 = new RoutingPolicyId(owner,
ClusterSpec.Id.from("my-cluster1"),
ZoneId.from("prod", "us-north-1"));
@@ -38,16 +39,20 @@ public class RoutingPolicySerializerTest {
ClusterSpec.Id.from("my-cluster2"),
ZoneId.from("prod", "us-north-2"));
var policies = ImmutableMap.of(id1, new RoutingPolicy(id1,
- HostName.from("long-and-ugly-name"),
- Optional.of("zone1"),
- endpoints, new Status(true, GlobalRouting.DEFAULT_STATUS)),
+ HostName.from("long-and-ugly-name"),
+ Optional.of("zone1"),
+ instanceEndpoints,
+ applicationEndpoints,
+ new Status(true, GlobalRouting.DEFAULT_STATUS)),
id2, new RoutingPolicy(id2,
- HostName.from("long-and-ugly-name-2"),
- Optional.empty(),
- endpoints, new Status(false,
- new GlobalRouting(GlobalRouting.Status.out,
- GlobalRouting.Agent.tenant,
- Instant.ofEpochSecond(123)))));
+ HostName.from("long-and-ugly-name-2"),
+ Optional.empty(),
+ instanceEndpoints,
+ Set.of(),
+ new Status(false,
+ new GlobalRouting(GlobalRouting.Status.out,
+ GlobalRouting.Agent.tenant,
+ Instant.ofEpochSecond(123)))));
var serialized = serializer.fromSlime(owner, serializer.toSlime(policies));
assertEquals(policies.size(), serialized.size());
for (Iterator<RoutingPolicy> it1 = policies.values().iterator(), it2 = serialized.values().iterator(); it1.hasNext();) {
@@ -56,7 +61,8 @@ public class RoutingPolicySerializerTest {
assertEquals(expected.id(), actual.id());
assertEquals(expected.canonicalName(), actual.canonicalName());
assertEquals(expected.dnsZone(), actual.dnsZone());
- assertEquals(expected.endpoints(), actual.endpoints());
+ assertEquals(expected.instanceEndpoints(), actual.instanceEndpoints());
+ assertEquals(expected.applicationEndpoints(), actual.applicationEndpoints());
assertEquals(expected.status(), actual.status());
}
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
index d2eb43d31f8..e204a8f820d 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
@@ -31,7 +31,6 @@ import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.ControllerTester;
import com.yahoo.vespa.hosted.controller.Instance;
import com.yahoo.vespa.hosted.controller.LockedTenant;
-import com.yahoo.vespa.hosted.controller.RoutingController;
import com.yahoo.vespa.hosted.controller.api.application.v4.EnvironmentResource;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.ProtonMetrics;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
@@ -52,11 +51,11 @@ import com.yahoo.vespa.hosted.controller.api.integration.resource.MeteringData;
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceAllocation;
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshot;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockMeteringClient;
-import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.Change;
import com.yahoo.vespa.hosted.controller.application.Deployment;
import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics;
import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
+import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.athenz.HostedAthenzIdentities;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
@@ -1523,7 +1522,6 @@ public class ApplicationApiTest extends ControllerContainerTest {
List.of(RoutingMethod.exclusive, RoutingMethod.shared));
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
.athenzIdentity(com.yahoo.config.provision.AthenzDomain.from("domain"), AthenzService.from("service"))
- .compileVersion(RoutingController.DIRECT_ROUTING_MIN_VERSION)
.instances("instance1")
.region(zone.region().value())
.build();
@@ -1564,7 +1562,6 @@ public class ApplicationApiTest extends ControllerContainerTest {
addUserToHostedOperatorRole(HostedAthenzIdentities.from(HOSTED_VESPA_OPERATOR));
ApplicationPackage applicationPackage = new ApplicationPackageBuilder()
.athenzIdentity(com.yahoo.config.provision.AthenzDomain.from("domain"), AthenzService.from("service"))
- .compileVersion(RoutingController.DIRECT_ROUTING_MIN_VERSION)
.instances("instance1")
.region(zone.region().value())
.build();
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java
index f8a7dcc5700..41607b17b52 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiTest.java
@@ -8,7 +8,6 @@ import com.yahoo.config.provision.AthenzService;
import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.ControllerTester;
-import com.yahoo.vespa.hosted.controller.RoutingController;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock;
@@ -131,7 +130,6 @@ public class RoutingApiTest extends ControllerContainerTest {
// Deploy application
var applicationPackage = new ApplicationPackageBuilder()
.athenzIdentity(AthenzDomain.from("domain"), AthenzService.from("service"))
- .compileVersion(RoutingController.DIRECT_ROUTING_MIN_VERSION)
.region(westZone.region())
.region(eastZone.region())
.endpoint("default", "default", eastZone.region().value(), westZone.region().value())
@@ -183,7 +181,6 @@ public class RoutingApiTest extends ControllerContainerTest {
// Endpoint is removed
applicationPackage = new ApplicationPackageBuilder()
.athenzIdentity(AthenzDomain.from("domain"), AthenzService.from("service"))
- .compileVersion(RoutingController.DIRECT_ROUTING_MIN_VERSION)
.region(westZone.region())
.region(eastZone.region())
.allow(ValidationId.globalEndpointChange)
@@ -313,7 +310,6 @@ public class RoutingApiTest extends ControllerContainerTest {
.region(westZone.region())
.region(eastZone.region())
.athenzIdentity(AthenzDomain.from("domain"), AthenzService.from("service"))
- .compileVersion(RoutingController.DIRECT_ROUTING_MIN_VERSION)
.endpoint("endpoint1", "default", westZone.region().value())
.endpoint("endpoint2", "default", eastZone.region().value())
.build();
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java
index c6d8116f59c..e2ef27492af 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java
@@ -17,18 +17,17 @@ import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.ControllerTester;
import com.yahoo.vespa.hosted.controller.Instance;
-import com.yahoo.vespa.hosted.controller.RoutingController;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.LoadBalancer;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType;
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;
-import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.Endpoint;
import com.yahoo.vespa.hosted.controller.application.EndpointId;
import com.yahoo.vespa.hosted.controller.application.EndpointList;
import com.yahoo.vespa.hosted.controller.application.SystemApplication;
+import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
@@ -138,7 +137,7 @@ public class RoutingPoliciesTest {
var policies = tester.policiesOf(context1.instanceId());
assertEquals(clustersPerZone * numberOfDeployments, policies.size());
assertTrue("Rotation membership is removed from all policies",
- policies.stream().allMatch(policy -> policy.endpoints().isEmpty()));
+ policies.stream().allMatch(policy -> policy.instanceEndpoints().isEmpty()));
assertEquals("Rotations for " + context2.application() + " are not removed", 2, tester.aliasDataOf(endpoint4).size());
}
@@ -361,7 +360,7 @@ public class RoutingPoliciesTest {
tester.assertTargets(context.instanceId(), EndpointId.defaultId(),
ClusterSpec.Id.from("default"), 0,
- Map.of(zone1, 1L, zone2, 1L), true);
+ Map.of(zone1, 1L, zone2, 1L));
assertEquals("Registers expected DNS names",
Set.of("app1.tenant1.aws-eu-west-1.w.vespa-app.cloud",
"app1.tenant1.aws-eu-west-1a.z.vespa-app.cloud",
@@ -705,8 +704,7 @@ public class RoutingPoliciesTest {
/** Returns an application package builder that satisfies requirements for a directly routed endpoint */
private static ApplicationPackageBuilder applicationPackageBuilder() {
return new ApplicationPackageBuilder()
- .athenzIdentity(AthenzDomain.from("domain"), AthenzService.from("service"))
- .compileVersion(RoutingController.DIRECT_ROUTING_MIN_VERSION);
+ .athenzIdentity(AthenzDomain.from("domain"), AthenzService.from("service"));
}
private static List<LoadBalancer> createLoadBalancers(ZoneId zone, ApplicationId application, boolean shared, int count) {
@@ -821,21 +819,14 @@ public class RoutingPoliciesTest {
.collect(Collectors.toList());
}
- private void assertTargets(ApplicationId application, EndpointId endpointId, ClusterSpec.Id cluster, int loadBalancerId, Map<ZoneId, Long> zoneWeights) {
- assertTargets(application, endpointId, cluster, loadBalancerId, zoneWeights, false);
- }
-
- private void assertTargets(ApplicationId application, EndpointId endpointId, ClusterSpec.Id cluster, int loadBalancerId, Map<ZoneId, Long> zoneWeights, boolean legacy) {
+ private void assertTargets(ApplicationId instance, EndpointId endpointId, ClusterSpec.Id cluster, int loadBalancerId, Map<ZoneId, Long> zoneWeights) {
Set<String> latencyTargets = new HashSet<>();
Map<String, List<ZoneId>> zonesByRegionEndpoint = new HashMap<>();
for (var zone : zoneWeights.keySet()) {
- DeploymentId deployment = new DeploymentId(application, zone);
+ DeploymentId deployment = new DeploymentId(instance, zone);
EndpointList regionEndpoints = tester.controller().routing().endpointsOf(deployment)
.cluster(cluster)
- .scope(Endpoint.Scope.region);
- if (!legacy) {
- regionEndpoints = regionEndpoints.not().legacy();
- }
+ .scope(Endpoint.Scope.weighted);
Endpoint regionEndpoint = regionEndpoints.first().orElseThrow(() -> new IllegalArgumentException("No region endpoint found for " + cluster + " in " + deployment));
zonesByRegionEndpoint.computeIfAbsent(regionEndpoint.dnsName(), (k) -> new ArrayList<>())
.add(zone);
@@ -843,7 +834,7 @@ public class RoutingPoliciesTest {
zonesByRegionEndpoint.forEach((regionEndpoint, zonesInRegion) -> {
Set<String> weightedTargets = zonesInRegion.stream()
.map(z -> "weighted/lb-" + loadBalancerId + "--" +
- application.serializedForm() + "--" + z.value() +
+ instance.serializedForm() + "--" + z.value() +
"/dns-zone-1/" + z.value() + "/" + zoneWeights.get(z))
.collect(Collectors.toSet());
assertEquals("Region endpoint " + regionEndpoint + " points to load balancer",
@@ -853,9 +844,10 @@ public class RoutingPoliciesTest {
String latencyTarget = "latency/" + regionEndpoint + "/dns-zone-1/" + zone.value();
latencyTargets.add(latencyTarget);
});
- String globalEndpoint = tester.controller().routing().endpointsOf(application)
+ List<DeploymentId> deployments = zoneWeights.keySet().stream().map(z -> new DeploymentId(instance, z)).collect(Collectors.toList());
+ String globalEndpoint = tester.controller().routing().endpointsOf(instance)
.named(endpointId)
- .targets(List.copyOf(zoneWeights.keySet()))
+ .targets(deployments)
.primary()
.map(Endpoint::dnsName)
.orElse("<none>");