summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2020-06-26 10:44:58 +0200
committerMartin Polden <mpolden@mpolden.no>2020-06-29 14:41:52 +0200
commit97dfae5b40f9f9a1aeb72caac363f82a27c1f381 (patch)
tree8c74cbb6c9e51566546c69e3ca9c43cbef4d64a9
parentba3680e98b722cc0c4926e96a7cca2089ef2e7b3 (diff)
Support building weighted endpoints
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java42
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/application/EndpointTest.java38
2 files changed, 74 insertions, 6 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 f8982c96637..62804074337 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
@@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.controller.application;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
@@ -36,15 +37,14 @@ public class Endpoint {
private final RoutingMethod routingMethod;
private final boolean tls;
- private Endpoint(String name, URI url, List<ZoneId> zones, Scope scope, Port port, boolean legacy,
- RoutingMethod routingMethod) {
+ private Endpoint(String name, URI url, List<ZoneId> zones, Scope scope, Port port, boolean legacy, RoutingMethod routingMethod) {
Objects.requireNonNull(name, "name must be non-null");
Objects.requireNonNull(zones, "zones must be non-null");
Objects.requireNonNull(scope, "scope must be non-null");
Objects.requireNonNull(port, "port must be non-null");
Objects.requireNonNull(routingMethod, "routingMethod must be non-null");
- if (scope == Scope.zone && zones.size() != 1) {
- throw new IllegalArgumentException("A single zone must be given for zone-scoped endpoints");
+ if ((scope == Scope.zone || scope == Scope.weighted) && zones.size() != 1) {
+ throw new IllegalArgumentException("A single zone must be given for " + scope + "-scoped endpoints");
}
this.name = name;
this.url = url;
@@ -189,8 +189,10 @@ public class Endpoint {
private static String scopePart(Scope scope, List<ZoneId> zones, boolean legacy) {
if (scope == Scope.global) return "global";
var zone = zones.get(0);
- if (!legacy && zone.environment().isProduction()) return zone.region().value(); // Skip prod environment for non-legacy endpoints
- return zone.region().value() + "." + zone.environment().value();
+ var region = zone.region().value();
+ if (scope == Scope.weighted) region += "-w";
+ if (!legacy && zone.environment().isProduction()) return region; // Skip prod environment for non-legacy endpoints
+ return region + "." + zone.environment().value();
}
private static String instancePart(ApplicationId application, String separator) {
@@ -241,6 +243,21 @@ public class Endpoint {
return part.substring(Math.max(0, part.length() - 63));
}
+ /** Returns the given region without availability zone */
+ private static RegionName effectiveRegion(RegionName region) {
+ if (region.value().isEmpty()) return region;
+ String value = region.value();
+ char lastChar = value.charAt(value.length() - 1);
+ if (lastChar >= 'a' && lastChar <= 'z') { // Remove availability zone
+ value = value.substring(0, value.length() - 1);
+ }
+ return RegionName.from(value);
+ }
+
+ private static ZoneId effectiveZone(ZoneId zone) {
+ return ZoneId.from(zone.environment(), effectiveRegion(zone.region()));
+ }
+
/** An endpoint's scope */
public enum Scope {
@@ -250,6 +267,9 @@ public class Endpoint {
/** Endpoint points to a single zone */
zone,
+ /** Endpoint points to a single region */
+ weighted,
+
}
/** Represents an endpoint's HTTP port */
@@ -388,6 +408,16 @@ public class Endpoint {
return this;
}
+ /** Make this a weighted endpoint */
+ public EndpointBuilder weighted() {
+ if (scope != Scope.zone && scope != Scope.weighted) {
+ throw new IllegalArgumentException("Endpoint must target zone before making it weighted");
+ }
+ this.scope = Scope.weighted;
+ this.zones = List.of(effectiveZone(zones.get(0)));
+ return this;
+ }
+
/** Sets the system that owns this */
public Endpoint in(SystemName system) {
String name;
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 f968b8c76d7..2e57a5eaaa1 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
@@ -228,6 +228,44 @@ public class EndpointTest {
}
@Test
+ public void weighted_endpoints() {
+ var cluster = ClusterSpec.Id.from("default");
+ Map<String, Endpoint> tests = Map.of(
+ "https://a1.t1.us-north-1-w.public.vespa.oath.cloud/",
+ Endpoint.of(app1)
+ .target(cluster, ZoneId.from("prod", "us-north-1a"))
+ .weighted()
+ .routingMethod(RoutingMethod.exclusive)
+ .on(Port.tls())
+ .in(SystemName.Public),
+ "https://a1.t1.us-north-2-w.public.vespa.oath.cloud/",
+ Endpoint.of(app1)
+ .target(cluster, ZoneId.from("prod", "us-north-2"))
+ .weighted()
+ .routingMethod(RoutingMethod.exclusive)
+ .on(Port.tls())
+ .in(SystemName.Public),
+ "https://a1.t1.us-north-2-w.test.public.vespa.oath.cloud/",
+ Endpoint.of(app1)
+ .target(cluster, ZoneId.from("test", "us-north-2"))
+ .weighted()
+ .routingMethod(RoutingMethod.exclusive)
+ .on(Port.tls())
+ .in(SystemName.Public)
+ );
+ tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString()));
+ Endpoint endpoint = Endpoint.of(app1)
+ .target(cluster, ZoneId.from("prod", "us-north-1a"))
+ .weighted()
+ .routingMethod(RoutingMethod.exclusive)
+ .on(Port.tls())
+ .in(SystemName.main);
+ assertEquals("Availability zone is removed from region",
+ "us-north-1",
+ endpoint.zones().get(0).region().value());
+ }
+
+ @Test
public void upstream_name() {
var zone = ZoneId.from("prod", "us-north-1");
var tests1 = Map.of(