diff options
author | Martin Polden <mpolden@mpolden.no> | 2020-02-24 09:32:37 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-24 09:32:37 +0100 |
commit | 6c122d41cb1d890c285ffd1786eb7cc16f8eba55 (patch) | |
tree | dfcfe11266fc462aaed20ff6f02ad2c8b5c67704 | |
parent | 0816207eafbd4a3dc51b41c13df9fccd9c8e54ee (diff) | |
parent | 1e6ca05a2d5caf86935f165554692da2d8bd26cc (diff) |
Merge pull request #12307 from vespa-engine/mpolden/endpoint-routing-method
Use RoutingMethod when building endpoint
7 files changed, 89 insertions, 65 deletions
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/zone/RoutingMethod.java b/config-provisioning/src/main/java/com/yahoo/config/provision/zone/RoutingMethod.java index 2603b157f91..02da2ab98e2 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/zone/RoutingMethod.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/zone/RoutingMethod.java @@ -15,5 +15,11 @@ public enum RoutingMethod { exclusive, /** Routing happens through a shared layer 4 load balancer */ - sharedLayer4 + sharedLayer4; + + /** Returns whether this method routes requests directly to the Vespa container cluster */ + public boolean isDirect() { + return this == exclusive || this == sharedLayer4; + } + } 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 17c9e852bd9..e3d41873a8e 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 @@ -49,7 +49,7 @@ import com.yahoo.vespa.hosted.controller.concurrent.Once; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger; import com.yahoo.vespa.hosted.controller.deployment.Run; import com.yahoo.vespa.hosted.controller.deployment.Versions; -import com.yahoo.vespa.hosted.controller.endpointcertificates.EndpointCertificateManager; +import com.yahoo.vespa.hosted.controller.certificate.EndpointCertificateManager; import com.yahoo.vespa.hosted.controller.persistence.CuratorDb; import com.yahoo.vespa.hosted.controller.security.AccessControl; import com.yahoo.vespa.hosted.controller.security.Credentials; 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 c39255fd7a8..5fa463233fd 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 @@ -1,15 +1,13 @@ // 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.google.common.hash.Hashing; -import com.google.common.io.BaseEncoding; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.zone.RoutingMethod; import com.yahoo.config.provision.zone.ZoneId; import java.net.URI; -import java.nio.charset.Charset; import java.util.Objects; /** @@ -20,28 +18,29 @@ import java.util.Objects; */ 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"; - public static final String PUBLIC_CD_DNS_SUFFIX = ".public-cd.vespa.oath.cloud"; + private static final String YAHOO_DNS_SUFFIX = ".vespa.yahooapis.com"; + private static final String OATH_DNS_SUFFIX = ".vespa.oath.cloud"; + private static final String PUBLIC_DNS_SUFFIX = ".public.vespa.oath.cloud"; + private static final String PUBLIC_CD_DNS_SUFFIX = ".public-cd.vespa.oath.cloud"; private final URI url; private final Scope scope; private final boolean legacy; - private final boolean directRouting; + private final RoutingMethod routingMethod; 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 wildcard) { + RoutingMethod routingMethod, 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"); Objects.requireNonNull(port, "port must be non-null"); - this.url = createUrl(name, application, zone, system, port, legacy, directRouting); + Objects.requireNonNull(routingMethod, "routingMethod must be non-null"); + this.url = createUrl(name, application, zone, system, port, legacy, routingMethod); this.scope = zone == null ? Scope.global : Scope.zone; this.legacy = legacy; - this.directRouting = directRouting; + this.routingMethod = routingMethod; this.tls = port.tls; this.wildcard = wildcard; } @@ -67,12 +66,9 @@ public class Endpoint { return legacy; } - /** - * Returns whether this endpoint supports direct routing. Direct routing means that this endpoint is served by an - * exclusive load balancer instead of a shared routing layer. - */ - public boolean directRouting() { - return directRouting; + /** Returns the routing used for this */ + public RoutingMethod routingMethod() { + return routingMethod; } /** Returns whether this endpoint supports TLS connections */ @@ -100,13 +96,18 @@ public class Endpoint { @Override public String toString() { - return String.format("endpoint %s [scope=%s, legacy=%s, directRouting=%s]", url, scope, legacy, directRouting); + return String.format("endpoint %s [scope=%s, legacy=%s, routingMethod=%s]", url, scope, legacy, routingMethod); + } + + /** Returns the DNS suffix used for endpoints in given system */ + public static String dnsSuffix(SystemName system) { + return dnsSuffix(system, false); } private static URI createUrl(String name, ApplicationId application, ZoneId zone, SystemName system, - Port port, boolean legacy, boolean directRouting) { + Port port, boolean legacy, RoutingMethod routingMethod) { String scheme = port.tls ? "https" : "http"; - String separator = separator(system, directRouting, port.tls); + String separator = separator(system, routingMethod, port.tls); String portPart = port.isDefault() ? "" : ":" + port.port; return URI.create(scheme + "://" + sanitize(namePart(name, separator)) + @@ -126,9 +127,9 @@ public class Endpoint { return part.replace('_', '-'); } - private static String separator(SystemName system, boolean directRouting, boolean tls) { + private static String separator(SystemName system, RoutingMethod routingMethod, boolean tls) { if (!tls) return "."; - if (directRouting) return "."; + if (routingMethod.isDirect()) return "."; if (system.isPublic()) return "."; return "--"; } @@ -214,13 +215,6 @@ public class Endpoint { } - /** Create a DNS name based on a hash of the ApplicationId. This should always be less than 64 characters long. */ - public static String createHashedCn(ApplicationId application, SystemName system) { - var hashCode = Hashing.sha1().hashString(application.serializedForm(), Charset.defaultCharset()); - var base32encoded = BaseEncoding.base32().omitPadding().lowerCase().encode(hashCode.asBytes()); - return 'v' + base32encoded + dnsSuffix(system, false); - } - /** Build an endpoint for given application */ public static EndpointBuilder of(ApplicationId application) { return new EndpointBuilder(application); @@ -234,8 +228,8 @@ public class Endpoint { private ClusterSpec.Id cluster; private EndpointId endpointId; private Port port; + private RoutingMethod routingMethod = RoutingMethod.shared; private boolean legacy = false; - private boolean directRouting = false; private boolean wildcard = false; private EndpointBuilder(ApplicationId application) { @@ -292,9 +286,9 @@ public class Endpoint { return this; } - /** Enables direct routing support for this */ - public EndpointBuilder directRouting() { - this.directRouting = true; + /** Sets the routing method for this */ + public EndpointBuilder routingMethod(RoutingMethod method) { + this.routingMethod = method; return this; } @@ -310,13 +304,13 @@ public class Endpoint { } else { 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"); + if (system.isPublic() && routingMethod != RoutingMethod.exclusive) { + throw new IllegalArgumentException("Public system only supports routing method " + RoutingMethod.exclusive); } - if (directRouting && !port.isDefault()) { - throw new IllegalArgumentException("Direct routing endpoints only support default port"); + if (routingMethod.isDirect() && !port.isDefault()) { + throw new IllegalArgumentException("Routing method " + routingMethod + " can only use default port"); } - return new Endpoint(name, application, zone, system, port, legacy, directRouting, wildcard); + return new Endpoint(name, application, zone, system, port, legacy, routingMethod, wildcard); } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/endpointcertificates/EndpointCertificateManager.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManager.java index 23a3ffb42b6..e22d381e615 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/endpointcertificates/EndpointCertificateManager.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManager.java @@ -1,8 +1,12 @@ -package com.yahoo.vespa.hosted.controller.endpointcertificates; +package com.yahoo.vespa.hosted.controller.certificate; import com.google.common.collect.Sets; +import com.google.common.hash.Hashing; +import com.google.common.io.BaseEncoding; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.zone.RoutingMethod; import com.yahoo.config.provision.zone.ZoneApi; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.container.jdisc.secretstore.SecretNotFoundException; @@ -23,6 +27,7 @@ import com.yahoo.vespa.hosted.controller.application.Endpoint; import com.yahoo.vespa.hosted.controller.application.EndpointId; import com.yahoo.vespa.hosted.controller.persistence.CuratorDb; +import java.nio.charset.Charset; import java.security.cert.X509Certificate; import java.time.Clock; import java.time.Instant; @@ -142,7 +147,7 @@ public class EndpointCertificateManager { if (storedMetaData.requestedDnsSans().isPresent() && storedMetaData.request_id().isPresent()) return; - var hashedCn = Endpoint.createHashedCn(applicationId, zoneRegistry.system()); // use as join key + var hashedCn = commonNameHashOf(applicationId, zoneRegistry.system()); // use as join key EndpointCertificateMetadata providerMetadata = sanToEndpointCertificate.get(hashedCn); if(providerMetadata == null) { @@ -233,7 +238,7 @@ public class EndpointCertificateManager { // We add first an endpoint name based on a hash of the applicationId, // as the certificate provider requires the first CN to be < 64 characters long. - endpointDnsNames.add(Endpoint.createHashedCn(applicationId, zoneRegistry.system())); + endpointDnsNames.add(commonNameHashOf(applicationId, zoneRegistry.system())); var globalDefaultEndpoint = Endpoint.of(applicationId).named(EndpointId.defaultId()); var rotationEndpoints = Endpoint.of(applicationId).wildcard(); @@ -244,7 +249,7 @@ public class EndpointCertificateManager { )); Stream.concat(Stream.of(globalDefaultEndpoint, rotationEndpoints), zoneLocalEndpoints) - .map(Endpoint.EndpointBuilder::directRouting) + .map(endpoint -> endpoint.routingMethod(RoutingMethod.exclusive)) .map(endpoint -> endpoint.on(Endpoint.Port.tls())) .map(endpointBuilder -> endpointBuilder.in(zoneRegistry.system())) .map(Endpoint::dnsName).forEach(endpointDnsNames::add); @@ -252,4 +257,11 @@ public class EndpointCertificateManager { return Collections.unmodifiableList(endpointDnsNames); } + /** Create a common name based on a hash of the ApplicationId. This should always be less than 64 characters long. */ + private static String commonNameHashOf(ApplicationId application, SystemName system) { + var hashCode = Hashing.sha1().hashString(application.serializedForm(), Charset.defaultCharset()); + var base32encoded = BaseEncoding.base32().omitPadding().lowerCase().encode(hashCode.asBytes()); + return 'v' + base32encoded + Endpoint.dnsSuffix(system); + } + } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java index 9ef2d519c05..32ec9f359a9 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java @@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableSortedSet; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.HostName; import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.zone.RoutingMethod; import com.yahoo.vespa.hosted.controller.application.Endpoint; import com.yahoo.vespa.hosted.controller.application.Endpoint.Port; import com.yahoo.vespa.hosted.controller.application.EndpointId; @@ -70,7 +71,10 @@ public class RoutingPolicy { /** Returns the endpoint of this */ public Endpoint endpointIn(SystemName system) { - return Endpoint.of(id.owner()).target(id.cluster(), id.zone()).on(Port.tls()).directRouting().in(system); + return Endpoint.of(id.owner()).target(id.cluster(), id.zone()) + .routingMethod(RoutingMethod.exclusive) + .on(Port.tls()) + .in(system); } /** Returns global endpoints which this is a member of */ @@ -100,7 +104,10 @@ public class RoutingPolicy { /** Creates a global endpoint for given application */ public static Endpoint globalEndpointOf(ApplicationId application, EndpointId endpointId, SystemName system) { - return Endpoint.of(application).named(endpointId).on(Port.tls()).directRouting().in(system); + return Endpoint.of(application).named(endpointId) + .on(Port.tls()) + .routingMethod(RoutingMethod.exclusive) + .in(system); } } 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 ea97e3e6c71..81bbbb479b2 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 @@ -4,6 +4,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.SystemName; +import com.yahoo.config.provision.zone.RoutingMethod; import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.application.Endpoint.Port; import org.junit.Test; @@ -47,23 +48,23 @@ public class EndpointTest { // Main endpoint with direct routing and default TLS port "https://a1.t1.global.vespa.oath.cloud/", - Endpoint.of(app1).named(endpointId).on(Port.tls()).directRouting().in(SystemName.main), + Endpoint.of(app1).named(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main), // Main endpoint with custom rotation name "https://r1.a1.t1.global.vespa.oath.cloud/", - Endpoint.of(app1).named(EndpointId.of("r1")).on(Port.tls()).directRouting().in(SystemName.main), + Endpoint.of(app1).named(EndpointId.of("r1")).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main), // Main endpoint for custom instance in default rotation "https://i2.a2.t2.global.vespa.oath.cloud/", - Endpoint.of(app2).named(endpointId).on(Port.tls()).directRouting().in(SystemName.main), + Endpoint.of(app2).named(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main), // Main endpoint for custom instance with custom rotation name "https://r2.i2.a2.t2.global.vespa.oath.cloud/", - Endpoint.of(app2).named(EndpointId.of("r2")).on(Port.tls()).directRouting().in(SystemName.main), + Endpoint.of(app2).named(EndpointId.of("r2")).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main), // Main endpoint in public system "https://a1.t1.global.public.vespa.oath.cloud/", - Endpoint.of(app1).named(endpointId).on(Port.tls()).directRouting().in(SystemName.Public) + Endpoint.of(app1).named(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public) ); tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString())); } @@ -95,23 +96,23 @@ public class EndpointTest { // Main endpoint with direct routing and default TLS port "https://a1.t1.global.vespa.oath.cloud/", - Endpoint.of(app1).named(endpointId).on(Port.tls()).directRouting().in(SystemName.main), + Endpoint.of(app1).named(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main), // Main endpoint with custom rotation name "https://r1.a1.t1.global.vespa.oath.cloud/", - Endpoint.of(app1).named(EndpointId.of("r1")).on(Port.tls()).directRouting().in(SystemName.main), + Endpoint.of(app1).named(EndpointId.of("r1")).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main), // Main endpoint for custom instance in default rotation "https://i2.a2.t2.global.vespa.oath.cloud/", - Endpoint.of(app2).named(endpointId).on(Port.tls()).directRouting().in(SystemName.main), + Endpoint.of(app2).named(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main), // Main endpoint for custom instance with custom rotation name "https://r2.i2.a2.t2.global.vespa.oath.cloud/", - Endpoint.of(app2).named(EndpointId.of("r2")).on(Port.tls()).directRouting().in(SystemName.main), + Endpoint.of(app2).named(EndpointId.of("r2")).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.main), // Main endpoint in public system "https://a1.t1.global.public.vespa.oath.cloud/", - Endpoint.of(app1).named(endpointId).on(Port.tls()).directRouting().in(SystemName.Public) + Endpoint.of(app1).named(endpointId).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public) ); tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString())); } @@ -153,11 +154,15 @@ public class EndpointTest { // 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"), prodZone).on(Port.tls()).routingMethod(RoutingMethod.exclusive).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"), prodZone).on(Port.tls()).routingMethod(RoutingMethod.exclusive).in(SystemName.Public), + + // Endpoint in main using shared layer 4 + "https://a1.t1.us-north-1.vespa.oath.cloud/", + Endpoint.of(app1).target(cluster, prodZone).on(Port.tls()).routingMethod(RoutingMethod.sharedLayer4).in(SystemName.main) ); tests.forEach((expected, endpoint) -> assertEquals(expected, endpoint.url().toString())); } @@ -173,7 +178,7 @@ public class EndpointTest { "https://a1.t1.global.public.vespa.oath.cloud/", Endpoint.of(app1) .named(EndpointId.defaultId()) - .directRouting() + .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) .in(SystemName.Public), @@ -181,7 +186,7 @@ public class EndpointTest { "https://*.a1.t1.global.public.vespa.oath.cloud/", Endpoint.of(app1) .wildcard() - .directRouting() + .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) .in(SystemName.Public), @@ -189,7 +194,7 @@ public class EndpointTest { "https://a1.t1.us-north-1.public.vespa.oath.cloud/", Endpoint.of(app1) .target(defaultCluster, prodZone) - .directRouting() + .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) .in(SystemName.Public), @@ -197,7 +202,7 @@ public class EndpointTest { "https://*.a1.t1.us-north-1.public.vespa.oath.cloud/", Endpoint.of(app1) .wildcard(prodZone) - .directRouting() + .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) .in(SystemName.Public), @@ -205,7 +210,7 @@ public class EndpointTest { "https://a1.t1.us-north-2.test.public.vespa.oath.cloud/", Endpoint.of(app1) .target(defaultCluster, testZone) - .directRouting() + .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) .in(SystemName.Public), @@ -213,7 +218,7 @@ public class EndpointTest { "https://*.a1.t1.us-north-2.test.public.vespa.oath.cloud/", Endpoint.of(app1) .wildcard(testZone) - .directRouting() + .routingMethod(RoutingMethod.exclusive) .on(Port.tls()) .in(SystemName.Public) ); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/endpointcertificates/EndpointCertificateManagerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManagerTest.java index 3f8e91dec58..93f081be63e 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/endpointcertificates/EndpointCertificateManagerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/certificate/EndpointCertificateManagerTest.java @@ -1,4 +1,4 @@ -package com.yahoo.vespa.hosted.controller.endpointcertificates; +package com.yahoo.vespa.hosted.controller.certificate; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.SystemName; |