diff options
author | Martin Polden <mpolden@mpolden.no> | 2019-04-09 14:55:30 +0200 |
---|---|---|
committer | Martin Polden <mpolden@mpolden.no> | 2019-04-09 15:33:41 +0200 |
commit | bfd1b9ec19f522ef3985de887e9a6ec13d355913 (patch) | |
tree | d096ab62078a86aef3ad3a650e4c37d2b20a6e00 | |
parent | ca3c7d297728a566bb0aa9b83a68a1189bc2e962 (diff) |
Add support for global endpoint names in public system
Refactors the existing name building to more easily support all the different
variants.
17 files changed, 607 insertions, 263 deletions
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/SystemName.java b/config-provisioning/src/main/java/com/yahoo/config/provision/SystemName.java index 2de11be08f2..db01bb91b3b 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/SystemName.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/SystemName.java @@ -24,7 +24,7 @@ public enum SystemName { Public, /** VaaS */ - vaas; + vaas; // TODO: Remove this and use public everywhere public static SystemName defaultSystem() { return main; @@ -35,7 +35,7 @@ public enum SystemName { case "dev": return dev; case "cd": return cd; case "main": return main; - case "public": return Public; + case "public": case "Public": return Public; case "vaas": return vaas; default: throw new IllegalArgumentException(String.format("'%s' is not a valid system", value)); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java index 3664f3712e1..84e15deea4c 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java @@ -9,16 +9,16 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.HostName; import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.api.integration.MetricsService.ApplicationMetrics; import com.yahoo.vespa.hosted.controller.api.integration.deployment.ApplicationVersion; import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId; import com.yahoo.vespa.hosted.controller.api.integration.organization.User; -import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.application.ApplicationActivity; import com.yahoo.vespa.hosted.controller.application.Change; import com.yahoo.vespa.hosted.controller.application.Deployment; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs; -import com.yahoo.vespa.hosted.controller.application.GlobalDnsName; +import com.yahoo.vespa.hosted.controller.application.EndpointList; import com.yahoo.vespa.hosted.controller.application.RotationStatus; import com.yahoo.vespa.hosted.controller.rotation.RotationId; @@ -199,9 +199,10 @@ public class Application { return rotation; } - /** Returns the global rotation dns name, if present */ - public Optional<GlobalDnsName> globalDnsName(SystemName system) { - return rotation.map(ignored -> new GlobalDnsName(id, system)); + /** Returns the default global endpoints for this in given system */ + public EndpointList endpointsIn(SystemName system) { + if (rotation.isEmpty()) return EndpointList.EMPTY; + return EndpointList.defaultGlobal(id, system); } public Optional<String> pemDeployKey() { return pemDeployKey; } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java index 7caefa55d28..4c5be570a02 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java @@ -45,7 +45,8 @@ import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.application.ApplicationPackage; import com.yahoo.vespa.hosted.controller.application.Deployment; import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics; -import com.yahoo.vespa.hosted.controller.application.GlobalDnsName; +import com.yahoo.vespa.hosted.controller.application.Endpoint; +import com.yahoo.vespa.hosted.controller.application.EndpointList; import com.yahoo.vespa.hosted.controller.application.JobList; import com.yahoo.vespa.hosted.controller.application.JobStatus; import com.yahoo.vespa.hosted.controller.application.JobStatus.JobRun; @@ -283,7 +284,7 @@ public class ApplicationController { ApplicationVersion applicationVersion; ApplicationPackage applicationPackage; Set<String> rotationNames = new HashSet<>(); - Set<String> cnames = new HashSet<>(); + Set<String> cnames; try (Lock lock = lock(applicationId)) { LockedApplication application = new LockedApplication(require(applicationId), lock); @@ -324,13 +325,7 @@ public class ApplicationController { // Assign global rotation application = withRotation(application, zone); Application app = application.get(); - app.globalDnsName(controller.system()).ifPresent(applicationRotation -> { - rotationNames.add(app.rotation().orElseThrow(() -> new RuntimeException("Global Dns assigned, but no rotation id present")).asString()); - cnames.add(applicationRotation.dnsName()); - cnames.add(applicationRotation.secureDnsName()); - cnames.add(applicationRotation.oathDnsName()); - }); - + cnames = app.endpointsIn(controller.system()).asList().stream().map(Endpoint::dnsName).collect(Collectors.toSet()); // Update application with information from application package if ( ! preferOldestVersion && ! application.get().deploymentJobs().deployedInternally() @@ -438,18 +433,20 @@ public class ApplicationController { application = application.with(rotation.id()); store(application); // store assigned rotation even if deployment fails - GlobalDnsName dnsName = application.get().globalDnsName(controller.system()) - .orElseThrow(() -> new IllegalStateException("Expected rotation to be assigned")); boolean redirectLegacyDns = redirectLegacyDnsFlag.with(FetchVector.Dimension.APPLICATION_ID, application.get().id().serializedForm()) .value(); - registerCname(dnsName.oathDnsName(), rotation.name()); - if (redirectLegacyDns) { - registerCname(dnsName.dnsName(), dnsName.oathDnsName()); - registerCname(dnsName.secureDnsName(), dnsName.oathDnsName()); - } else { - registerCname(dnsName.dnsName(), rotation.name()); - registerCname(dnsName.secureDnsName(), rotation.name()); - } + + EndpointList globalEndpoints = application.get() + .endpointsIn(controller.system()) + .scope(Endpoint.Scope.global); + globalEndpoints.main().ifPresent(mainEndpoint -> { + registerCname(mainEndpoint.dnsName(), rotation.name()); + if (redirectLegacyDns) { + globalEndpoints.legacy(true).asList().forEach(endpoint -> registerCname(endpoint.dnsName(), mainEndpoint.dnsName())); + } else { + globalEndpoints.legacy(true).asList().forEach(endpoint -> registerCname(endpoint.dnsName(), rotation.name())); + } + }); } } return 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 new file mode 100644 index 00000000000..a872421b5ea --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java @@ -0,0 +1,281 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.application; + +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.RotationName; +import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.zone.ZoneId; + +import java.net.URI; +import java.util.Objects; + +/** + * Represents an application's endpoint. The endpoint scope can either be global or a specific zone. This is visible to + * the tenant and is used by the tenant when accessing deployments. + * + * @author mpolden + */ +public class Endpoint { + + public static final String YAHOO_DNS_SUFFIX = ".vespa.yahooapis.com"; + public static final String OATH_DNS_SUFFIX = ".vespa.oath.cloud"; + public static final String PUBLIC_DNS_SUFFIX = ".public.vespa.oath.cloud"; + + private final URI url; + private final Scope scope; + private final boolean legacy; + private final boolean directRouting; + + private Endpoint(String name, ApplicationId application, ZoneId zone, SystemName system, Port port, boolean legacy, + boolean directRouting) { + Objects.requireNonNull(name, "name must be non-null"); + Objects.requireNonNull(application, "application must be non-null"); + Objects.requireNonNull(system, "system must be non-null"); + Objects.requireNonNull(port, "port must be non-null"); + this.url = createUrl(name, application, zone, system, port, legacy, directRouting); + this.scope = zone == null ? Scope.global : Scope.zone; + this.legacy = legacy; + this.directRouting = directRouting; + } + + /** Returns the URL used to access this */ + public URI url() { + return url; + } + + /** Returns the DNS name of this */ + public String dnsName() { + return url.getHost(); + } + + /** Returns the scope of this */ + public Scope scope() { + return scope; + } + + /** Returns whether this is considered a legacy DNS name that is due for removal */ + public boolean legacy() { + return legacy; + } + + /** Returns whether this endpoint supports direct routing */ + public boolean directRouting() { + return directRouting; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Endpoint endpoint = (Endpoint) o; + return url.equals(endpoint.url); + } + + @Override + public int hashCode() { + return Objects.hash(url); + } + + @Override + public String toString() { + return String.format("endpoint %s [scope=%s, legacy=%s, directRouting=%s]", url, scope, legacy, directRouting); + } + + private static URI createUrl(String name, ApplicationId application, ZoneId zone, SystemName system, + Port port, boolean legacy, boolean directRouting) { + String scheme = port.tls ? "https" : "http"; + String separator = separator(system, directRouting, port.tls); + String portPart = port.isDefault() ? "" : ":" + port.port; + return URI.create(scheme + "://" + + sanitize(namePart(name, separator)) + + systemPart(system, separator) + + sanitize(instancePart(application, separator)) + + sanitize(application.application().value()) + + separator + + sanitize(application.tenant().value()) + + "." + + scopePart(zone, legacy) + + dnsSuffix(system, legacy) + + portPart + + "/"); + } + + private static String sanitize(String part) { // TODO: Reject reserved words + return part.replace('_', '-'); + } + + private static String separator(SystemName system, boolean directRouting, boolean tls) { + if (!tls) return "."; + if (directRouting) return "."; + if (isPublic(system)) return "."; + return "--"; + } + + private static String namePart(String name, String separator) { + if ("default".equals(name)) return ""; + return name + separator; + } + + private static String scopePart(ZoneId zone, boolean legacy) { + if (zone == null) return "global"; + if (!legacy && zone.environment().isProduction()) return zone.region().value(); // Skip prod environment for non-legacy endpoints + return zone.region().value() + "." + zone.environment().value(); + } + + private static String instancePart(ApplicationId application, String separator) { + if (application.instance().isDefault()) return ""; // Skip "default" + return application.instance().value() + separator; + } + + private static String systemPart(SystemName system, String separator) { + if (system == SystemName.main || isPublic(system)) return ""; + return system.name() + separator; + } + + private static String dnsSuffix(SystemName system, boolean legacy) { + switch (system) { + case cd: + case main: + if (legacy) return YAHOO_DNS_SUFFIX; + return OATH_DNS_SUFFIX; + case Public: + case vaas: + return PUBLIC_DNS_SUFFIX; + default: throw new IllegalArgumentException("No DNS suffix declared for system " + system); + } + } + + private static boolean isPublic(SystemName system) { // TODO: Remove and inline once we're down to one + return system == SystemName.Public || system == SystemName.vaas; + } + + /** An endpoint's scope */ + public enum Scope { + + /** Endpoint points to all zones */ + global, + + /** Endpoint points to a single zone */ + zone, + + } + + /** Represents an endpoint's HTTP port */ + public static class Port { + + private final int port; + private final boolean tls; + + private Port(int port, boolean tls) { + if (port < 1 || port > 65535) { + throw new IllegalArgumentException("Port must be between 1 and 65535, got " + port); + } + this.port = port; + this.tls = tls; + } + + private boolean isDefault() { + return port == 80 || port == 443; + } + + /** Returns the default HTTPS port */ + public static Port tls() { + return new Port(443, true); + } + + /** Create a HTTPS port */ + public static Port tls(int port) { + return new Port(port, true); + } + + /** Create a HTTP port */ + public static Port plain(int port) { + return new Port(port, false); + } + + } + + /** Build an endpoint for given application */ + public static EndpointBuilder of(ApplicationId application) { + return new EndpointBuilder(application); + } + + public static class EndpointBuilder { + + private final ApplicationId application; + + private ZoneId zone; + private ClusterSpec.Id cluster; + private RotationName rotation; + private Port port; + private boolean legacy = false; + private boolean directRouting = false; + + + private EndpointBuilder(ApplicationId application) { + this.application = application; + } + + /** Sets the cluster and zone target of this endpoint */ + public EndpointBuilder target(ClusterSpec.Id cluster, ZoneId zone) { + if (rotation != null) { + throw new IllegalArgumentException("Cannot set both cluster and rotation target"); + } + this.cluster = cluster; + this.zone = zone; + return this; + } + + /** Sets the rotation target of this endpoint */ + public EndpointBuilder target(RotationName rotation) { + if (cluster != null && zone != null) { + throw new IllegalArgumentException("Cannot set both cluster and rotation target"); + } + this.rotation = rotation; + return this; + } + + /** Sets the port of this endpoint */ + public EndpointBuilder on(Port port) { + this.port = port; + return this; + } + + /** Sets this endpoint as a legacy endpoint */ + public EndpointBuilder legacy() { + this.legacy = true; + return this; + } + + /** + * Sets direct routing support for this endpoint. Direct routing means that this endpoint is served by an + * exclusive load balancer instead of a shared routing layer. + */ + public EndpointBuilder directRouting() { + this.directRouting = true; + return this; + } + + /** The system where this endpoint is available */ + public Endpoint in(SystemName system) { + String name; + if (cluster != null && zone != null) { + name = cluster.value(); + } else if (rotation != null) { + name = rotation.value(); + } else { + throw new IllegalArgumentException("Must set either cluster or rotation target"); + } + if (isPublic(system) && !directRouting) { + throw new IllegalArgumentException("Public system only supports direct routing endpoints"); + } + if (directRouting && !port.isDefault()) { + throw new IllegalArgumentException("Direct routing endpoints only support default port"); + } + return new Endpoint(name, application, zone, system, port, legacy, directRouting); + } + + } + +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java new file mode 100644 index 00000000000..0c04a1f099c --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java @@ -0,0 +1,85 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.application; + +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.RotationName; +import com.yahoo.config.provision.SystemName; +import com.yahoo.vespa.hosted.controller.application.Endpoint.Port; + +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + + +/** + * A list of endpoints for an application. + * + * @author mpolden + */ +public class EndpointList { + + public static final EndpointList EMPTY = new EndpointList(List.of()); + + private final List<Endpoint> endpoints; + + private EndpointList(List<Endpoint> endpoints) { + long mainEndpoints = endpoints.stream() + .filter(endpoint -> endpoint.scope() == Endpoint.Scope.global) + .filter(Predicate.not(Endpoint::directRouting)) + .filter(Predicate.not(Endpoint::legacy)).count(); + if (mainEndpoints > 1) { + throw new IllegalArgumentException("Can have only 1 non-legacy global endpoint, got " + endpoints); + } + if (endpoints.stream().distinct().count() != endpoints.size()) { + throw new IllegalArgumentException("Expected all endpoints to be distinct, got " + endpoints); + } + this.endpoints = List.copyOf(endpoints); + } + + public List<Endpoint> asList() { + return endpoints; + } + + /** Returns the main endpoint, if any */ + public Optional<Endpoint> main() { + return endpoints.stream().filter(Predicate.not(Endpoint::legacy)).findFirst(); + } + + /** Returns the subset of endpoints are either legacy or not */ + public EndpointList legacy(boolean legacy) { + return of(endpoints.stream().filter(endpoint -> endpoint.legacy() == legacy)); + } + + /** Returns the subset of endpoints with given scope */ + public EndpointList scope(Endpoint.Scope scope) { + return of(endpoints.stream().filter(endpoint -> endpoint.scope() == scope)); + } + + /** Returns the union of this and given endpoints */ + public EndpointList and(EndpointList endpoints) { + return of(Stream.concat(asList().stream(), endpoints.asList().stream())); + } + + public static EndpointList of(Stream<Endpoint> endpoints) { + return new EndpointList(endpoints.collect(Collectors.toUnmodifiableList())); + } + + /** Returns the default global endpoints in given system. Default endpoints are served by a pre-provisioned routing layer */ + public static EndpointList defaultGlobal(ApplicationId application, SystemName system) { + // Rotation name is always default in the routing layer + RotationName rotation = RotationName.from("default"); + switch (system) { + case cd: + case main: + return new EndpointList(List.of( + Endpoint.of(application).target(rotation).on(Port.plain(4080)).legacy().in(system), + Endpoint.of(application).target(rotation).on(Port.tls(4443)).legacy().in(system), + Endpoint.of(application).target(rotation).on(Port.tls(4443)).in(system) + )); + } + return EMPTY; + } + +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/GlobalDnsName.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/GlobalDnsName.java deleted file mode 100644 index ae638beed5c..00000000000 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/GlobalDnsName.java +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller.application; - -import com.yahoo.config.provision.ApplicationId; -import com.yahoo.config.provision.RotationName; -import com.yahoo.config.provision.SystemName; - -import java.net.URI; -import java.util.Objects; - -/** - * Represents names for an application's global rotation. - * - * @author mpolden - */ -public class GlobalDnsName { - - // TODO: TLS: Remove all non-secure stuff when all traffic is on HTTPS. - public static final String DNS_SUFFIX = "global.vespa.yahooapis.com"; - public static final String OATH_DNS_SUFFIX = "global.vespa.oath.cloud"; - private static final int port = 4080; - private static final int securePort = 4443; - - private final URI url; - private final URI secureUrl; - private final URI oathUrl; - - public GlobalDnsName(ApplicationId application, SystemName system) { - this(application, system, null); - } - - public GlobalDnsName(ApplicationId application, SystemName system, RotationName rotation) { - Objects.requireNonNull(application, "application must be non-null"); - Objects.requireNonNull(system, "system must be non-null"); - - this.url = URI.create(String.format("http://%s%s%s.%s.%s:%d/", - clusterPart(rotation, "."), - systemPart(system, "."), - sanitize(application.application().value()), - sanitize(application.tenant().value()), - DNS_SUFFIX, - port)); - this.secureUrl = URI.create(String.format("https://%s%s%s--%s.%s:%d/", - clusterPart(rotation, "--"), - systemPart(system, "--"), - sanitize(application.application().value()), - sanitize(application.tenant().value()), - DNS_SUFFIX, - securePort)); - this.oathUrl = URI.create(String.format("https://%s%s%s--%s.%s:%d/", - clusterPart(rotation, "--"), - systemPart(system, "--"), - sanitize(application.application().value()), - sanitize(application.tenant().value()), - OATH_DNS_SUFFIX, - securePort)); - } - - /** URL to this rotation */ - public URI url() { - return url; - } - - /** HTTPS URL to this rotation */ - public URI secureUrl() { - return secureUrl; - } - - /** Oath HTTPS URL to this rotation */ - public URI oathUrl() { - return oathUrl; - } - - /** DNS name for this rotation */ - public String dnsName() { - return url.getHost(); - } - - /** DNS name for this rotation */ - public String secureDnsName() { - return secureUrl.getHost(); - } - - /** Oath DNS name for this rotation */ - public String oathDnsName() { - return oathUrl.getHost(); - } - - /** Sanitize by translating '_' to '-' as the former is not allowed in a DNS name */ - private static String sanitize(String s) { - return s.replace('_', '-'); - } - - private static String clusterPart(RotationName rotation, String separator) { - return rotation == null ? "" : rotation.value() + separator; - } - - private static String systemPart(SystemName system, String separator) { - return SystemName.main == system ? "" : system.name() + separator; - } - -} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/RoutingPolicy.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/RoutingPolicy.java index c459d519ab6..9cf4e4a9254 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/RoutingPolicy.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/RoutingPolicy.java @@ -6,14 +6,13 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.HostName; import com.yahoo.config.provision.RotationName; +import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.zone.ZoneId; +import com.yahoo.vespa.hosted.controller.application.Endpoint.Port; -import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Collectors; /** * Represents the DNS routing policy for a load balancer. @@ -21,7 +20,7 @@ import java.util.stream.Collectors; * @author mortent * @author mpolden */ -public class RoutingPolicy { +public class RoutingPolicy { private final ApplicationId owner; private final ZoneId zone; @@ -41,9 +40,9 @@ public class RoutingPolicy { this.rotations = ImmutableSortedSet.copyOf(Objects.requireNonNull(rotations, "rotations must be non-null")); } - public RoutingPolicy(ApplicationId owner, ZoneId zone, ClusterSpec.Id cluster, HostName canonicalName, + public RoutingPolicy(ApplicationId owner, ZoneId zone, ClusterSpec.Id cluster, SystemName system, HostName canonicalName, Optional<String> dnsZone, Set<RotationName> rotations) { - this(owner, zone, HostName.from(aliasOf(cluster, owner, zone)), canonicalName, dnsZone, rotations); + this(owner, zone, HostName.from(endpointOf(cluster, owner, zone, system).dnsName()), canonicalName, dnsZone, rotations); } /** The application owning this */ @@ -76,6 +75,11 @@ public class RoutingPolicy { return rotations; } + /** Endpoints for this routing policy */ + public EndpointList endpointsIn(SystemName system) { + return EndpointList.of(rotations.stream().map(rotation -> endpointOf(owner, rotation, system))); + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -98,21 +102,14 @@ public class RoutingPolicy { zone.value()); } - /** Returns the alias to use for the given application cluster in zone */ - private static String aliasOf(ClusterSpec.Id cluster, ApplicationId application, ZoneId zone) { - List<String> parts = List.of(ignorePartIfDefault(cluster.value()), - ignorePartIfDefault(application.instance().value()), - application.application().value(), - application.tenant().value() + - "." + zone.value() + "." + "vespa.oath.cloud" - ); - return parts.stream() - .filter(Predicate.not(String::isBlank)) - .collect(Collectors.joining("--")); + /** Returns the endpoint of given rotation */ + public static Endpoint endpointOf(ApplicationId application, RotationName rotation, SystemName system) { + return Endpoint.of(application).target(rotation).on(Port.tls()).directRouting().in(system); } - private static String ignorePartIfDefault(String s) { - return "default".equalsIgnoreCase(s) ? "" : s; + /** Returns the endpoint of given clsuter */ + public static Endpoint endpointOf(ClusterSpec.Id cluster, ApplicationId application, ZoneId zone, SystemName system) { + return Endpoint.of(application).target(cluster, zone).on(Port.tls()).directRouting().in(system); } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainer.java index 7693f224b56..c2c68591dea 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainer.java @@ -5,7 +5,7 @@ import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.api.integration.dns.NameService; import com.yahoo.vespa.hosted.controller.api.integration.dns.Record; import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordData; -import com.yahoo.vespa.hosted.controller.application.GlobalDnsName; +import com.yahoo.vespa.hosted.controller.application.Endpoint; import com.yahoo.vespa.hosted.controller.rotation.Rotation; import com.yahoo.vespa.hosted.controller.rotation.RotationId; import com.yahoo.vespa.hosted.controller.rotation.RotationLock; @@ -84,8 +84,8 @@ public class DnsMaintainer extends Maintainer { /** Returns whether we can update the given record */ private static boolean canUpdate(Record record) { String recordName = record.name().asString(); - return recordName.endsWith(GlobalDnsName.DNS_SUFFIX) || - recordName.endsWith(GlobalDnsName.OATH_DNS_SUFFIX); + return recordName.endsWith(Endpoint.YAHOO_DNS_SUFFIX) || + recordName.endsWith(Endpoint.OATH_DNS_SUFFIX); } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicyMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicyMaintainer.java index 9e10c5f9194..417a1944ad3 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicyMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicyMaintainer.java @@ -15,7 +15,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.dns.Record; import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordData; import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName; import com.yahoo.config.provision.zone.ZoneId; -import com.yahoo.vespa.hosted.controller.application.GlobalDnsName; +import com.yahoo.vespa.hosted.controller.application.Endpoint; import com.yahoo.vespa.hosted.controller.application.RoutingId; import com.yahoo.vespa.hosted.controller.application.RoutingPolicy; import com.yahoo.vespa.hosted.controller.persistence.CuratorDb; @@ -91,7 +91,8 @@ public class RoutingPolicyMaintainer extends Maintainer { // Create DNS record for each routing ID for (Map.Entry<RoutingId, List<RoutingPolicy>> route : routingTable.entrySet()) { - GlobalDnsName dnsName = dnsName(route.getKey()); + Endpoint endpoint = RoutingPolicy.endpointOf(route.getKey().application(), route.getKey().rotation(), + controller().system()); Set<AliasTarget> targets = route.getValue() .stream() .filter(policy -> policy.dnsZone().isPresent()) @@ -100,10 +101,10 @@ public class RoutingPolicyMaintainer extends Maintainer { policy.zone())) .collect(Collectors.toSet()); try { - nameService.createAlias(RecordName.from(dnsName.oathDnsName()), targets); + nameService.createAlias(RecordName.from(endpoint.dnsName()), targets); } catch (Exception e) { log.log(LogLevel.WARNING, "Failed to create or update DNS record for global rotation " + - dnsName.oathDnsName() + ". Retrying in " + maintenanceInterval(), e); + endpoint.dnsName() + ". Retrying in " + maintenanceInterval(), e); } } } @@ -136,7 +137,8 @@ public class RoutingPolicyMaintainer extends Maintainer { /** Register DNS alias for given load balancer */ private RoutingPolicy registerCname(ApplicationId application, ZoneId zone, LoadBalancer loadBalancer) { - RoutingPolicy routingPolicy = new RoutingPolicy(application, zone, loadBalancer.cluster(), + RoutingPolicy routingPolicy = new RoutingPolicy(application, zone, + loadBalancer.cluster(), controller().system(), loadBalancer.hostname(), loadBalancer.dnsZone(), loadBalancer.rotations()); RecordName name = RecordName.from(routingPolicy.alias().value()); @@ -186,23 +188,18 @@ public class RoutingPolicyMaintainer extends Maintainer { Set<RoutingId> activeRoutingIds = routingIdsFrom(loadBalancers); removalCandidates.removeAll(activeRoutingIds); for (RoutingId id : removalCandidates) { - GlobalDnsName dnsName = dnsName(id); + Endpoint endpoint = RoutingPolicy.endpointOf(id.application(), id.rotation(), controller().system()); try { - List<Record> records = nameService.findRecords(Record.Type.ALIAS, RecordName.from(dnsName.oathDnsName())); + List<Record> records = nameService.findRecords(Record.Type.ALIAS, RecordName.from(endpoint.dnsName())); nameService.removeRecords(records); } catch (Exception e) { - log.log(LogLevel.WARNING, "Failed to remove all ALIAS records with name '" + dnsName.oathDnsName() + + log.log(LogLevel.WARNING, "Failed to remove all ALIAS records with name '" + endpoint.dnsName() + "'. Retrying in " + maintenanceInterval()); } } } } - /** Create a global DNS name for given routing ID */ - private GlobalDnsName dnsName(RoutingId routingId) { - return new GlobalDnsName(routingId.application(), controller().system(), routingId.rotation()); - } - /** Compute routing IDs from given load balancers */ private static Set<RoutingId> routingIdsFrom(Map<DeploymentId, List<LoadBalancer>> loadBalancers) { Set<RoutingId> routingIds = new LinkedHashSet<>(); 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 44794600551..a09a3512ef4 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 @@ -9,7 +9,6 @@ import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ApplicationName; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.RegionName; -import com.yahoo.config.provision.RotationName; import com.yahoo.config.provision.TenantName; import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.container.jdisc.HttpResponse; @@ -58,7 +57,7 @@ import com.yahoo.vespa.hosted.controller.application.Deployment; import com.yahoo.vespa.hosted.controller.application.DeploymentCost; import com.yahoo.vespa.hosted.controller.application.DeploymentJobs; import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics; -import com.yahoo.vespa.hosted.controller.application.GlobalDnsName; +import com.yahoo.vespa.hosted.controller.application.Endpoint; import com.yahoo.vespa.hosted.controller.application.JobStatus; import com.yahoo.vespa.hosted.controller.application.RotationStatus; import com.yahoo.vespa.hosted.controller.application.RoutingPolicy; @@ -432,21 +431,22 @@ public class ApplicationApiHandler extends LoggingRequestHandler { // Rotation Cursor globalRotationsArray = object.setArray("globalRotations"); + application.endpointsIn(controller.system()) + .scope(Endpoint.Scope.global) + .asList().stream() + .map(Endpoint::url) + .map(URI::toString) + .forEach(globalRotationsArray::addString); - application.globalDnsName(controller.system()).ifPresent(rotation -> { - globalRotationsArray.addString(rotation.url().toString()); - globalRotationsArray.addString(rotation.secureUrl().toString()); - globalRotationsArray.addString(rotation.oathUrl().toString()); - object.setString("rotationId", application.rotation().get().asString()); - }); + application.rotation().ifPresent(rotation -> object.setString("rotationId", rotation.asString())); // Per-cluster rotations Set<RoutingPolicy> routingPolicies = controller.applications().routingPolicies(application.id()); for (RoutingPolicy policy : routingPolicies) { - for (RotationName rotation : policy.rotations()) { - GlobalDnsName dnsName = new GlobalDnsName(application.id(), controller.system(), rotation); - globalRotationsArray.addString(dnsName.oathUrl().toString()); - } + policy.endpointsIn(controller.system()).asList().stream() + .map(Endpoint::url) + .map(URI::toString) + .forEach(globalRotationsArray::addString); } // Deployments sorted according to deployment spec @@ -524,6 +524,9 @@ public class ApplicationApiHandler extends LoggingRequestHandler { response.setString("environment", deploymentId.zoneId().environment().value()); response.setString("region", deploymentId.zoneId().region().value()); + // serviceUrls contains zone/cluster-specific endpoints for this deployment. The name of these endpoints may + // contain the cluster name (if non-default) and since the controller has no knowledge of clusters, we have to + // ask the routing layer here Cursor serviceUrlArray = response.setArray("serviceUrls"); controller.applications().getDeploymentEndpoints(deploymentId) .ifPresent(endpoints -> endpoints.forEach(endpoint -> serviceUrlArray.addString(endpoint.toString()))); 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 new file mode 100644 index 00000000000..b67f3356e22 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java @@ -0,0 +1,114 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.application; + +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.RotationName; +import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.zone.ZoneId; +import com.yahoo.vespa.hosted.controller.application.Endpoint.Port; +import org.junit.Test; + +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +/** + * @author mpolden + */ +public class EndpointTest { + + private static final ApplicationId app1 = ApplicationId.from("t1", "a1", "default"); + private static final ApplicationId app2 = ApplicationId.from("t2", "a2", "i2"); + + @Test + public void test_global_endpoints() { + RotationName rotation = RotationName.from("default"); // Always default for non-direct routing + + Map<String, Endpoint> tests = Map.of( + // Legacy endpoint + "http://a1.t1.global.vespa.yahooapis.com:4080/", + Endpoint.of(app1).target(rotation).on(Port.plain(4080)).legacy().in(SystemName.main), + + // Legacy endpoint with TLS + "https://a1--t1.global.vespa.yahooapis.com:4443/", + Endpoint.of(app1).target(rotation).on(Port.tls(4443)).legacy().in(SystemName.main), + + // Main endpoint + "https://a1--t1.global.vespa.oath.cloud:4443/", + Endpoint.of(app1).target(rotation).on(Port.tls(4443)).in(SystemName.main), + + // Main endpoint in CD + "https://cd--a1--t1.global.vespa.oath.cloud:4443/", + Endpoint.of(app1).target(rotation).on(Port.tls(4443)).in(SystemName.cd), + + // Main endpoint with direct routing and default TLS port + "https://a1.t1.global.vespa.oath.cloud/", + Endpoint.of(app1).target(rotation).on(Port.tls()).directRouting().in(SystemName.main), + + // Main endpoint with custom rotation name + "https://r1.a1.t1.global.vespa.oath.cloud/", + Endpoint.of(app1).target(RotationName.from("r1")).on(Port.tls()).directRouting().in(SystemName.main), + + // Main endpoint for custom instance + "https://i2.a2.t2.global.vespa.oath.cloud/", + Endpoint.of(app2).target(rotation).on(Port.tls()).directRouting().in(SystemName.main), + + // Main endpoint for custom instance with custom rotation name + "https://r2.i2.a2.t2.global.vespa.oath.cloud/", + Endpoint.of(app2).target(RotationName.from("r2")).on(Port.tls()).directRouting().in(SystemName.main), + + // Main endpoint in public system + "https://a1.t1.global.public.vespa.oath.cloud/", + Endpoint.of(app1).target(rotation).on(Port.tls()).directRouting().in(SystemName.Public) + ); + tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString())); + } + + @Test + public void test_zone_endpoints() { + ClusterSpec.Id cluster = ClusterSpec.Id.from("default"); // Always default for non-direct routing + ZoneId prodZone = ZoneId.from("prod", "us-north-1"); + ZoneId testZone = ZoneId.from("test", "us-north-2"); + + Map<String, Endpoint> tests = Map.of( + // Legacy endpoint (always contains environment) + "http://a1.t1.us-north-1.prod.vespa.yahooapis.com:4080/", + Endpoint.of(app1).target(cluster, prodZone).on(Port.plain(4080)).legacy().in(SystemName.main), + + // Secure legacy endpoint + "https://a1--t1.us-north-1.prod.vespa.yahooapis.com:4443/", + Endpoint.of(app1).target(cluster, prodZone).on(Port.tls(4443)).legacy().in(SystemName.main), + + // Prod endpoint in main + "https://a1--t1.us-north-1.vespa.oath.cloud:4443/", + Endpoint.of(app1).target(cluster, prodZone).on(Port.tls(4443)).in(SystemName.main), + + // Prod endpoint in CD + "https://cd--a1--t1.us-north-1.vespa.oath.cloud:4443/", + Endpoint.of(app1).target(cluster, prodZone).on(Port.tls(4443)).in(SystemName.cd), + + // Test endpoint in main + "https://a1--t1.us-north-2.test.vespa.oath.cloud:4443/", + Endpoint.of(app1).target(cluster, testZone).on(Port.tls(4443)).in(SystemName.main), + + // Non-default cluster in main + "https://c1--a1--t1.us-north-1.vespa.oath.cloud/", + Endpoint.of(app1).target(ClusterSpec.Id.from("c1"), prodZone).on(Port.tls()).in(SystemName.main), + + // Non-default instance in main + "https://i2--a2--t2.us-north-1.vespa.oath.cloud:4443/", + Endpoint.of(app2).target(cluster, prodZone).on(Port.tls(4443)).in(SystemName.main), + + // Non-default cluster in public + "https://c1.a1.t1.us-north-1.public.vespa.oath.cloud/", + Endpoint.of(app1).target(ClusterSpec.Id.from("c1"), prodZone).on(Port.tls()).directRouting().in(SystemName.Public), + + // Non-default cluster and instance in public + "https://c2.i2.a2.t2.us-north-1.public.vespa.oath.cloud/", + Endpoint.of(app2).target(ClusterSpec.Id.from("c2"), prodZone).on(Port.tls()).directRouting().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/application/RoutingPolicyTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/RoutingPolicyTest.java deleted file mode 100644 index 56c2f9bd968..00000000000 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/RoutingPolicyTest.java +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller.application; - -import com.yahoo.config.provision.ApplicationId; -import com.yahoo.config.provision.ClusterSpec; -import com.yahoo.config.provision.HostName; -import com.yahoo.config.provision.zone.ZoneId; -import org.junit.Test; - -import java.util.Optional; -import java.util.Set; - -import static org.junit.Assert.assertEquals; - -/** - * @author mpolden - */ -public class RoutingPolicyTest { - - @Test - public void test_endpoint_names() { - ZoneId zoneId = ZoneId.from("prod", "us-north-1"); - ApplicationId withInstance = ApplicationId.from("tenant", "application", "instance"); - testAlias("instance--application--tenant.prod.us-north-1.vespa.oath.cloud", "default", withInstance, zoneId); - testAlias("cluster--instance--application--tenant.prod.us-north-1.vespa.oath.cloud", "cluster", withInstance, zoneId); - - ApplicationId withDefaultInstance = ApplicationId.from("tenant", "application", "default"); - testAlias("application--tenant.prod.us-north-1.vespa.oath.cloud", "default", withDefaultInstance, zoneId); - testAlias("cluster--application--tenant.prod.us-north-1.vespa.oath.cloud", "cluster", withDefaultInstance, zoneId); - } - - private void testAlias(String expected, String clusterName, ApplicationId applicationId, ZoneId zoneId) { - assertEquals(expected, new RoutingPolicy(applicationId, zoneId, ClusterSpec.Id.from(clusterName), - HostName.from("lb-0"), Optional.empty(), Set.of()).alias().value()); - } - -} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainerTest.java index cbf50b65d1a..256ace4ae09 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainerTest.java @@ -11,7 +11,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordData; import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.application.ApplicationPackage; -import com.yahoo.vespa.hosted.controller.application.GlobalDnsName; +import com.yahoo.vespa.hosted.controller.application.Endpoint; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb; @@ -117,7 +117,7 @@ public class DnsMaintainerTest { for (int i = 1; i <= staleTotal; i++) { Rotation r = rotation(i); tester.controllerTester().nameService().createCname(RecordName.from("stale-record-" + i + "." + - GlobalDnsName.OATH_DNS_SUFFIX), + Endpoint.OATH_DNS_SUFFIX), RecordData.from(r.name() + ".")); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicyMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicyMaintainerTest.java index 47d507e6094..0541a0b05f5 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicyMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicyMaintainerTest.java @@ -6,11 +6,12 @@ import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.HostName; import com.yahoo.config.provision.RotationName; +import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.api.integration.configserver.LoadBalancer; import com.yahoo.vespa.hosted.controller.api.integration.dns.Record; import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName; -import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.application.ApplicationPackage; import com.yahoo.vespa.hosted.controller.application.RoutingPolicy; import com.yahoo.vespa.hosted.controller.deployment.ApplicationPackageBuilder; @@ -56,15 +57,22 @@ public class RoutingPolicyMaintainerTest { public void maintains_global_routing_policies() { int clustersPerZone = 2; tester.deployCompletely(app1, applicationPackage); - Map<Integer, Set<RotationName>> rotations = Map.of(0, Set.of(RotationName.from("r0"))); + // Cluster is member of 2 global rotations + Map<Integer, Set<RotationName>> rotations = Map.of(0, Set.of(RotationName.from("r0"), RotationName.from("r1"))); provisionLoadBalancers(app1, clustersPerZone, rotations); - // Creates alias record for cluster0 + // Creates alias records for cluster0 maintainer.maintain(); - Supplier<List<Record>> records1 = () -> tester.controllerTester().nameService().findRecords(Record.Type.ALIAS, RecordName.from("r0--app1--tenant1.global.vespa.oath.cloud")); + Supplier<List<Record>> records1 = () -> tester.controllerTester().nameService().findRecords(Record.Type.ALIAS, RecordName.from("r0.app1.tenant1.global.vespa.oath.cloud")); + Supplier<List<Record>> records2 = () -> tester.controllerTester().nameService().findRecords(Record.Type.ALIAS, RecordName.from("r1.app1.tenant1.global.vespa.oath.cloud")); assertEquals(2, records1.get().size()); + assertEquals(records1.get().size(), records2.get().size()); assertEquals("lb-0--tenant1:app1:default--prod.us-central-1.", records1.get().get(0).data().asString()); assertEquals("lb-0--tenant1:app1:default--prod.us-west-1.", records1.get().get(1).data().asString()); + assertEquals("lb-0--tenant1:app1:default--prod.us-central-1.", records2.get().get(0).data().asString()); + assertEquals("lb-0--tenant1:app1:default--prod.us-west-1.", records2.get().get(1).data().asString()); + assertEquals(2, tester.controller().applications().routingPolicies(app1.id()).iterator().next() + .endpointsIn(SystemName.main).asList().size()); // Applications gains a new deployment ApplicationPackage updatedApplicationPackage = new ApplicationPackageBuilder() @@ -85,13 +93,13 @@ public class RoutingPolicyMaintainerTest { assertEquals("lb-0--tenant1:app1:default--prod.us-west-1.", records1.get().get(2).data().asString()); // Another application is deployed - Supplier<List<Record>> records2 = () -> tester.controllerTester().nameService().findRecords(Record.Type.ALIAS, RecordName.from("r0--app2--tenant1.global.vespa.oath.cloud")); + Supplier<List<Record>> records3 = () -> tester.controllerTester().nameService().findRecords(Record.Type.ALIAS, RecordName.from("r0.app2.tenant1.global.vespa.oath.cloud")); tester.deployCompletely(app2, applicationPackage); provisionLoadBalancers(app2, 1, Map.of(0, Set.of(RotationName.from("r0")))); maintainer.maintain(); - assertEquals(2, records2.get().size()); - assertEquals("lb-0--tenant1:app2:default--prod.us-central-1.", records2.get().get(0).data().asString()); - assertEquals("lb-0--tenant1:app2:default--prod.us-west-1.", records2.get().get(1).data().asString()); + assertEquals(2, records3.get().size()); + assertEquals("lb-0--tenant1:app2:default--prod.us-central-1.", records3.get().get(0).data().asString()); + assertEquals("lb-0--tenant1:app2:default--prod.us-west-1.", records3.get().get(1).data().asString()); // All rotations for app1 are removed provisionLoadBalancers(app1, clustersPerZone, Collections.emptyMap()); @@ -101,7 +109,7 @@ public class RoutingPolicyMaintainerTest { assertEquals(clustersPerZone * numberOfDeployments, policies.size()); assertTrue("Rotation membership is removed from all policies", policies.stream().allMatch(policy -> policy.rotations().isEmpty())); - assertEquals("Rotations for " + app2 + " are not removed", 2, records2.get().size()); + assertEquals("Rotations for " + app2 + " are not removed", 2, records3.get().size()); } @Test @@ -114,10 +122,10 @@ public class RoutingPolicyMaintainerTest { // Creates records and policies for all clusters in all zones maintainer.maintain(); Set<String> expectedRecords = Set.of( - "c0--app1--tenant1.prod.us-west-1.vespa.oath.cloud", - "c1--app1--tenant1.prod.us-west-1.vespa.oath.cloud", - "c0--app1--tenant1.prod.us-central-1.vespa.oath.cloud", - "c1--app1--tenant1.prod.us-central-1.vespa.oath.cloud" + "c0.app1.tenant1.us-west-1.vespa.oath.cloud", + "c1.app1.tenant1.us-west-1.vespa.oath.cloud", + "c0.app1.tenant1.us-central-1.vespa.oath.cloud", + "c1.app1.tenant1.us-central-1.vespa.oath.cloud" ); assertEquals(expectedRecords, recordNames()); assertEquals(4, policies(app1).size()); @@ -131,12 +139,12 @@ public class RoutingPolicyMaintainerTest { provisionLoadBalancers(app1, clustersPerZone + 1); maintainer.maintain(); expectedRecords = Set.of( - "c0--app1--tenant1.prod.us-west-1.vespa.oath.cloud", - "c1--app1--tenant1.prod.us-west-1.vespa.oath.cloud", - "c2--app1--tenant1.prod.us-west-1.vespa.oath.cloud", - "c0--app1--tenant1.prod.us-central-1.vespa.oath.cloud", - "c1--app1--tenant1.prod.us-central-1.vespa.oath.cloud", - "c2--app1--tenant1.prod.us-central-1.vespa.oath.cloud" + "c0.app1.tenant1.us-west-1.vespa.oath.cloud", + "c1.app1.tenant1.us-west-1.vespa.oath.cloud", + "c2.app1.tenant1.us-west-1.vespa.oath.cloud", + "c0.app1.tenant1.us-central-1.vespa.oath.cloud", + "c1.app1.tenant1.us-central-1.vespa.oath.cloud", + "c2.app1.tenant1.us-central-1.vespa.oath.cloud" ); assertEquals(expectedRecords, recordNames()); assertEquals(6, policies(app1).size()); @@ -146,16 +154,16 @@ public class RoutingPolicyMaintainerTest { provisionLoadBalancers(app2, clustersPerZone); maintainer.maintain(); expectedRecords = Set.of( - "c0--app1--tenant1.prod.us-west-1.vespa.oath.cloud", - "c1--app1--tenant1.prod.us-west-1.vespa.oath.cloud", - "c2--app1--tenant1.prod.us-west-1.vespa.oath.cloud", - "c0--app1--tenant1.prod.us-central-1.vespa.oath.cloud", - "c1--app1--tenant1.prod.us-central-1.vespa.oath.cloud", - "c2--app1--tenant1.prod.us-central-1.vespa.oath.cloud", - "c0--app2--tenant1.prod.us-central-1.vespa.oath.cloud", - "c1--app2--tenant1.prod.us-central-1.vespa.oath.cloud", - "c0--app2--tenant1.prod.us-west-1.vespa.oath.cloud", - "c1--app2--tenant1.prod.us-west-1.vespa.oath.cloud" + "c0.app1.tenant1.us-west-1.vespa.oath.cloud", + "c1.app1.tenant1.us-west-1.vespa.oath.cloud", + "c2.app1.tenant1.us-west-1.vespa.oath.cloud", + "c0.app1.tenant1.us-central-1.vespa.oath.cloud", + "c1.app1.tenant1.us-central-1.vespa.oath.cloud", + "c2.app1.tenant1.us-central-1.vespa.oath.cloud", + "c0.app2.tenant1.us-central-1.vespa.oath.cloud", + "c1.app2.tenant1.us-central-1.vespa.oath.cloud", + "c0.app2.tenant1.us-west-1.vespa.oath.cloud", + "c1.app2.tenant1.us-west-1.vespa.oath.cloud" ); assertEquals(expectedRecords, recordNames()); assertEquals(4, policies(app2).size()); @@ -164,14 +172,14 @@ public class RoutingPolicyMaintainerTest { provisionLoadBalancers(app1, clustersPerZone); maintainer.maintain(); expectedRecords = Set.of( - "c0--app1--tenant1.prod.us-west-1.vespa.oath.cloud", - "c1--app1--tenant1.prod.us-west-1.vespa.oath.cloud", - "c0--app1--tenant1.prod.us-central-1.vespa.oath.cloud", - "c1--app1--tenant1.prod.us-central-1.vespa.oath.cloud", - "c0--app2--tenant1.prod.us-central-1.vespa.oath.cloud", - "c1--app2--tenant1.prod.us-central-1.vespa.oath.cloud", - "c0--app2--tenant1.prod.us-west-1.vespa.oath.cloud", - "c1--app2--tenant1.prod.us-west-1.vespa.oath.cloud" + "c0.app1.tenant1.us-west-1.vespa.oath.cloud", + "c1.app1.tenant1.us-west-1.vespa.oath.cloud", + "c0.app1.tenant1.us-central-1.vespa.oath.cloud", + "c1.app1.tenant1.us-central-1.vespa.oath.cloud", + "c0.app2.tenant1.us-central-1.vespa.oath.cloud", + "c1.app2.tenant1.us-central-1.vespa.oath.cloud", + "c0.app2.tenant1.us-west-1.vespa.oath.cloud", + "c1.app2.tenant1.us-west-1.vespa.oath.cloud" ); assertEquals(expectedRecords, recordNames()); @@ -183,10 +191,10 @@ public class RoutingPolicyMaintainerTest { }); maintainer.maintain(); expectedRecords = Set.of( - "c0--app1--tenant1.prod.us-west-1.vespa.oath.cloud", - "c1--app1--tenant1.prod.us-west-1.vespa.oath.cloud", - "c0--app1--tenant1.prod.us-central-1.vespa.oath.cloud", - "c1--app1--tenant1.prod.us-central-1.vespa.oath.cloud" + "c0.app1.tenant1.us-west-1.vespa.oath.cloud", + "c1.app1.tenant1.us-west-1.vespa.oath.cloud", + "c0.app1.tenant1.us-central-1.vespa.oath.cloud", + "c1.app1.tenant1.us-central-1.vespa.oath.cloud" ); assertEquals(expectedRecords, recordNames()); } 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 ec17e26d867..b47694a61ff 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 @@ -1327,7 +1327,8 @@ public class ApplicationApiTest extends ControllerContainerTest { public void applicationWithPerClusterGlobalRotation() { Application app = controllerTester.createApplication(); RoutingPolicy policy = new RoutingPolicy(app.id(), ZoneId.from(Environment.prod, RegionName.from("us-west-1")), - ClusterSpec.Id.from("default"), HostName.from("lb-0-canonical-name"), + ClusterSpec.Id.from("default"), controllerTester.controller().system(), + HostName.from("lb-0-canonical-name"), Optional.of("dns-zone-1"), Set.of(RotationName.from("c0"))); tester.controller().curator().writeRoutingPolicies(app.id(), Set.of(policy)); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-cluster-global-rotation.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-cluster-global-rotation.json index cd531bb96da..baaf0cd038d 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-cluster-global-rotation.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-cluster-global-rotation.json @@ -8,7 +8,7 @@ "changeBlockers": [], "compileVersion": "(ignore)", "globalRotations": [ - "https://c0--application1--tenant1.global.vespa.oath.cloud:4443/" + "https://c0.application1.tenant1.global.vespa.oath.cloud/" ], "instances": [], "metrics": { 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 666c7774cf5..8d1f40260e3 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 @@ -65,8 +65,8 @@ public class RotationRepositoryTest { application = tester.applications().require(application.id()); assertEquals(expected.id(), application.rotation().get()); - assertEquals(URI.create("http://app1.tenant1.global.vespa.yahooapis.com:4080/"), - application.globalDnsName(SystemName.main).get().url()); + assertEquals(URI.create("https://app1--tenant1.global.vespa.oath.cloud:4443/"), + application.endpointsIn(SystemName.main).main().get().url()); try (RotationLock lock = repository.lock()) { Rotation rotation = repository.getOrAssignRotation(tester.applications().require(application.id()), lock); assertEquals(expected, rotation); @@ -153,10 +153,9 @@ public class RotationRepositoryTest { Application application = tester.createApplication("app2", "tenant2", 22L, 2L); tester.deployCompletely(application, applicationPackage); - assertEquals(new RotationId("foo-1"), tester.applications().require(application.id()) - .rotation().get()); - assertEquals("https://cd--app2--tenant2.global.vespa.yahooapis.com:4443/", tester.applications().require(application.id()) - .globalDnsName(SystemName.cd).get().secureUrl().toString()); + assertEquals(new RotationId("foo-1"), tester.applications().require(application.id()).rotation().get()); + assertEquals("https://cd--app2--tenant2.global.vespa.oath.cloud:4443/", tester.applications().require(application.id()) + .endpointsIn(SystemName.cd).main().get().url().toString()); } } |