diff options
author | Morten Tokle <mortent@verizonmedia.com> | 2021-11-05 10:38:14 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-11-05 10:38:14 +0100 |
commit | 0c959c5ebb2687b63b85c10561be3778d911258b (patch) | |
tree | 8d4f9ac6949713948f067ec7dda0b5ff5cc3007c /controller-server | |
parent | 9c4689e1094bba36a28299f04fbbed1fea5b88ee (diff) | |
parent | b47bd195bd82ffedea5d628f076884e1d84b7abd (diff) |
Merge pull request #19883 from vespa-engine/mpolden/application-endpoint-format
Finalize application endpoint names
Diffstat (limited to 'controller-server')
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>"); |