diff options
author | Martin Polden <mpolden@mpolden.no> | 2022-11-16 13:16:23 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-16 13:16:23 +0100 |
commit | b393c0c7cc3d3bda40d7694baee5580950f97004 (patch) | |
tree | 5fb3f08587277c3548467d8250066d5fce5e0ee8 | |
parent | 5ab24c9563828c4df64742d05f840c25dd619c45 (diff) | |
parent | 7f2f6aff04c8aafc038c55a3aa288b57eeb47d2d (diff) |
Merge pull request #24878 from vespa-engine/jonmv/relax-cloud-account-requirements
Collapse multiple regions to a single application endpoint
13 files changed, 219 insertions, 110 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 38c06e4dac2..84aa26b93f3 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 @@ -20,6 +20,7 @@ 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.Endpoint; import com.yahoo.vespa.hosted.controller.application.Endpoint.Port; +import com.yahoo.vespa.hosted.controller.application.Endpoint.Scope; import com.yahoo.vespa.hosted.controller.application.EndpointId; import com.yahoo.vespa.hosted.controller.application.EndpointList; import com.yahoo.vespa.hosted.controller.application.SystemApplication; @@ -115,7 +116,7 @@ public class RoutingController { if (!policy.status().isActive()) continue; RoutingMethod routingMethod = controller.zoneRegistry().routingMethod(policy.id().zone()); endpoints.addAll(policy.zoneEndpointsIn(controller.system(), routingMethod, controller.zoneRegistry())); - endpoints.add(policy.regionEndpointIn(controller.system(), routingMethod)); + endpoints.add(policy.regionEndpointIn(controller.system(), routingMethod, controller.zoneRegistry())); } return EndpointList.copyOf(endpoints); } @@ -158,23 +159,21 @@ public class RoutingController { } // Add application endpoints for (var declaredEndpoint : deploymentSpec.endpoints()) { - Map<ZoneId, Map<DeploymentId, Integer>> deployments = declaredEndpoint.targets().stream() - .collect(groupingBy(t -> ZoneId.from(Environment.prod, t.region()), - toMap(t -> new DeploymentId(application.id().instance(t.instance()), - ZoneId.from(Environment.prod, t.region())), - t -> t.weight()))); - - deployments.forEach((zone, weightedInstances) -> { - // Application endpoints are only supported when using direct routing methods - RoutingMethod routingMethod = usesSharedRouting(zone) ? RoutingMethod.sharedLayer4 : RoutingMethod.exclusive; - endpoints.add(Endpoint.of(application.id()) - .targetApplication(EndpointId.of(declaredEndpoint.endpointId()), - ClusterSpec.Id.from(declaredEndpoint.containerId()), - weightedInstances) - .routingMethod(routingMethod) - .on(Port.fromRoutingMethod(routingMethod)) - .in(controller.system())); - }); + Map<DeploymentId, Integer> deployments = declaredEndpoint.targets().stream() + .collect(toMap(t -> new DeploymentId(application.id().instance(t.instance()), + ZoneId.from(Environment.prod, t.region())), + t -> t.weight())); + + ZoneId zone = deployments.keySet().iterator().next().zoneId(); // Where multiple zones are possible, they all have the same routing method. + // Application endpoints are only supported when using direct routing methods + RoutingMethod routingMethod = usesSharedRouting(zone) ? RoutingMethod.sharedLayer4 : RoutingMethod.exclusive; + endpoints.add(Endpoint.of(application.id()) + .targetApplication(EndpointId.of(declaredEndpoint.endpointId()), + ClusterSpec.Id.from(declaredEndpoint.containerId()), + deployments) + .routingMethod(routingMethod) + .on(Port.fromRoutingMethod(routingMethod)) + .in(controller.system())); } return EndpointList.copyOf(endpoints); } @@ -236,6 +235,7 @@ public class RoutingController { .on(Port.tls()) .in(controller.system()); endpointDnsNames.add(endpoint.dnsName()); + if (endpoint.scope() == Scope.application) endpointDnsNames.add(endpoint.legacyRegionalDnsName()); } return Collections.unmodifiableList(endpointDnsNames); } @@ -313,6 +313,9 @@ public class RoutingController { controller.nameServiceForwarder().createRecord( new Record(Record.Type.CNAME, RecordName.from(endpoint.dnsName()), RecordData.fqdn(vipHostname)), Priority.normal); + controller.nameServiceForwarder().createRecord( + new Record(Record.Type.CNAME, RecordName.from(endpoint.legacyRegionalDnsName()), RecordData.fqdn(vipHostname)), + Priority.normal); } Map<ClusterSpec.Id, EndpointList> applicationEndpointsByCluster = applicationEndpoints.groupingBy(Endpoint::cluster); for (var kv : applicationEndpointsByCluster.entrySet()) { @@ -325,7 +328,7 @@ public class RoutingController { if (matchingTarget.isEmpty()) throw new IllegalStateException("No target found routing to " + deployment + " in " + endpoint); containerEndpoints.add(new ContainerEndpoint(clusterId.value(), asString(Endpoint.Scope.application), - List.of(endpoint.dnsName()), + List.of(endpoint.dnsName(), endpoint.legacyRegionalDnsName()), OptionalInt.of(matchingTarget.get().weight()), endpoint.routingMethod())); } 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 88366466289..cbac700a9a0 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 @@ -20,9 +20,11 @@ import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; +import static java.util.Comparator.comparing; + /** * Represents an application or instance endpoint in hosted Vespa. - * + * <p> * This encapsulates the logic for building URLs and DNS names for applications in all hosted Vespa systems. * * @author mpolden @@ -38,13 +40,14 @@ public class Endpoint { private final ClusterSpec.Id cluster; private final Optional<InstanceName> instance; private final URI url; + private final URI legacyRegionalUrl; private final List<Target> targets; private final Scope scope; private final boolean legacy; private final 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, + ClusterSpec.Id cluster, URI url, URI legacyRegionalUrl, List<Target> targets, Scope scope, Port port, boolean legacy, RoutingMethod routingMethod, boolean certificateName) { Objects.requireNonNull(application, "application must be non-null"); Objects.requireNonNull(instanceName, "instanceName must be non-null"); @@ -58,6 +61,7 @@ public class Endpoint { this.cluster = requireCluster(cluster, certificateName); this.instance = requireInstance(instanceName, scope); this.url = url; + this.legacyRegionalUrl = legacyRegionalUrl; this.targets = List.copyOf(requireTargets(targets, application, instanceName, scope, certificateName)); this.scope = requireScope(scope, routingMethod); this.legacy = legacy; @@ -96,6 +100,12 @@ public class Endpoint { return url.getAuthority().replaceAll(":.*", ""); } + /** Returns the legacy DNS name with region, for application endpoints */ + public String legacyRegionalDnsName() { + if (scope != Scope.application) throw new IllegalStateException("legacy regional URL is only for application scope endpoints, not " + this); + return legacyRegionalUrl.getAuthority().replaceAll(":.*", ""); + } + /** Returns the target(s) to which this routes traffic */ public List<Target> targets() { return targets; @@ -160,7 +170,8 @@ public class Endpoint { } private static URI createUrl(String name, TenantAndApplicationId application, Optional<InstanceName> instance, - List<Target> targets, Scope scope, SystemName system, Port port, boolean legacy) { + List<Target> targets, Scope scope, SystemName system, Port port, boolean legacyRegionalUrl) { + String separator = "."; String portPart = port.isDefault() ? "" : ":" + port.port; return URI.create("https://" + @@ -171,8 +182,8 @@ public class Endpoint { separator + sanitize(application.tenant().value()) + "." + - scopePart(scope, targets, system, legacy) + - dnsSuffix(system, legacy) + + scopePart(scope, targets, system, legacyRegionalUrl) + + dnsSuffix(system) + portPart + "/"); } @@ -186,13 +197,14 @@ public class Endpoint { return name + separator; } - private static String scopePart(Scope scope, List<Target> targets, SystemName system, boolean legacy) { - String scopeSymbol = scopeSymbol(scope, system); + private static String scopePart(Scope scope, List<Target> targets, SystemName system, boolean legacyRegion) { + String scopeSymbol = scopeSymbol(scope, system, legacyRegion); if (scope == Scope.global) return scopeSymbol; + if (scope == Scope.application && ! legacyRegion) return scopeSymbol; - ZoneId zone = targets.get(0).deployment().zoneId(); + ZoneId zone = targets.stream().map(target -> target.deployment.zoneId()).min(comparing(ZoneId::value)).get(); String region = zone.region().value(); - boolean skipEnvironment = zone.environment().isProduction() && (system.isPublic() || !legacy); + boolean skipEnvironment = zone.environment().isProduction(); String environment = skipEnvironment ? "" : "." + zone.environment().value(); if (system.isPublic()) { return region + environment + "." + scopeSymbol; @@ -200,20 +212,21 @@ public class Endpoint { return region + (scopeSymbol.isEmpty() ? "" : "-" + scopeSymbol) + environment; } - private static String scopeSymbol(Scope scope, SystemName system) { + private static String scopeSymbol(Scope scope, SystemName system, boolean legacyRegion) { + if (legacyRegion) return "r"; if (system.isPublic()) { return switch (scope) { case zone -> "z"; case weighted -> "w"; case global -> "g"; - case application -> "r"; + case application -> "a"; }; } return switch (scope) { case zone -> ""; case weighted -> "w"; case global -> "global"; - case application -> "r"; + case application -> "a"; }; } @@ -230,18 +243,15 @@ public class Endpoint { } /** Returns the DNS suffix used for endpoints in given system */ - private static String dnsSuffix(SystemName system, boolean legacy) { + private static String dnsSuffix(SystemName system) { switch (system) { case cd, main -> { - if (legacy) return YAHOO_DNS_SUFFIX; return OATH_DNS_SUFFIX; } case Public -> { - if (legacy) throw new IllegalArgumentException("No legacy DNS suffix declared for system " + system); return PUBLIC_DNS_SUFFIX; } case PublicCd -> { - if (legacy) throw new IllegalArgumentException("No legacy DNS suffix declared for system " + system); return PUBLIC_CD_DNS_SUFFIX; } default -> throw new IllegalArgumentException("No DNS suffix declared for system " + system); @@ -250,7 +260,7 @@ public class Endpoint { /** Returns the DNS suffix used for internal names (i.e. names not exposed to tenants) in given system */ public static String internalDnsSuffix(SystemName system) { - String suffix = dnsSuffix(system, false); + String suffix = dnsSuffix(system); if (system.isPublic()) { // Certificate provider requires special approval for three-level DNS names, e.g. foo.vespa-app.cloud. // To avoid this in public we always add an extra level. @@ -578,12 +588,22 @@ public class Endpoint { Objects.requireNonNull(scope, "scope must be non-null"), Objects.requireNonNull(system, "system must be non-null"), Objects.requireNonNull(port, "port must be non-null"), - legacy); + false); + URI legacyRegionalUrl = scope != Scope.application ? null + : createUrl(endpointOrClusterAsString(endpointId, cluster), + Objects.requireNonNull(application, "application must be non-null"), + Objects.requireNonNull(instance, "instance must be non-null"), + Objects.requireNonNull(targets, "targets must be non-null"), + Objects.requireNonNull(scope, "scope must be non-null"), + Objects.requireNonNull(system, "system must be non-null"), + Objects.requireNonNull(port, "port must be non-null"), + true); return new Endpoint(application, instance, endpointId, cluster, url, + legacyRegionalUrl, targets, scope, port, 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 27247c065ed..b0d16126600 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 @@ -215,7 +215,7 @@ public class RoutingPolicies { for (var policy : policies) { if (policy.dnsZone().isEmpty() && policy.canonicalName().isPresent()) continue; if (controller.zoneRegistry().routingMethod(policy.id().zone()) != RoutingMethod.exclusive) continue; - Endpoint endpoint = policy.regionEndpointIn(controller.system(), RoutingMethod.exclusive); + Endpoint endpoint = policy.regionEndpointIn(controller.system(), RoutingMethod.exclusive, controller.zoneRegistry()); var zonePolicy = db.readZoneRoutingPolicy(policy.id().zone()); long weight = 1; if (isConfiguredOut(zonePolicy, policy, inactiveZones)) { @@ -289,12 +289,10 @@ public class RoutingPolicies { activeTargets.addAll(inactiveTargets); inactiveTargets.clear(); } + targetsByEndpoint.forEach((applicationEndpoint, targets) -> { - ZoneId targetZone = applicationEndpoint.targets().stream() - .map(Endpoint.Target::deployment) - .map(DeploymentId::zoneId) - .findFirst() - .get(); + // Where multiple zones are permitted, they all have the same routing policy, and nameServiceForwarder (below). + ZoneId targetZone = applicationEndpoint.targets().iterator().next().deployment().zoneId(); Set<AliasTarget> aliasTargets = new LinkedHashSet<>(); Set<DirectTarget> directTargets = new LinkedHashSet<>(); for (Target target : targets) { @@ -305,23 +303,28 @@ public class RoutingPolicies { if ( ! aliasTargets.isEmpty()) { nameServiceForwarderIn(targetZone).createAlias( RecordName.from(applicationEndpoint.dnsName()), aliasTargets, Priority.normal); + nameServiceForwarderIn(targetZone).createAlias( + RecordName.from(applicationEndpoint.legacyRegionalDnsName()), aliasTargets, Priority.normal); } if ( ! directTargets.isEmpty()) { nameServiceForwarderIn(targetZone).createDirect( RecordName.from(applicationEndpoint.dnsName()), directTargets, Priority.normal); + nameServiceForwarderIn(targetZone).createDirect( + RecordName.from(applicationEndpoint.legacyRegionalDnsName()), directTargets, Priority.normal); } }); inactiveTargetsByEndpoint.forEach((applicationEndpoint, targets) -> { - ZoneId targetZone = applicationEndpoint.targets().stream() - .map(Endpoint.Target::deployment) - .map(DeploymentId::zoneId) - .findFirst() - .get(); + // Where multiple zones are permitted, they all have the same routing policy, and nameServiceForwarder. + ZoneId targetZone = applicationEndpoint.targets().iterator().next().deployment().zoneId(); targets.forEach(target -> { nameServiceForwarderIn(targetZone).removeRecords(target.type(), RecordName.from(applicationEndpoint.dnsName()), target.data(), Priority.normal); + nameServiceForwarderIn(targetZone).removeRecords(target.type(), + RecordName.from(applicationEndpoint.legacyRegionalDnsName()), + target.data(), + Priority.normal); }); }); } @@ -377,9 +380,8 @@ public class RoutingPolicies { .not().matching(policy -> activeIds.contains(policy.id())); for (var policy : removable) { 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), + RecordName.from(endpoint.dnsName()), Priority.normal); } newPolicies.remove(policy.id()); @@ -424,14 +426,22 @@ public class RoutingPolicies { for (Endpoint endpoint : endpoints) { if (policy.canonicalName().isPresent()) { forwarder.removeRecords(Record.Type.ALIAS, - RecordName.from(endpoint.dnsName()), - RecordData.fqdn(policy.canonicalName().get().value()), - Priority.normal); + RecordName.from(endpoint.dnsName()), + RecordData.fqdn(policy.canonicalName().get().value()), + Priority.normal); + forwarder.removeRecords(Record.Type.ALIAS, + RecordName.from(endpoint.legacyRegionalDnsName()), + RecordData.fqdn(policy.canonicalName().get().value()), + Priority.normal); } else { forwarder.removeRecords(Record.Type.DIRECT, - RecordName.from(endpoint.dnsName()), - RecordData.from(policy.ipAddress().get()), - Priority.normal); + RecordName.from(endpoint.dnsName()), + RecordData.from(policy.ipAddress().get()), + Priority.normal); + forwarder.removeRecords(Record.Type.DIRECT, + RecordName.from(endpoint.legacyRegionalDnsName()), + RecordData.from(policy.ipAddress().get()), + Priority.normal); } } } 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 04c32590a4c..6ae729a3c02 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 @@ -96,12 +96,12 @@ public record RoutingPolicy(RoutingPolicyId id, /** Returns the zone endpoints of this */ public List<Endpoint> zoneEndpointsIn(SystemName system, RoutingMethod routingMethod, ZoneRegistry zoneRegistry) { DeploymentId deployment = new DeploymentId(id.owner(), id.zone()); - return List.of(endpoint(routingMethod).target(id.cluster(), deployment).in(system)); + return List.of(endpoint(routingMethod, zoneRegistry).target(id.cluster(), deployment).in(system)); } /** Returns the region endpoint of this */ - public Endpoint regionEndpointIn(SystemName system, RoutingMethod routingMethod) { - return endpoint(routingMethod).targetRegion(id.cluster(), id.zone()).in(system); + public Endpoint regionEndpointIn(SystemName system, RoutingMethod routingMethod, ZoneRegistry zoneRegistry) { + return endpoint(routingMethod, zoneRegistry).targetRegion(id.cluster(), id.zone()).in(system); } @Override @@ -125,7 +125,7 @@ public record RoutingPolicy(RoutingPolicyId id, id.zone().value()); } - private Endpoint.EndpointBuilder endpoint(RoutingMethod routingMethod) { + private Endpoint.EndpointBuilder endpoint(RoutingMethod routingMethod, ZoneRegistry zones) { return Endpoint.of(id.owner()) .on(Port.fromRoutingMethod(routingMethod)) .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 c87a4e490b9..978587d9c5c 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 @@ -664,22 +664,22 @@ public class ControllerTest { ZoneId east1a = ZoneId.from("prod", "aws-us-east-1a"); ZoneId east1b = ZoneId.from("prod", "aws-us-east-1b"); // Expected container endpoints are passed to each deployment - Map<DeploymentId, Map<String, Integer>> deploymentEndpoints = Map.of( + Map<DeploymentId, Map<List<String>, Integer>> deploymentEndpoints = Map.of( new DeploymentId(beta, east3), Map.of(), - new DeploymentId(main, east3), Map.of("e.app1.tenant1.us-east-3-r.vespa.oath.cloud", 3), - new DeploymentId(beta, west1), Map.of("d.app1.tenant1.us-west-1-r.vespa.oath.cloud", 3), - new DeploymentId(main, west1), Map.of("d.app1.tenant1.us-west-1-r.vespa.oath.cloud", 7), - new DeploymentId(beta, east1a), Map.of("a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud", 2, - "b.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud", 1), - new DeploymentId(main, east1a), Map.of("a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud", 8, - "b.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud", 1), - new DeploymentId(beta, east1b), Map.of("c.app1.tenant1.aws-us-east-1b-r.vespa.oath.cloud", 4), - new DeploymentId(main, east1b), Map.of("a.app1.tenant1.aws-us-east-1b-r.vespa.oath.cloud", 1) + new DeploymentId(main, east3), Map.of(List.of("e.app1.tenant1.a.vespa.oath.cloud", "e.app1.tenant1.us-east-3-r.vespa.oath.cloud"), 3), + new DeploymentId(beta, west1), Map.of(List.of("d.app1.tenant1.a.vespa.oath.cloud", "d.app1.tenant1.us-west-1-r.vespa.oath.cloud"), 3), + new DeploymentId(main, west1), Map.of(List.of("d.app1.tenant1.a.vespa.oath.cloud", "d.app1.tenant1.us-west-1-r.vespa.oath.cloud"), 7), + new DeploymentId(beta, east1a), Map.of(List.of("a.app1.tenant1.a.vespa.oath.cloud", "a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"), 2, + List.of("b.app1.tenant1.a.vespa.oath.cloud", "b.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"), 1), + new DeploymentId(main, east1a), Map.of(List.of("a.app1.tenant1.a.vespa.oath.cloud", "a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"), 8, + List.of("b.app1.tenant1.a.vespa.oath.cloud", "b.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"), 1), + new DeploymentId(beta, east1b), Map.of(List.of("c.app1.tenant1.a.vespa.oath.cloud", "c.app1.tenant1.aws-us-east-1b-r.vespa.oath.cloud"), 4), + new DeploymentId(main, east1b), Map.of(List.of("a.app1.tenant1.a.vespa.oath.cloud", "a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"), 1) ); deploymentEndpoints.forEach((deployment, endpoints) -> { Set<ContainerEndpoint> expected = endpoints.entrySet().stream() .map(kv -> new ContainerEndpoint("default", "application", - List.of(kv.getKey()), + kv.getKey(), OptionalInt.of(kv.getValue()), tester.controller().zoneRegistry().routingMethod(deployment.zoneId()))) .collect(Collectors.toSet()); @@ -704,13 +704,37 @@ public class ControllerTest { RecordName.from("main.app1.tenant1.aws-us-east-1b.vespa.oath.cloud"), RecordData.from("lb-0--tenant1.app1.main--prod.aws-us-east-1b.")), new Record(Record.Type.ALIAS, + RecordName.from("a.app1.tenant1.a.vespa.oath.cloud"), + RecordData.from("weighted/lb-0--tenant1.app1.beta--prod.aws-us-east-1a/dns-zone-1/prod.aws-us-east-1a/2")), + new Record(Record.Type.ALIAS, + RecordName.from("a.app1.tenant1.a.vespa.oath.cloud"), + RecordData.from("weighted/lb-0--tenant1.app1.main--prod.aws-us-east-1a/dns-zone-1/prod.aws-us-east-1a/8")), + new Record(Record.Type.ALIAS, + RecordName.from("a.app1.tenant1.a.vespa.oath.cloud"), + RecordData.from("weighted/lb-0--tenant1.app1.main--prod.aws-us-east-1b/dns-zone-1/prod.aws-us-east-1b/1")), + new Record(Record.Type.ALIAS, + RecordName.from("b.app1.tenant1.a.vespa.oath.cloud"), + RecordData.from("weighted/lb-0--tenant1.app1.beta--prod.aws-us-east-1a/dns-zone-1/prod.aws-us-east-1a/1")), + new Record(Record.Type.ALIAS, + RecordName.from("b.app1.tenant1.a.vespa.oath.cloud"), + RecordData.from("weighted/lb-0--tenant1.app1.main--prod.aws-us-east-1a/dns-zone-1/prod.aws-us-east-1a/1")), + new Record(Record.Type.ALIAS, + RecordName.from("c.app1.tenant1.a.vespa.oath.cloud"), + RecordData.from("weighted/lb-0--tenant1.app1.beta--prod.aws-us-east-1b/dns-zone-1/prod.aws-us-east-1b/4")), + new Record(Record.Type.CNAME, + RecordName.from("d.app1.tenant1.a.vespa.oath.cloud"), + RecordData.from("vip.prod.us-west-1.")), + new Record(Record.Type.CNAME, + RecordName.from("e.app1.tenant1.a.vespa.oath.cloud"), + RecordData.from("vip.prod.us-east-3.")), + new Record(Record.Type.ALIAS, RecordName.from("a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"), RecordData.from("weighted/lb-0--tenant1.app1.beta--prod.aws-us-east-1a/dns-zone-1/prod.aws-us-east-1a/2")), new Record(Record.Type.ALIAS, RecordName.from("a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"), RecordData.from("weighted/lb-0--tenant1.app1.main--prod.aws-us-east-1a/dns-zone-1/prod.aws-us-east-1a/8")), new Record(Record.Type.ALIAS, - RecordName.from("a.app1.tenant1.aws-us-east-1b-r.vespa.oath.cloud"), + RecordName.from("a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"), RecordData.from("weighted/lb-0--tenant1.app1.main--prod.aws-us-east-1b/dns-zone-1/prod.aws-us-east-1b/1")), new Record(Record.Type.ALIAS, RecordName.from("b.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud"), @@ -732,12 +756,11 @@ public class ControllerTest { .scope(Endpoint.Scope.application) .sortedBy(comparing(Endpoint::dnsName)) .mapToList(Endpoint::dnsName); - assertEquals(List.of("a.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud", - "a.app1.tenant1.aws-us-east-1b-r.vespa.oath.cloud", - "b.app1.tenant1.aws-us-east-1a-r.vespa.oath.cloud", - "c.app1.tenant1.aws-us-east-1b-r.vespa.oath.cloud", - "d.app1.tenant1.us-west-1-r.vespa.oath.cloud", - "e.app1.tenant1.us-east-3-r.vespa.oath.cloud"), + assertEquals(List.of("a.app1.tenant1.a.vespa.oath.cloud", + "b.app1.tenant1.a.vespa.oath.cloud", + "c.app1.tenant1.a.vespa.oath.cloud", + "d.app1.tenant1.a.vespa.oath.cloud", + "e.app1.tenant1.a.vespa.oath.cloud"), endpointDnsNames); } 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 f3324e0c1f3..a76d2eca521 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 @@ -277,30 +277,65 @@ public class EndpointTest { } @Test + void application_endpoints_legacy_dns_names() { + Map<String, Endpoint> tests = Map.of( + "weighted.a1.t1.us-west-1.r.vespa-app.cloud", + Endpoint.of(app1) + .targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"), + Map.of(new DeploymentId(app1.instance("i1"), ZoneId.from("prod", "us-west-1")), 1)) + .routingMethod(RoutingMethod.exclusive) + .on(Port.tls()) + .in(SystemName.Public), + "weighted.a1.t1.us-west-1.r.cd.vespa-app.cloud", + Endpoint.of(app1) + .targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"), + Map.of(new DeploymentId(app1.instance("i1"), ZoneId.from("prod", "us-west-1")), 1)) + .routingMethod(RoutingMethod.exclusive) + .on(Port.tls()) + .in(SystemName.PublicCd), + "a2.t2.us-east-3-r.vespa.oath.cloud", + Endpoint.of(app2) + .targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"), + Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1)) + .routingMethod(RoutingMethod.exclusive) + .on(Port.tls()) + .in(SystemName.main), + "cd.a2.t2.us-east-3-r.vespa.oath.cloud", + Endpoint.of(app2) + .targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"), + Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1)) + .routingMethod(RoutingMethod.exclusive) + .on(Port.tls()) + .in(SystemName.cd) + ); + tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.legacyRegionalDnsName())); + } + + @Test void application_endpoints() { Map<String, Endpoint> tests = Map.of( - "https://weighted.a1.t1.us-west-1.r.vespa-app.cloud/", + "https://weighted.a1.t1.a.vespa-app.cloud/", Endpoint.of(app1) .targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"), 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.us-west-1.r.cd.vespa-app.cloud/", + "https://weighted.a1.t1.a.cd.vespa-app.cloud/", Endpoint.of(app1) .targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"), 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.us-east-3-r.vespa.oath.cloud/", + "https://a2.t2.a.vespa.oath.cloud/", Endpoint.of(app2) .targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"), 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.us-east-3-r.vespa.oath.cloud/", + "https://cd.a2.t2.a.vespa.oath.cloud/", Endpoint.of(app2) .targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"), Map.of(new DeploymentId(app2.instance("i1"), ZoneId.from("prod", "us-east-3")), 1)) diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java index b06b4eb0cfa..d79a81c7746 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificatesTest.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.controller.certificate; import com.yahoo.config.application.api.DeploymentSpec; import com.yahoo.config.application.api.xml.DeploymentSpecXmlReader; import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.CloudName; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.RegionName; @@ -25,6 +26,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCe import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.integration.SecretStoreMock; +import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.controller.persistence.CuratorDb; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -39,6 +41,8 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -136,9 +140,15 @@ public class EndpointCertificatesTest { assertEquals(expectedSans, endpointCertificateMetadata.get().requestedDnsSans()); } + private ControllerTester publicTester() { + ControllerTester publicTester = new ControllerTester(SystemName.Public); + publicTester.zoneRegistry().setZones(tester.zoneRegistry().zones().all().zones()); + return publicTester; + } + @Test void provisions_new_certificate_in_public_prod() { - ControllerTester tester = new ControllerTester(SystemName.Public); + ControllerTester tester = publicTester(); EndpointCertificateValidatorImpl endpointCertificateValidator = new EndpointCertificateValidatorImpl(secretStore, clock); EndpointCertificates endpointCertificates = new EndpointCertificates(tester.controller(), endpointCertificateMock, endpointCertificateValidator); List<String> expectedSans = List.of( @@ -238,6 +248,9 @@ public class EndpointCertificatesTest { Instance instance = new Instance(ApplicationId.from("t1", "a1", "default"), Tags.empty()); ZoneId zone1 = ZoneId.from(Environment.prod, RegionName.from("aws-us-east-1c")); ZoneId zone2 = ZoneId.from(Environment.prod, RegionName.from("aws-us-west-2a")); + ControllerTester tester = publicTester(); + tester.zoneRegistry().addZones(ZoneApiMock.newBuilder().with(CloudName.DEFAULT).with(zone1).build(), + ZoneApiMock.newBuilder().with(CloudName.AWS).with(zone2).build()); ApplicationPackage applicationPackage = new ApplicationPackageBuilder() .instances("beta,main") .region(zone1.region()) @@ -251,16 +264,17 @@ public class EndpointCertificatesTest { InstanceName.from("main"), 6), zone2.region().value(), Map.of(InstanceName.from("main"), 2))) .build(); - ControllerTester tester = new ControllerTester(SystemName.Public); EndpointCertificateValidatorImpl endpointCertificateValidator = new EndpointCertificateValidatorImpl(secretStore, clock); EndpointCertificates endpointCertificates = new EndpointCertificates(tester.controller(), endpointCertificateMock, endpointCertificateValidator); - List<String> expectedSans = List.of( + List<String> expectedSans = Stream.of( "vlfms2wpoa4nyrka2s5lktucypjtxkqhv.internal.vespa-app.cloud", "a1.t1.g.vespa-app.cloud", "*.a1.t1.g.vespa-app.cloud", + "a1.t1.a.vespa-app.cloud", "a1.t1.aws-us-west-2a.r.vespa-app.cloud", - "*.a1.t1.aws-us-west-2a.r.vespa-app.cloud", "a1.t1.aws-us-east-1c.r.vespa-app.cloud", + "*.a1.t1.a.vespa-app.cloud", + "*.a1.t1.aws-us-west-2a.r.vespa-app.cloud", "*.a1.t1.aws-us-east-1c.r.vespa-app.cloud", "a1.t1.aws-us-east-1c.z.vespa-app.cloud", "*.a1.t1.aws-us-east-1c.z.vespa-app.cloud", @@ -268,13 +282,13 @@ public class EndpointCertificatesTest { "*.a1.t1.us-east-1.test.z.vespa-app.cloud", "a1.t1.us-east-3.staging.z.vespa-app.cloud", "*.a1.t1.us-east-3.staging.z.vespa-app.cloud" - ); + ).sorted().toList(); Optional<EndpointCertificateMetadata> endpointCertificateMetadata = endpointCertificates.getMetadata(instance, zone1, applicationPackage.deploymentSpec()); assertTrue(endpointCertificateMetadata.isPresent()); assertTrue(endpointCertificateMetadata.get().keyName().matches("vespa.tls.t1.a1.*-key")); assertTrue(endpointCertificateMetadata.get().certName().matches("vespa.tls.t1.a1.*-cert")); assertEquals(0, endpointCertificateMetadata.get().version()); - assertEquals(expectedSans, endpointCertificateMetadata.get().requestedDnsSans()); + assertEquals(expectedSans, endpointCertificateMetadata.get().requestedDnsSans().stream().sorted().toList()); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java index 6493eafcde5..831a79f24b8 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/TestConfigSerializerTest.java @@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.controller.deployment; import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.CloudName; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.zone.ZoneId; @@ -31,22 +32,23 @@ public class TestConfigSerializerTest { @Test void testConfig() throws IOException { ZoneId zone = DeploymentContext.systemTest.zone(); - byte[] json = new TestConfigSerializer(SystemName.PublicCd).configJson(instanceId, - DeploymentContext.systemTest, - true, - Version.fromString("1.2.3"), - RevisionId.forProduction(321), - Instant.ofEpochMilli(222), - Map.of(zone, List.of(Endpoint.of(ApplicationId.defaultId()) - .target(EndpointId.of("ai"), ClusterSpec.Id.from("qrs"), - List.of(new DeploymentId(ApplicationId.defaultId(), - ZoneId.defaultId()))) - .on(Endpoint.Port.tls()) - .in(SystemName.main))), - Map.of(zone, List.of("facts"))); + byte[] json = new TestConfigSerializer(SystemName.PublicCd) + .configJson(instanceId, + DeploymentContext.systemTest, + true, + Version.fromString("1.2.3"), + RevisionId.forProduction(321), + Instant.ofEpochMilli(222), + Map.of(zone, List.of(Endpoint.of(ApplicationId.defaultId()) + .target(EndpointId.of("ai"), ClusterSpec.Id.from("qrs"), + List.of(new DeploymentId(ApplicationId.defaultId(), + ZoneId.defaultId()))) + .on(Endpoint.Port.tls()) + .in(SystemName.main))), + Map.of(zone, List.of("facts"))); byte[] expected = Files.readAllBytes(Paths.get("src/test/resources/testConfig.json")); assertEquals(new String(SlimeUtils.toJsonBytes(SlimeUtils.jsonToSlime(expected))), - new String(json)); + new String(json)); } } 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 0f03333146f..d6da677cb27 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 @@ -114,10 +114,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class ApplicationApiTest extends ControllerContainerTest { private static final String responseFiles = "src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/"; - private static final String pemPublicKey = "-----BEGIN PUBLIC KEY-----\n" + - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuKVFA8dXk43kVfYKzkUqhEY2rDT9\n" + - "z/4jKSTHwbYR8wdsOSrJGVEUPbS2nguIJ64OJH7gFnxM6sxUVj+Nm2HlXw==\n" + - "-----END PUBLIC KEY-----\n"; + private static final String pemPublicKey = """ + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuKVFA8dXk43kVfYKzkUqhEY2rDT9 + z/4jKSTHwbYR8wdsOSrJGVEUPbS2nguIJ64OJH7gFnxM6sxUVj+Nm2HlXw== + -----END PUBLIC KEY----- + """; private static final String quotedPemPublicKey = pemPublicKey.replaceAll("\\n", "\\\\n"); private static final String accessDenied = "{\n \"code\" : 403,\n \"message\" : \"Access denied\"\n}"; diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json index 37bd69d5863..cc42b3e006c 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json @@ -24,7 +24,7 @@ { "cluster": "foo", "tls": true, - "url": "https://a0.application1.tenant1.us-central-1-r.vespa.oath.cloud/", + "url": "https://a0.application1.tenant1.a.vespa.oath.cloud/", "scope": "application", "routingMethod": "sharedLayer4", "legacy": false diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json index d78ab67dcc5..f37112ea887 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/instance1-recursive.json @@ -111,7 +111,7 @@ { "cluster": "foo", "tls": true, - "url": "https://a0.application1.tenant1.us-central-1-r.vespa.oath.cloud/", + "url": "https://a0.application1.tenant1.a.vespa.oath.cloud/", "scope": "application", "routingMethod": "sharedLayer4", "legacy": false diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json index 8d76d54458d..4458040858b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/recursive-root.json @@ -118,7 +118,7 @@ { "cluster": "foo", "tls": true, - "url": "https://a0.application1.tenant1.us-central-1-r.vespa.oath.cloud/", + "url": "https://a0.application1.tenant1.a.vespa.oath.cloud/", "scope": "application", "routingMethod": "sharedLayer4", "legacy": false diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1-recursive.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1-recursive.json index b9bf714d362..ea025b60d1b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1-recursive.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/tenant1-recursive.json @@ -117,7 +117,7 @@ { "cluster": "foo", "tls": true, - "url": "https://a0.application1.tenant1.us-central-1-r.vespa.oath.cloud/", + "url": "https://a0.application1.tenant1.a.vespa.oath.cloud/", "scope": "application", "routingMethod": "sharedLayer4", "legacy": false |