aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2019-04-09 14:55:30 +0200
committerMartin Polden <mpolden@mpolden.no>2019-04-09 15:33:41 +0200
commitbfd1b9ec19f522ef3985de887e9a6ec13d355913 (patch)
treed096ab62078a86aef3ad3a650e4c37d2b20a6e00
parentca3c7d297728a566bb0aa9b83a68a1189bc2e962 (diff)
Add support for global endpoint names in public system
Refactors the existing name building to more easily support all the different variants.
-rw-r--r--config-provisioning/src/main/java/com/yahoo/config/provision/SystemName.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java11
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java35
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java281
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/EndpointList.java85
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/GlobalDnsName.java102
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/RoutingPolicy.java35
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainer.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicyMaintainer.java23
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java27
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java114
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/RoutingPolicyTest.java37
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainerTest.java4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/RoutingPolicyMaintainerTest.java90
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application-cluster-global-rotation.json2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java11
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());
}
}