diff options
author | Martin Polden <mpolden@mpolden.no> | 2021-05-10 09:23:20 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-10 09:23:20 +0200 |
commit | ef7e0dec2c3fb3e8390bdb7a278bf7ad89bf549f (patch) | |
tree | b1f758a27eb1d3a024a6b60491eeb8c4aa38d734 /controller-server | |
parent | 82f6ea0f659f75171992a53b3608705e1a7c2234 (diff) | |
parent | 1e9a37784e2150a1ccec021f14708b7b592e7de4 (diff) |
Merge pull request #17776 from vespa-engine/mpolden/new-endpoints
Register new endpoints in DNS
Diffstat (limited to 'controller-server')
21 files changed, 380 insertions, 138 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 52ad813d7cc..433b2b340d5 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 @@ -96,7 +96,7 @@ public class RoutingController { 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.add(policy.regionEndpointIn(controller.system(), routingMethod)); + endpoints.addAll(policy.regionEndpointsIn(controller.system(), routingMethod)); } } return EndpointList.copyOf(endpoints); @@ -315,6 +315,14 @@ public class RoutingController { .on(Port.fromRoutingMethod(method)) .routingMethod(method) .in(controller.system())); + if (controller.system().isPublic()) { + endpoints.add(Endpoint.of(routingId.application()) + .target(routingId.endpointId(), cluster, zones) + .on(Port.fromRoutingMethod(method)) + .routingMethod(method) + .legacy() + .in(controller.system())); + } // Add legacy endpoints if (legacyNamesAvailable && method == RoutingMethod.shared) { endpoints.add(Endpoint.of(routingId.application()) 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 cc1a0a455c4..3f079a5fb9b 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 @@ -28,6 +28,10 @@ public class Endpoint { private static final String OATH_DNS_SUFFIX = ".vespa.oath.cloud"; private static final String PUBLIC_DNS_SUFFIX = ".public.vespa.oath.cloud"; private static final String PUBLIC_CD_DNS_SUFFIX = ".public-cd.vespa.oath.cloud"; + // TODO(mpolden): New domain is considered "legacy" for the time being, until it's ready for use. Once it's ready + // we'll make the vespa.oath.cloud variant legacy and this non-legacy. + private static final String PUBLIC_DNS_LEGACY_SUFFIX = ".vespa-app.cloud"; + private static final String PUBLIC_CD_LEGACY_DNS_SUFFIX = ".cd.vespa-app.cloud"; private final EndpointId id; private final ClusterSpec.Id cluster; @@ -173,13 +177,13 @@ public class Endpoint { String portPart = port.isDefault() ? "" : ":" + port.port; return URI.create(scheme + "://" + sanitize(namePart(name, separator)) + - systemPart(system, separator) + + systemPart(system, separator, legacy) + sanitize(instancePart(application, separator)) + sanitize(application.application().value()) + separator + sanitize(application.tenant().value()) + "." + - scopePart(scope, zones, legacy) + + scopePart(scope, zones, legacy, system) + dnsSuffix(system, legacy) + portPart + "/"); @@ -201,7 +205,15 @@ public class Endpoint { return name + separator; } - private static String scopePart(Scope scope, List<ZoneId> zones, boolean legacy) { + private static String scopePart(Scope scope, List<ZoneId> zones, boolean legacy, SystemName system) { + if (system.isPublic() && legacy) { + if (scope == Scope.global) return "g"; + var zone = zones.get(0); + var region = zone.region().value(); + char scopeSymbol = scope == Scope.region ? 'r' : 'z'; + String environment = zone.environment().isProduction() ? "" : "." + zone.environment().value(); + return region + environment + "." + scopeSymbol; + } if (scope == Scope.global) return "global"; var zone = zones.get(0); var region = zone.region().value(); @@ -215,8 +227,9 @@ public class Endpoint { return application.instance().value() + separator; } - private static String systemPart(SystemName system, String separator) { + private static String systemPart(SystemName system, String separator, boolean legacy) { if (!system.isCd()) return ""; + if (system.isPublic() && legacy) return ""; return system.value() + separator; } @@ -227,8 +240,10 @@ public class Endpoint { if (legacy) return YAHOO_DNS_SUFFIX; return OATH_DNS_SUFFIX; case Public: + if (legacy) return PUBLIC_DNS_LEGACY_SUFFIX; return PUBLIC_DNS_SUFFIX; case PublicCd: + if (legacy) return PUBLIC_CD_LEGACY_DNS_SUFFIX; return PUBLIC_CD_DNS_SUFFIX; default: throw new IllegalArgumentException("No DNS suffix declared for system " + system); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java index 015da1faae8..0491cf61ef3 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java @@ -61,7 +61,7 @@ public class ControllerMaintenance extends AbstractComponent { maintainers.add(new SystemRoutingPolicyMaintainer(controller, intervals.systemRoutingPolicyMaintainer)); maintainers.add(new ApplicationMetaDataGarbageCollector(controller, intervals.applicationMetaDataGarbageCollector)); maintainers.add(new ContainerImageExpirer(controller, intervals.containerImageExpirer)); - maintainers.add(new HostInfoUpdater(controller, intervals.hostSwitchUpdater)); + maintainers.add(new HostInfoUpdater(controller, intervals.hostInfoUpdater)); maintainers.add(new ReindexingTriggerer(controller, intervals.reindexingTriggerer)); maintainers.add(new EndpointCertificateMaintainer(controller, intervals.endpointCertificateMaintainer)); maintainers.add(new TrafficShareUpdater(controller, intervals.trafficFractionUpdater)); @@ -116,7 +116,7 @@ public class ControllerMaintenance extends AbstractComponent { private final Duration systemRoutingPolicyMaintainer; private final Duration applicationMetaDataGarbageCollector; private final Duration containerImageExpirer; - private final Duration hostSwitchUpdater; + private final Duration hostInfoUpdater; private final Duration reindexingTriggerer; private final Duration endpointCertificateMaintainer; private final Duration trafficFractionUpdater; @@ -148,7 +148,7 @@ public class ControllerMaintenance extends AbstractComponent { this.systemRoutingPolicyMaintainer = duration(10, MINUTES); this.applicationMetaDataGarbageCollector = duration(12, HOURS); this.containerImageExpirer = duration(12, HOURS); - this.hostSwitchUpdater = duration(12, HOURS); + this.hostInfoUpdater = duration(12, HOURS); this.reindexingTriggerer = duration(1, HOURS); this.endpointCertificateMaintainer = duration(12, HOURS); this.trafficFractionUpdater = duration(5, MINUTES); 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 2a7132c08d6..ae4d891069c 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 @@ -1308,6 +1308,7 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { object.setString("url", endpoint.url().toString()); object.setString("scope", endpointScopeString(endpoint.scope())); object.setString("routingMethod", routingMethodString(endpoint.routingMethod())); + object.setBool("legacy", endpoint.legacy()); } private void toSlime(Cursor response, DeploymentId deploymentId, Deployment deployment, HttpRequest request) { @@ -1319,17 +1320,22 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler { var application = controller.applications().requireApplication(TenantAndApplicationId.from(deploymentId.applicationId())); // Add zone endpoints + boolean legacyEndpoints = request.getBooleanProperty("includeLegacyEndpoints"); var endpointArray = response.setArray("endpoints"); EndpointList zoneEndpoints = controller.routing().endpointsOf(deploymentId) - .scope(Endpoint.Scope.zone) - .not().legacy(); + .scope(Endpoint.Scope.zone); + if (!legacyEndpoints) { + zoneEndpoints = zoneEndpoints.not().legacy(); + } for (var endpoint : controller.routing().directEndpoints(zoneEndpoints, deploymentId.applicationId())) { toSlime(endpoint, endpointArray.addObject()); } // Add global endpoints EndpointList globalEndpoints = controller.routing().endpointsOf(application, deploymentId.applicationId().instance()) - .not().legacy() .targets(deploymentId.zoneId()); + if (!legacyEndpoints) { + globalEndpoints = globalEndpoints.not().legacy(); + } for (var endpoint : controller.routing().directEndpoints(globalEndpoints, deploymentId.applicationId())) { toSlime(endpoint, endpointArray.addObject()); } 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 0d1469a61fc..e2a8be15361 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 @@ -119,54 +119,63 @@ public class RoutingPolicies { } } - /** Update global DNS record for given policies */ + /** Update global DNS records for given policies */ private void updateGlobalDnsOf(Collection<RoutingPolicy> routingPolicies, Set<ZoneId> inactiveZones, @SuppressWarnings("unused") Lock lock) { Map<RoutingId, List<RoutingPolicy>> routingTable = routingTableFrom(routingPolicies); for (Map.Entry<RoutingId, List<RoutingPolicy>> routeEntry : routingTable.entrySet()) { - Collection<RegionEndpoint> regionEndpoints = computeRegionEndpoints(routeEntry.getValue(), inactiveZones); - // Create a weighted ALIAS per region, pointing to all zones within the same region - regionEndpoints.forEach(regionEndpoint -> { - controller.nameServiceForwarder().createAlias(RecordName.from(regionEndpoint.target().name().value()), - Collections.unmodifiableSet(regionEndpoint.zoneTargets()), - Priority.normal); - }); - - // Create global latency-based ALIAS pointing to each per-region weighted ALIAS - Set<AliasTarget> latencyTargets = new LinkedHashSet<>(); - Set<AliasTarget> inactiveLatencyTargets = new LinkedHashSet<>(); - for (var regionEndpoint : regionEndpoints) { - if (regionEndpoint.active()) { - latencyTargets.add(regionEndpoint.target()); - } else { - inactiveLatencyTargets.add(regionEndpoint.target()); - } - } - // If all targets are configured out, all targets are set in. We do this because otherwise removing 100% of - // the ALIAS records would cause the global endpoint to stop resolving entirely (NXDOMAIN). - if (latencyTargets.isEmpty() && !inactiveLatencyTargets.isEmpty()) { - latencyTargets.addAll(inactiveLatencyTargets); - inactiveLatencyTargets.clear(); + RoutingId routingId = routeEntry.getKey(); + controller.routing().endpointsOf(routingId.application()) + .named(routingId.endpointId()) + .not().requiresRotation() + .forEach(endpoint -> updateGlobalDnsOf(endpoint, inactiveZones, routeEntry.getValue())); + } + } + + /** Update global DNS records for given global endpoint */ + 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()); + regionEndpoints.forEach(regionEndpoint -> { + controller.nameServiceForwarder().createAlias(RecordName.from(regionEndpoint.target().name().value()), + Collections.unmodifiableSet(regionEndpoint.zoneTargets()), + Priority.normal); + }); + + // Create global latency-based ALIAS pointing to each per-region weighted ALIAS + Set<AliasTarget> latencyTargets = new LinkedHashSet<>(); + Set<AliasTarget> inactiveLatencyTargets = new LinkedHashSet<>(); + for (var regionEndpoint : regionEndpoints) { + if (regionEndpoint.active()) { + latencyTargets.add(regionEndpoint.target()); + } else { + inactiveLatencyTargets.add(regionEndpoint.target()); } - var endpoints = controller.routing().endpointsOf(routeEntry.getKey().application()) - .named(routeEntry.getKey().endpointId()) - .not().requiresRotation(); - endpoints.forEach(endpoint -> controller.nameServiceForwarder().createAlias(RecordName.from(endpoint.dnsName()), - latencyTargets, Priority.normal)); - inactiveLatencyTargets.forEach(t -> controller.nameServiceForwarder() - .removeRecords(Record.Type.ALIAS, - RecordData.fqdn(t.name().value()), - Priority.normal)); } + + // If all targets are configured OUT, all targets are kept IN. We do this because otherwise removing 100% of + // the ALIAS records would cause the global endpoint to stop resolving entirely (NXDOMAIN). + if (latencyTargets.isEmpty() && !inactiveLatencyTargets.isEmpty()) { + latencyTargets.addAll(inactiveLatencyTargets); + inactiveLatencyTargets.clear(); + } + + controller.nameServiceForwarder().createAlias(RecordName.from(endpoint.dnsName()), latencyTargets, Priority.normal); + inactiveLatencyTargets.forEach(t -> controller.nameServiceForwarder() + .removeRecords(Record.Type.ALIAS, + RecordData.fqdn(t.name().value()), + Priority.normal)); } + /** Compute region endpoints and their targets from given policies */ - private Collection<RegionEndpoint> computeRegionEndpoints(List<RoutingPolicy> policies, Set<ZoneId> inactiveZones) { + private Collection<RegionEndpoint> computeRegionEndpoints(List<RoutingPolicy> policies, Set<ZoneId> inactiveZones, boolean legacy) { 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); + Endpoint regionEndpoint = policy.regionEndpointIn(controller.system(), routingMethod, legacy); var zonePolicy = db.readZoneRoutingPolicy(policy.id().zone()); long weight = 1; if (isConfiguredOut(policy, zonePolicy, inactiveZones)) { 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 3ece10337f1..ae33d214ecc 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 @@ -12,6 +12,7 @@ import com.yahoo.vespa.hosted.controller.application.EndpointId; import com.yahoo.vespa.hosted.controller.application.SystemApplication; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -75,29 +76,45 @@ public class RoutingPolicy { public List<Endpoint> endpointsIn(SystemName system, RoutingMethod routingMethod, ZoneRegistry zoneRegistry) { Optional<Endpoint> infraEndpoint = SystemApplication.matching(id.owner()) .flatMap(app -> app.endpointIn(id.zone(), zoneRegistry)); - List<Endpoint> endpoints = new ArrayList<>(3); if (infraEndpoint.isPresent()) { - endpoints.add(infraEndpoint.get()); - } else { - endpoints.add(endpoint(routingMethod).target(id.cluster(), id.zone()).in(system)); - // Add legacy endpoints - if (routingMethod == RoutingMethod.shared) { - endpoints.add(endpoint(routingMethod).target(id.cluster(), id.zone()) - .on(Port.plain(4080)) - .legacy() - .in(system)); - endpoints.add(endpoint(routingMethod).target(id.cluster(), id.zone()) - .on(Port.tls(4443)) - .legacy() - .in(system)); - } + return List.of(infraEndpoint.get()); + } + List<Endpoint> endpoints = new ArrayList<>(3); + endpoints.add(endpoint(routingMethod).target(id.cluster(), id.zone()).in(system)); + if (system.isPublic()) { + endpoints.add(endpoint(routingMethod).target(id.cluster(), id.zone()).legacy().in(system)); + } + // Add legacy endpoints + if (routingMethod == RoutingMethod.shared) { + endpoints.add(endpoint(routingMethod).target(id.cluster(), id.zone()) + .on(Port.plain(4080)) + .legacy() + .in(system)); + endpoints.add(endpoint(routingMethod).target(id.cluster(), id.zone()) + .on(Port.tls(4443)) + .legacy() + .in(system)); } return endpoints; } + /** Returns all region endpoints of this */ + public List<Endpoint> regionEndpointsIn(SystemName system, RoutingMethod routingMethod) { + List<Endpoint> endpoints = new ArrayList<>(2); + endpoints.add(regionEndpointIn(system, routingMethod, false)); + if (system.isPublic()) { + endpoints.add(regionEndpointIn(system, routingMethod, true)); + } + return Collections.unmodifiableList(endpoints); + } + /** 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, boolean legacy) { + Endpoint.EndpointBuilder endpoint = endpoint(routingMethod).targetRegion(id.cluster(), id.zone()); + if (legacy) { + endpoint = endpoint.legacy(); + } + return endpoint.in(system); } @Override 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 422c856ca01..e52d1900a9d 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 @@ -599,7 +599,7 @@ public class ControllerTest { var context = tester.newDeploymentContext(); ZoneId zone = ZoneId.from("dev", "us-east-1"); tester.controllerTester().zoneRegistry() - .setRoutingMethod(ZoneApiMock.from(zone), RoutingMethod.shared, RoutingMethod.sharedLayer4); + .setRoutingMethod(ZoneApiMock.from(zone), RoutingMethod.shared, RoutingMethod.sharedLayer4); // Deploy context.runJob(zone, applicationPackage); @@ -834,7 +834,7 @@ public class ControllerTest { @Test public void testDeploymentDirectRouting() { // Rotation-less system - DeploymentTester tester = new DeploymentTester(new ControllerTester(new RotationsConfig.Builder().build())); + DeploymentTester tester = new DeploymentTester(new ControllerTester(new RotationsConfig.Builder().build(), main)); var context = tester.newDeploymentContext(); var zone1 = ZoneId.from("prod", "us-west-1"); var zone2 = ZoneId.from("prod", "us-east-3"); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java index 3e25a09b7d7..006e4e63136 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java @@ -8,6 +8,7 @@ import com.yahoo.config.provision.HostName; import com.yahoo.config.provision.RegionName; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.TenantName; +import com.yahoo.config.provision.zone.RoutingMethod; import com.yahoo.config.provision.zone.ZoneApi; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.test.ManualClock; @@ -38,6 +39,7 @@ import com.yahoo.vespa.hosted.controller.integration.ConfigServerMock; import com.yahoo.vespa.hosted.controller.integration.MetricsMock; import com.yahoo.vespa.hosted.controller.integration.SecretStoreMock; import com.yahoo.vespa.hosted.controller.integration.ServiceRegistryMock; +import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.controller.integration.ZoneRegistryMock; import com.yahoo.vespa.hosted.controller.persistence.CuratorDb; import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb; @@ -68,6 +70,7 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import java.util.logging.Handler; import java.util.logging.Logger; +import java.util.stream.Collectors; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -106,8 +109,8 @@ public final class ControllerTester { this(new AthenzDbMock(), new MockCuratorDb(), defaultRotationsConfig(), serviceRegistryMock); } - public ControllerTester(RotationsConfig rotationsConfig) { - this(rotationsConfig, new MockCuratorDb()); + public ControllerTester(RotationsConfig rotationsConfig, SystemName system) { + this(new AthenzDbMock(), new MockCuratorDb(), rotationsConfig, new ServiceRegistryMock(system)); } public ControllerTester(MockCuratorDb curatorDb) { @@ -197,6 +200,21 @@ public final class ControllerTester { return new Version(current.getMajor(), nextMinorVersion.getAndIncrement(), current.getMicro()); } + /** Set the zones and system for this and bootstrap infrastructure nodes */ + public ControllerTester setZones(List<ZoneId> zones, SystemName system) { + zoneRegistry().setZones(zones.stream().map(ZoneApiMock::from).collect(Collectors.toList())) + .setSystemName(system); + configServer().bootstrap(zones, SystemApplication.notController()); + return this; + } + + /** Set the routing method for given zones */ + public ControllerTester setRoutingMethod(List<ZoneId> zones, RoutingMethod routingMethod) { + zoneRegistry().setRoutingMethod(zones.stream().map(ZoneApiMock::from).collect(Collectors.toList()), + routingMethod); + return this; + } + /** Create a new controller instance. Useful to verify that controller state is rebuilt from persistence */ public final void createNewController() { if (inContainer) 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 2d81d7304a1..468c92d3539 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 @@ -69,6 +69,21 @@ public class EndpointTest { Endpoint.of(app1).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public) ); tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString())); + + Map<String, Endpoint> tests2 = Map.of( + // Default endpoint in public system using new domain + "https://a1.t1.g.vespa-app.cloud/", + Endpoint.of(app1).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).legacy().in(SystemName.Public), + + // Default endpoint in public CD system using new domain + "https://a1.t1.g.cd.vespa-app.cloud/", + Endpoint.of(app1).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).legacy().in(SystemName.PublicCd), + + // Custom instance in public system, using new domain + "https://i2.a2.t2.g.vespa-app.cloud/", + Endpoint.of(app2).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).legacy().in(SystemName.Public) + ); + tests2.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString())); } @Test @@ -117,6 +132,13 @@ public class EndpointTest { Endpoint.of(app1).target(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public) ); tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString())); + + Map<String, Endpoint> tests2 = Map.of( + // Custom endpoint and instance in public system, using new domain + "https://foo.i2.a2.t2.g.vespa-app.cloud/", + Endpoint.of(app2).target(EndpointId.of("foo")).on(Port.tls()).routingMethod(RoutingMethod.exclusive).legacy().in(SystemName.Public) + ); + tests2.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString())); } @Test @@ -167,6 +189,21 @@ public class EndpointTest { Endpoint.of(app1).target(cluster, prodZone).on(Port.tls()).routingMethod(RoutingMethod.sharedLayer4).in(SystemName.main) ); tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString())); + + Map<String, Endpoint> tests2 = Map.of( + // Custom cluster name in public, using new domain + "https://c1.a1.t1.us-north-1.z.vespa-app.cloud/", + Endpoint.of(app1).target(ClusterSpec.Id.from("c1"), prodZone).on(Port.tls()).routingMethod(RoutingMethod.exclusive).legacy().in(SystemName.Public), + + // Default cluster name in non-production zone in public, using new domain + "https://a1.t1.us-north-2.test.z.vespa-app.cloud/", + Endpoint.of(app1).target(ClusterSpec.Id.from("default"), testZone).on(Port.tls()).routingMethod(RoutingMethod.exclusive).legacy().in(SystemName.Public), + + // Default cluster name in public CD, using new domain + "https://a1.t1.us-north-1.z.cd.vespa-app.cloud/", + Endpoint.of(app1).target(ClusterSpec.Id.from("default"), prodZone).on(Port.tls()).routingMethod(RoutingMethod.exclusive).legacy().in(SystemName.PublicCd) + ); + tests2.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString())); } @Test @@ -229,8 +266,9 @@ public class EndpointTest { } @Test - public void weighted_endpoints() { + public void region_endpoints() { var cluster = ClusterSpec.Id.from("default"); + var prodZone = ZoneId.from("prod", "us-north-2"); Map<String, Endpoint> tests = Map.of( "https://a1.t1.us-north-1-w.public.vespa.oath.cloud/", Endpoint.of(app1) @@ -240,7 +278,7 @@ public class EndpointTest { .in(SystemName.Public), "https://a1.t1.us-north-2-w.public.vespa.oath.cloud/", Endpoint.of(app1) - .targetRegion(cluster, ZoneId.from("prod", "us-north-2")) + .targetRegion(cluster, prodZone) .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) .in(SystemName.Public), @@ -249,6 +287,13 @@ public class EndpointTest { .targetRegion(cluster, ZoneId.from("test", "us-north-2")) .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) + .in(SystemName.Public), + "https://c1.a1.t1.us-north-2.r.vespa-app.cloud/", + Endpoint.of(app1) + .targetRegion(ClusterSpec.Id.from("c1"), prodZone) + .routingMethod(RoutingMethod.exclusive) + .on(Port.tls()) + .legacy() .in(SystemName.Public) ); tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString())); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ApplicationPackageBuilder.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ApplicationPackageBuilder.java index 9eaa15cdbe3..fc7a99eb2f0 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ApplicationPackageBuilder.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/ApplicationPackageBuilder.java @@ -186,27 +186,28 @@ public class ApplicationPackageBuilder { return this; } + /** Add a trusted certificate to security/clients.pem */ public ApplicationPackageBuilder trust(X509Certificate certificate) { this.trustedCertificates.add(certificate); return this; } + /** Add a default trusted certificate to security/clients.pem */ public ApplicationPackageBuilder trustDefaultCertificate() { try { var generator = KeyPairGenerator.getInstance("RSA"); - var builder = X509CertificateBuilder.fromKeypair( + var certificate = X509CertificateBuilder.fromKeypair( generator.generateKeyPair(), new X500Principal("CN=name"), Instant.now(), Instant.now().plusMillis(300_000), SignatureAlgorithm.SHA256_WITH_RSA, X509CertificateBuilder.generateRandomSerialNumber() - ); - this.trustedCertificates.add(builder.build()); + ).build(); + return trust(certificate); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } - return this; } private byte[] deploymentSpec() { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java index 81c9f51278e..c8b4eaa5236 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java @@ -5,15 +5,11 @@ import com.yahoo.component.Version; import com.yahoo.config.provision.SystemName; 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.api.integration.deployment.ApplicationVersion; 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.application.ApplicationPackage; import com.yahoo.vespa.hosted.controller.application.Change; -import com.yahoo.vespa.hosted.controller.application.SystemApplication; -import com.yahoo.vespa.hosted.controller.integration.ServiceRegistryMock; -import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock; import com.yahoo.vespa.hosted.controller.versions.VespaVersion; import org.junit.Test; @@ -1061,17 +1057,13 @@ public class DeploymentTriggerTest { ApplicationPackage cdPackage = new ApplicationPackageBuilder().region("cd-us-central-1") .region("cd-aws-us-east-1a") .build(); - ServiceRegistryMock services = new ServiceRegistryMock(); - var zones = List.of(ZoneApiMock.fromId("test.cd-us-central-1"), - ZoneApiMock.fromId("staging.cd-us-central-1"), - ZoneApiMock.fromId("prod.cd-us-central-1"), - ZoneApiMock.fromId("prod.cd-aws-us-east-1a")); - services.zoneRegistry() - .setSystemName(SystemName.cd) - .setZones(zones) - .setRoutingMethod(zones, RoutingMethod.shared); - tester = new DeploymentTester(new ControllerTester(services)); - tester.configServer().bootstrap(services.zoneRegistry().zones().all().ids(), SystemApplication.values()); + var zones = List.of(ZoneId.from("test.cd-us-central-1"), + ZoneId.from("staging.cd-us-central-1"), + ZoneId.from("prod.cd-us-central-1"), + ZoneId.from("prod.cd-aws-us-east-1a")); + tester.controllerTester() + .setZones(zones, SystemName.cd) + .setRoutingMethod(zones, RoutingMethod.shared); tester.controllerTester().upgradeSystem(Version.fromString("6.1")); tester.controllerTester().computeVersionStatus(); var app = tester.newDeploymentContext(); 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 31a3b5ff1cf..10e398ad133 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 @@ -1472,6 +1472,12 @@ public class ApplicationApiTest extends ControllerContainerTest { .userIdentity(USER_ID), new File("deployment-with-routing-policy.json")); + // GET deployment including legacy endpoints + tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/environment/prod/region/us-west-1/instance/instance1", GET) + .userIdentity(USER_ID) + .properties(Map.of("includeLegacyEndpoints", "true")), + new File("deployment-with-routing-policy-legacy.json")); + // Hide shared endpoints ((InMemoryFlagSource) tester.controller().flagSource()).withBooleanFlag(Flags.HIDE_SHARED_ROUTING_ENDPOINT.id(), true); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-cloud.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-cloud.json index 59022f124c0..3353d80204e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-cloud.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-cloud.json @@ -10,7 +10,8 @@ "tls": true, "url": "https://albums.scoober.aws-us-east-1c.public.vespa.oath.cloud/", "scope": "zone", - "routingMethod": "exclusive" + "routingMethod": "exclusive", + "legacy": false } ], "clusters": "http://localhost:8080/application/v4/tenant/scoober/application/albums/instance/default/environment/prod/region/aws-us-east-1c/clusters", @@ -44,4 +45,4 @@ "queryLatencyMillis": 0.0, "writeLatencyMillis": 0.0 } -}
\ No newline at end of file +} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy-legacy.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy-legacy.json new file mode 100644 index 00000000000..6efcc822264 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy-legacy.json @@ -0,0 +1,71 @@ +{ + "tenant": "tenant1", + "application": "application1", + "instance": "instance1", + "environment": "prod", + "region": "us-west-1", + "endpoints": [ + { + "cluster": "default", + "tls": true, + "url": "https://instance1.application1.tenant1.us-west-1.vespa.oath.cloud/", + "scope": "zone", + "routingMethod": "exclusive", + "legacy": false + }, + { + "cluster": "default", + "tls": true, + "url": "https://instance1--application1--tenant1.us-west-1.vespa.oath.cloud:4443/", + "scope": "zone", + "routingMethod": "shared", + "legacy": false + }, + { + "cluster": "default", + "tls": false, + "url": "http://instance1.application1.tenant1.us-west-1.prod.vespa.yahooapis.com:4080/", + "scope": "zone", + "routingMethod": "shared", + "legacy": true + }, + { + "cluster": "default", + "tls": true, + "url": "https://instance1--application1--tenant1.us-west-1.prod.vespa.yahooapis.com:4443/", + "scope": "zone", + "routingMethod": "shared", + "legacy": true + } + ], + "clusters": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-west-1/clusters", + "nodes": "http://localhost:8080/zone/v2/prod/us-west-1/nodes/v2/node/?recursive=true&application=tenant1.application1.instance1", + "yamasUrl": "http://monitoring-system.test/?environment=prod®ion=us-west-1&application=tenant1.application1.instance1", + "version": "6.1.0", + "revision": "1.0.1-commit1", + "deployTimeEpochMs": "(ignore)", + "screwdriverId": "1000", + "gitRepository": "repository1", + "gitBranch": "master", + "gitCommit": "commit1", + "applicationVersion": { + "hash": "1.0.1-commit1", + "build": 1, + "source": { + "gitRepository": "repository1", + "gitBranch": "master", + "gitCommit": "commit1" + }, + "sourceUrl": "repository1/tree/commit1", + "commit": "commit1" + }, + "status": "complete", + "activity": {}, + "metrics": { + "queriesPerSecond": 0.0, + "writesPerSecond": 0.0, + "documentCount": 0.0, + "queryLatencyMillis": 0.0, + "writeLatencyMillis": 0.0 + } +} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy.json index 6b3c316a485..8767c369bc3 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-with-routing-policy.json @@ -10,14 +10,16 @@ "tls": true, "url": "https://instance1.application1.tenant1.us-west-1.vespa.oath.cloud/", "scope": "zone", - "routingMethod": "exclusive" + "routingMethod": "exclusive", + "legacy": false }, { "cluster": "default", "tls": true, "url": "https://instance1--application1--tenant1.us-west-1.vespa.oath.cloud:4443/", "scope": "zone", - "routingMethod": "shared" + "routingMethod": "shared", + "legacy": false } ], "clusters":"http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-west-1/clusters", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-without-shared-endpoints.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-without-shared-endpoints.json index 66fe28a95ad..b59c1d6cf73 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-without-shared-endpoints.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment-without-shared-endpoints.json @@ -10,7 +10,8 @@ "tls": true, "url": "https://instance1.application1.tenant1.us-west-1.vespa.oath.cloud/", "scope": "zone", - "routingMethod": "exclusive" + "routingMethod": "exclusive", + "legacy": false } ], "clusters":"http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-west-1/clusters", 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 946593fca00..6c00d654008 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 @@ -10,14 +10,16 @@ "tls": true, "url": "https://instance1--application1--tenant1.us-central-1.vespa.oath.cloud:4443/", "scope": "zone", - "routingMethod": "shared" + "routingMethod": "shared", + "legacy": false }, { "cluster": "foo", "tls": true, "url": "https://instance1--application1--tenant1.global.vespa.oath.cloud:4443/", "scope": "global", - "routingMethod": "shared" + "routingMethod": "shared", + "legacy": false } ], "clusters": "http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1/clusters", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1.json index 7ba63e1664d..1084afc9388 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/dev-us-east-1.json @@ -10,7 +10,8 @@ "tls": true, "url": "https://instance1--application1--tenant1.us-east-1.dev.vespa.oath.cloud:4443/", "scope": "zone", - "routingMethod": "shared" + "routingMethod": "shared", + "legacy": false } ], "clusters":"http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/dev/region/us-east-1/clusters", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json index 4251ba1ad95..9059ea338b1 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json @@ -13,14 +13,16 @@ "tls": true, "url": "https://instance1--application1--tenant1.us-central-1.vespa.oath.cloud:4443/", "scope": "zone", - "routingMethod": "shared" + "routingMethod": "shared", + "legacy": false }, { "cluster": "foo", "tls": true, "url": "https://instance1--application1--tenant1.global.vespa.oath.cloud:4443/", "scope": "global", - "routingMethod": "shared" + "routingMethod": "shared", + "legacy": false } ], "clusters":"http://localhost:8080/application/v4/tenant/tenant1/application/application1/instance/instance1/environment/prod/region/us-central-1/clusters", diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java index 136ed508a33..aa9775f1d43 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java @@ -51,7 +51,7 @@ public class RotationRepositoryTest { .region("us-west-1") .build(); - private final DeploymentTester tester = new DeploymentTester(new ControllerTester(rotationsConfig)); + private final DeploymentTester tester = new DeploymentTester(new ControllerTester(rotationsConfig, SystemName.main)); private final RotationRepository repository = tester.controller().routing().rotations(); private final DeploymentContext application = tester.newDeploymentContext("tenant1", "app1", "default"); @@ -92,7 +92,7 @@ public class RotationRepositoryTest { @Test public void strips_whitespace_in_rotation_fqdn() { - var tester = new DeploymentTester(new ControllerTester(rotationsConfigWhitespaces)); + var tester = new DeploymentTester(new ControllerTester(rotationsConfigWhitespaces, SystemName.main)); RotationRepository repository = tester.controller().routing().rotations(); var application2 = tester.newDeploymentContext("tenant1", "app2", "default"); 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 bfb749eb681..047a4461f7c 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 @@ -14,7 +14,6 @@ import com.yahoo.config.provision.HostName; import com.yahoo.config.provision.RegionName; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.zone.RoutingMethod; -import com.yahoo.config.provision.zone.ZoneApi; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.Instance; @@ -28,6 +27,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName; import com.yahoo.vespa.hosted.controller.application.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.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext; @@ -312,7 +312,7 @@ public class RoutingPoliciesTest { @Test public void zone_routing_policies_without_dns_update() { - var tester = new RoutingPoliciesTester(new DeploymentTester(), false); + var tester = new RoutingPoliciesTester(new DeploymentTester(), SystemName.main, false); var context = tester.newDeploymentContext("tenant1", "app1", "default"); tester.provisionLoadBalancers(1, context.instanceId(), true, zone1, zone2); context.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy(); @@ -323,13 +323,17 @@ public class RoutingPoliciesTest { @Test public void global_routing_policies_in_rotationless_system() { - var tester = new RoutingPoliciesTester(new DeploymentTester(new ControllerTester(new RotationsConfig.Builder().build())), true); + var tester = new RoutingPoliciesTester(SystemName.Public); var context = tester.newDeploymentContext("tenant1", "app1", "default"); + List<ZoneId> prodZones = tester.controllerTester().controller().zoneRegistry().zones().all().in(Environment.prod).ids(); + ZoneId zone1 = prodZones.get(0); + ZoneId zone2 = prodZones.get(1); tester.provisionLoadBalancers(1, context.instanceId(), zone1, zone2); var applicationPackage = applicationPackageBuilder() .region(zone1.region().value()) .endpoint("r0", "c0") + .trustDefaultCertificate() .build(); context.submit(applicationPackage).deferLoadBalancerProvisioningIn(Environment.prod).deploy(); @@ -340,6 +344,39 @@ public class RoutingPoliciesTest { } @Test + public void global_routing_policies_in_public() { + var tester = new RoutingPoliciesTester(SystemName.Public); + var context = tester.newDeploymentContext("tenant1", "app1", "default"); + List<ZoneId> prodZones = tester.controllerTester().controller().zoneRegistry().zones().all().in(Environment.prod).ids(); + ZoneId zone1 = prodZones.get(0); + ZoneId zone2 = prodZones.get(1); + + var applicationPackage = applicationPackageBuilder() + .region(zone1.region().value()) + .region(zone2.region().value()) + .endpoint("default", "default") + .trustDefaultCertificate() + .build(); + context.submit(applicationPackage).deploy(); + + tester.assertTargets(context.instanceId(), EndpointId.defaultId(), + ClusterSpec.Id.from("default"), 0, + Map.of(zone1, 1L, zone2, 1L), true); + assertEquals("Registers expected DNS names", + Set.of("app1.tenant1.aws-eu-west-1-w.public.vespa.oath.cloud", + "app1.tenant1.aws-eu-west-1.r.vespa-app.cloud", + "app1.tenant1.aws-eu-west-1a.public.vespa.oath.cloud", + "app1.tenant1.aws-eu-west-1a.z.vespa-app.cloud", + "app1.tenant1.aws-us-east-1-w.public.vespa.oath.cloud", + "app1.tenant1.aws-us-east-1.r.vespa-app.cloud", + "app1.tenant1.aws-us-east-1c.public.vespa.oath.cloud", + "app1.tenant1.aws-us-east-1c.z.vespa-app.cloud", + "app1.tenant1.g.vespa-app.cloud", + "app1.tenant1.global.public.vespa.oath.cloud"), + tester.recordNames()); + } + + @Test public void manual_deployment_creates_routing_policy() { // Empty application package is valid in manually deployed environments var tester = new RoutingPoliciesTester(); @@ -572,24 +609,11 @@ public class RoutingPoliciesTest { var tester = new RoutingPoliciesTester(SystemName.Public); // Configure the system to use the same region for test, staging and prod - var sharedRegion = RegionName.from("aws-us-east-1c"); - var prodZone = ZoneId.from(Environment.prod, sharedRegion); - var stagingZone = ZoneId.from(Environment.staging, sharedRegion); - var testZone = ZoneId.from(Environment.test, sharedRegion); - var zones = List.of(ZoneApiMock.from(prodZone), - ZoneApiMock.from(stagingZone), - ZoneApiMock.from(testZone)); - tester.controllerTester().zoneRegistry() - .setZones(zones) - .setRoutingMethod(zones, RoutingMethod.exclusive); - tester.controllerTester().configServer().bootstrap(List.of(prodZone, stagingZone, testZone), - SystemApplication.notController()); - var context = tester.tester.newDeploymentContext(); var endpointId = EndpointId.of("r0"); var applicationPackage = applicationPackageBuilder() .trustDefaultCertificate() - .region(sharedRegion) + .region("aws-us-east-1c") .endpoint(endpointId.id(), "default") .build(); @@ -600,14 +624,14 @@ public class RoutingPoliciesTest { // Since runJob implicitly tears down the deployment and immediately deletes DNS records associated with the // deployment, we consume only one DNS update at a time here do { - context = context.flushDnsUpdates(1); + context.flushDnsUpdates(1); tester.assertTargets(context.instanceId(), endpointId, 0); } while (!tester.recordNames().isEmpty()); } // Deployment completes context.completeRollout(); - tester.assertTargets(context.instanceId(), endpointId, ClusterSpec.Id.from("default"), 0, Map.of(prodZone, 1L)); + tester.assertTargets(context.instanceId(), endpointId, ClusterSpec.Id.from("default"), 0, Map.of(ZoneId.from("prod", "aws-us-east-1c"), 1L)); } @Test @@ -711,6 +735,14 @@ public class RoutingPoliciesTest { return loadBalancers; } + private static List<ZoneId> publicZones() { + var sharedRegion = RegionName.from("aws-us-east-1c"); + return List.of(ZoneId.from(Environment.prod, sharedRegion), + ZoneId.from(Environment.prod, RegionName.from("aws-eu-west-1a")), + ZoneId.from(Environment.staging, sharedRegion), + ZoneId.from(Environment.test, sharedRegion)); + } + private static class RoutingPoliciesTester { private final DeploymentTester tester; @@ -720,7 +752,26 @@ public class RoutingPoliciesTest { } public RoutingPoliciesTester(SystemName system) { - this(new DeploymentTester(new ControllerTester(system)), true); + this(new DeploymentTester(system.isPublic() + ? new ControllerTester(new RotationsConfig.Builder().build(), system) + : new ControllerTester()), + system, + true); + } + + public RoutingPoliciesTester(DeploymentTester tester, SystemName system, boolean exclusiveRouting) { + this.tester = tester; + List<ZoneId> zones; + if (system.isPublic()) { + zones = publicZones(); + } else { + zones = new ArrayList<>(tester.controllerTester().zoneRegistry().zones().all().ids()); // Default zones + zones.add(zone4); // Missing from default ZoneRegistryMock zones + } + tester.controllerTester().setZones(zones, system); + if (exclusiveRouting) { + tester.controllerTester().setRoutingMethod(zones, RoutingMethod.exclusive); + } } public RoutingPolicies routingPolicies() { @@ -739,19 +790,6 @@ public class RoutingPoliciesTest { return tester.controllerTester(); } - public RoutingPoliciesTester(DeploymentTester tester, boolean exclusiveRouting) { - this.tester = tester; - List<ZoneApi> zones = new ArrayList<>(tester.controllerTester().zoneRegistry().zones().all().zones()); - zones.add(ZoneApiMock.from(zone3)); - zones.add(ZoneApiMock.from(zone4)); - tester.controllerTester().zoneRegistry().setZones(zones); - if (exclusiveRouting) { - tester.controllerTester().zoneRegistry().exclusiveRoutingIn(zones); - } - tester.controllerTester().configServer().bootstrap(tester.controllerTester().zoneRegistry().zones().all().ids(), - SystemApplication.notController()); - } - private void provisionLoadBalancers(int clustersPerZone, ApplicationId application, boolean shared, ZoneId... zones) { for (ZoneId zone : zones) { tester.configServer().removeLoadBalancers(application, zone); @@ -789,14 +827,21 @@ public class RoutingPoliciesTest { } 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) { Set<String> latencyTargets = new HashSet<>(); Map<String, List<ZoneId>> zonesByRegionEndpoint = new HashMap<>(); for (var zone : zoneWeights.keySet()) { - Endpoint regionEndpoint = tester.controller().routing().endpointsOf(new DeploymentId(application, zone)) - .scope(Endpoint.Scope.region) - .cluster(cluster) - .asList() - .get(0); + DeploymentId deployment = new DeploymentId(application, zone); + EndpointList regionEndpoints = tester.controller().routing().endpointsOf(deployment) + .cluster(cluster) + .scope(Endpoint.Scope.region); + if (!legacy) { + regionEndpoints = regionEndpoints.not().legacy(); + } + Endpoint regionEndpoint = regionEndpoints.first().orElseThrow(() -> new IllegalArgumentException("No region endpoint found for " + cluster + " in " + deployment)); zonesByRegionEndpoint.computeIfAbsent(regionEndpoint.dnsName(), (k) -> new ArrayList<>()) .add(zone); } |