diff options
Diffstat (limited to 'controller-server')
3 files changed, 114 insertions, 23 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 4041c955cc4..de84b892ca4 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 @@ -27,9 +27,10 @@ public class Endpoint { private final boolean legacy; private final boolean directRouting; private final boolean tls; + private final boolean wildcard; private Endpoint(String name, ApplicationId application, ZoneId zone, SystemName system, Port port, boolean legacy, - boolean directRouting) { + boolean directRouting, boolean wildcard) { Objects.requireNonNull(name, "name must be non-null"); Objects.requireNonNull(application, "application must be non-null"); Objects.requireNonNull(system, "system must be non-null"); @@ -39,6 +40,7 @@ public class Endpoint { this.legacy = legacy; this.directRouting = directRouting; this.tls = port.tls; + this.wildcard = wildcard; } /** Returns the URL used to access this */ @@ -74,6 +76,11 @@ public class Endpoint { return tls; } + /** Returns whether this is a wildcard endpoint (used only in certificates) */ + public boolean wildcard() { + return wildcard; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -219,30 +226,41 @@ public class Endpoint { private Port port; private boolean legacy = false; private boolean directRouting = false; + private boolean wildcard = false; private EndpointBuilder(ApplicationId application) { this.application = application; } - /** Sets the cluster and zone target of this */ - public EndpointBuilder target(ClusterSpec.Id cluster, ZoneId zone) { - if (endpointId != null) { + /** Sets the cluster target for this */ + public EndpointBuilder target(ClusterSpec.Id cluster) { + if (endpointId != null || wildcard) { throw new IllegalArgumentException("Cannot set multiple target types"); } this.cluster = cluster; - this.zone = zone; return this; } - /** Sets the endpoint ID as defines in deployments.xml */ + /** Sets the endpoint target ID for this (as defined in deployments.xml) */ public EndpointBuilder named(EndpointId endpointId) { - if (cluster != null || zone != null) { + if (cluster != null || wildcard) { throw new IllegalArgumentException("Cannot set multiple target types"); + } else if (zone != null) { + throw new IllegalArgumentException("Cannot set zone for rotation target"); } this.endpointId = endpointId; return this; } + /** Sets the wildcard target for this */ + public EndpointBuilder wildcard() { + if (endpointId != null || cluster != null) { + throw new IllegalArgumentException("Cannot set multiple target types"); + } + this.wildcard = true; + return this; + } + /** Sets the port of this */ public EndpointBuilder on(Port port) { this.port = port; @@ -261,15 +279,28 @@ public class Endpoint { return this; } + public EndpointBuilder zone(ZoneId zone) { + if (endpointId != null) { + throw new IllegalArgumentException("Cannot set zone for endpoint target"); + } else if (this.zone != null) { + throw new IllegalArgumentException("Cannot set multiple zones"); + } + this.zone = zone; + return this; + } + /** Sets the system that owns this */ public Endpoint in(SystemName system) { String name; - if (cluster != null && zone != null) { - name = cluster.value(); + if (wildcard) { + name = "*"; } else if (endpointId != null) { name = endpointId.id(); + } else if (cluster != null) { + name = cluster.value(); + if(zone == null) throw new IllegalArgumentException("Must set zone for cluster target"); } else { - throw new IllegalArgumentException("Must set either cluster or rotation target"); + throw new IllegalArgumentException("Must set either cluster, rotation or wildcard target"); } if (system.isPublic() && !directRouting) { throw new IllegalArgumentException("Public system only supports direct routing endpoints"); @@ -277,9 +308,7 @@ public class Endpoint { if (directRouting && !port.isDefault()) { throw new IllegalArgumentException("Direct routing endpoints only support default port"); } - return new Endpoint(name, application, zone, system, port, legacy, directRouting); + return new Endpoint(name, application, zone, system, port, legacy, directRouting, wildcard); } - } - } 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 a86bbaa317e..8ff860cc37d 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 @@ -71,7 +71,7 @@ public class RoutingPolicy { /** Returns the endpoint of this */ public Endpoint endpointIn(SystemName system) { - return Endpoint.of(owner).target(cluster, zone).on(Port.tls()).directRouting().in(system); + return Endpoint.of(owner).target(cluster).zone(zone).on(Port.tls()).directRouting().in(system); } /** Returns rotation endpoints of this */ 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 bf798d2f004..99701470dc1 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 @@ -117,41 +117,103 @@ public class EndpointTest { 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), + Endpoint.of(app1).target(cluster).zone(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), + Endpoint.of(app1).target(cluster).zone(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), + Endpoint.of(app1).target(cluster).zone(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), + Endpoint.of(app1).target(cluster).zone(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), + Endpoint.of(app1).target(cluster).zone(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), + Endpoint.of(app1).target(ClusterSpec.Id.from("c1")).zone(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), + Endpoint.of(app2).target(cluster).zone(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), + Endpoint.of(app1).target(ClusterSpec.Id.from("c1")).zone(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) + Endpoint.of(app2).target(ClusterSpec.Id.from("c2")).zone(prodZone).on(Port.tls()).directRouting().in(SystemName.Public) ); tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString())); } + @Test + public void test_wildcard_endpoints() { + var defaultCluster = ClusterSpec.Id.from("default"); + var prodZone = ZoneId.from("prod", "us-north-1"); + var testZone = ZoneId.from("test", "us-north-2"); + + var tests = Map.of( + // Default rotation + "https://a1.t1.global.public.vespa.oath.cloud/", + Endpoint.of(app1) + .named(EndpointId.default_()) + .directRouting() + .on(Port.tls()) + .in(SystemName.Public), + + // Wildcard to match other rotations + "https://*.a1.t1.global.public.vespa.oath.cloud/", + Endpoint.of(app1) + .wildcard() + .directRouting() + .on(Port.tls()) + .in(SystemName.Public), + + // Default cluster in zone + "https://a1.t1.us-north-1.public.vespa.oath.cloud/", + Endpoint.of(app1) + .target(defaultCluster) + .zone(prodZone) + .directRouting() + .on(Port.tls()) + .in(SystemName.Public), + + // Wildcard to match other clusters in zone + "https://*.a1.t1.us-north-1.public.vespa.oath.cloud/", + Endpoint.of(app1) + .wildcard() + .zone(prodZone) + .directRouting() + .on(Port.tls()) + .in(SystemName.Public), + + // Default cluster in test zone + "https://a1.t1.us-north-2.test.public.vespa.oath.cloud/", + Endpoint.of(app1) + .target(defaultCluster) + .zone(testZone) + .directRouting() + .on(Port.tls()) + .in(SystemName.Public), + + // Wildcard to match other clusters in test zone + "https://*.a1.t1.us-north-2.test.public.vespa.oath.cloud/", + Endpoint.of(app1) + .wildcard() + .zone(testZone) + .directRouting() + .on(Port.tls()) + .in(SystemName.Public) + ); + + tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString())); + } } |