summaryrefslogtreecommitdiffstats
path: root/controller-server/src
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2021-11-02 15:50:24 +0100
committerMartin Polden <mpolden@mpolden.no>2021-11-02 15:50:24 +0100
commit7f6989ae1f410f3932fb661beb28922f02d2d01f (patch)
tree0871912e951739ec33f130f89def4f5f9d0b6c45 /controller-server/src
parent6bd3bb5ee34ea82c129dd03d30eeb414eb15196e (diff)
Support building application-scoped endpoint names
Diffstat (limited to 'controller-server/src')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java43
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java44
2 files changed, 78 insertions, 9 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
index cd70b804dc3..5e458a051b6 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
@@ -75,7 +75,7 @@ public class Endpoint {
zones,
scope,
Objects.requireNonNull(system, "system must be non-null"),
- port,
+ Objects.requireNonNull(port, "port must be non-null"),
legacy,
routingMethod),
zones, scope, port, legacy, routingMethod);
@@ -140,7 +140,7 @@ public class Endpoint {
/** Returns the upstream ID of given deployment. This *must* match what the routing layer generates */
public String upstreamIdOf(DeploymentId deployment) {
- if (scope != Scope.global) throw new IllegalArgumentException("Scope " + scope + " does not have upstream name");
+ if (!scope.multiRegion()) throw new IllegalArgumentException("Scope " + scope + " does not have upstream name");
if (!routingMethod.isShared()) throw new IllegalArgumentException("Routing method " + routingMethod + " does not have upstream name");
return upstreamIdOf(cluster.value(), deployment.applicationId(), deployment.zoneId());
}
@@ -175,7 +175,7 @@ public class Endpoint {
String portPart = port.isDefault() ? "" : ":" + port.port;
return URI.create(scheme + "://" +
sanitize(namePart(name, separator)) +
- systemPart(system, separator, legacy) +
+ systemPart(system, separator) +
sanitize(instancePart(instance, separator)) +
sanitize(application.application().value()) +
separator +
@@ -205,7 +205,7 @@ public class Endpoint {
private static String scopePart(Scope scope, List<ZoneId> zones, SystemName system, boolean legacy) {
String scopeSymbol = scopeSymbol(scope, system);
- if (scope == Scope.global) return scopeSymbol;
+ if (scope.multiRegion()) return scopeSymbol;
ZoneId zone = zones.get(0);
String region = zone.region().value();
@@ -223,12 +223,14 @@ public class Endpoint {
case zone: return "z";
case region: return "w";
case global: return "g";
+ case application: return "a";
}
}
switch (scope) {
case zone: return "";
case region: return "w";
case global: return "global";
+ case application: return "a";
}
throw new IllegalArgumentException("No scope symbol defined for " + scope + " in " + system);
}
@@ -239,9 +241,9 @@ public class Endpoint {
return instance.get().value() + separator;
}
- private static String systemPart(SystemName system, String separator, boolean legacy) {
+ private static String systemPart(SystemName system, String separator) {
if (!system.isCd()) return "";
- if (system.isPublic() && !legacy) return "";
+ if (system.isPublic()) return "";
return system.value() + separator;
}
@@ -315,6 +317,13 @@ public class Endpoint {
/** An endpoint's scope */
public enum Scope {
+ /**
+ * Endpoint points to a multiple instances of an application.
+ *
+ * Traffic is routed across instances according to weights specified in deployment.xml
+ */
+ application,
+
/** Endpoint points to one or more zones. Traffic is routed to the zone closest to the client */
global,
@@ -326,12 +335,23 @@ public class Endpoint {
region,
/** Endpoint points to a single zone */
- zone,
+ zone;
+
+ /** Returns whether this scope may span multiple regions */
+ public boolean multiRegion() {
+ // application scope doesn't technically support multiple regions in practice, but we assume it does for the
+ // purposes of building an endpoint name. This allows us to support multiple regions in the future without
+ // needing to change endpoint names.
+ return this == application || this == global;
+ }
+
}
/** Represents an endpoint's HTTP port */
public static class Port {
+ private static final Port TLS_DEFAULT = new Port(443, true);
+
private final int port;
private final boolean tls;
@@ -349,7 +369,7 @@ public class Endpoint {
/** Returns the default HTTPS port */
public static Port tls() {
- return new Port(443, true);
+ return TLS_DEFAULT;
}
/** Returns default port for the given routing method */
@@ -440,6 +460,13 @@ public class Endpoint {
return target(ClusterSpec.Id.from("*"), zone);
}
+ /** Sets the application target with given ID, zones and cluster (as defined in deployments.xml) */
+ public EndpointBuilder targetApplication(EndpointId endpointId, ClusterSpec.Id cluster, ZoneId zone) {
+ target(endpointId, cluster, List.of(zone));
+ this.scope = Scope.application;
+ return this;
+ }
+
/** Sets the region target for this, deduced from given zone */
public EndpointBuilder targetRegion(ClusterSpec.Id cluster, ZoneId zone) {
checkScope();
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
index 09298113827..e2fb70e5338 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java
@@ -311,6 +311,41 @@ public class EndpointTest {
}
@Test
+ public void application_endpoints() {
+ Map<String, Endpoint> tests = Map.of(
+ "https://weighted.a1.t1.a.vespa-app.cloud/",
+ Endpoint.of(app1)
+ .targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"),
+ ZoneId.from("prod", "us-west-1"))
+ .routingMethod(RoutingMethod.exclusive)
+ .on(Port.tls())
+ .in(SystemName.Public),
+ "https://weighted.a1.t1.a.cd.vespa-app.cloud/",
+ Endpoint.of(app1)
+ .targetApplication(EndpointId.of("weighted"), ClusterSpec.Id.from("qrs"),
+ ZoneId.from("prod", "us-west-1"))
+ .routingMethod(RoutingMethod.exclusive)
+ .on(Port.tls())
+ .in(SystemName.PublicCd),
+ "https://a2.t2.a.vespa.oath.cloud/",
+ Endpoint.of(app2)
+ .targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"),
+ ZoneId.from("prod", "us-east-3"))
+ .routingMethod(RoutingMethod.exclusive)
+ .on(Port.tls())
+ .in(SystemName.main),
+ "https://cd.a2.t2.a.vespa.oath.cloud/",
+ Endpoint.of(app2)
+ .targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("qrs"),
+ ZoneId.from("prod", "us-east-3"))
+ .routingMethod(RoutingMethod.exclusive)
+ .on(Port.tls())
+ .in(SystemName.cd)
+ );
+ tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString()));
+ }
+
+ @Test
public void upstream_name() {
var zone = ZoneId.from("prod", "us-north-1");
var tests1 = Map.of(
@@ -320,7 +355,14 @@ public class EndpointTest {
// With non-default cluster
"c1.a1.t1.us-north-1.prod",
- Endpoint.of(instance1).target(EndpointId.of("ignored1"), ClusterSpec.Id.from("c1"), List.of(zone)).on(Port.tls(4443)).in(SystemName.main)
+ Endpoint.of(instance1).target(EndpointId.of("ignored1"), ClusterSpec.Id.from("c1"), List.of(zone)).on(Port.tls(4443)).in(SystemName.main),
+
+ // With application endpoint
+ "c2.a1.t1.us-north-1.prod",
+ Endpoint.of(app1).targetApplication(EndpointId.defaultId(), ClusterSpec.Id.from("c2"), zone)
+ .routingMethod(RoutingMethod.sharedLayer4)
+ .on(Port.tls())
+ .in(SystemName.main)
);
var tests2 = Map.of(
// With non-default instance and default cluster