diff options
7 files changed, 121 insertions, 22 deletions
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationClusterEndpoint.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationClusterEndpoint.java index 0154e5d3b13..ebd4059f9f9 100644 --- a/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationClusterEndpoint.java +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationClusterEndpoint.java @@ -4,6 +4,7 @@ package com.yahoo.config.model.api; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.SystemName; import java.util.List; import java.util.Objects; @@ -17,6 +18,18 @@ import java.util.stream.Stream; * @author mortent */ public class ApplicationClusterEndpoint { + @Override + public String toString() { + return "ApplicationClusterEndpoint{" + + "dnsName=" + dnsName + + ", scope=" + scope + + ", routingMethod=" + routingMethod + + ", weight=" + weight + + ", hostNames=" + hostNames + + ", clusterId='" + clusterId + '\'' + + '}'; + } + public enum Scope {application, global, zone} public enum RoutingMethod {shared, sharedLayer4} @@ -133,15 +146,15 @@ public class ApplicationClusterEndpoint { } // TODO: remove - public static DnsName sharedNameFrom(ClusterSpec.Id cluster, ApplicationId applicationId, String suffix) { - String name = dnsParts(cluster, applicationId) + public static DnsName sharedNameFrom(SystemName systemName, ClusterSpec.Id cluster, ApplicationId applicationId, String suffix) { + String name = dnsParts(systemName, cluster, applicationId) .filter(Objects::nonNull) // remove null values that were "default" .collect(Collectors.joining("--")); return new DnsName(sanitize(name) + suffix); // Need to sanitize name since it is considered one label } - public static DnsName sharedL4NameFrom(ClusterSpec.Id cluster, ApplicationId applicationId, String suffix) { - String name = dnsParts(cluster, applicationId) + public static DnsName sharedL4NameFrom(SystemName systemName, ClusterSpec.Id cluster, ApplicationId applicationId, String suffix) { + String name = dnsParts(systemName, cluster, applicationId) .filter(Objects::nonNull) // remove null values that were "default" .map(DnsName::sanitize) .collect(Collectors.joining(".")); @@ -152,9 +165,10 @@ public class ApplicationClusterEndpoint { return new DnsName(name); } - private static Stream<String> dnsParts(ClusterSpec.Id cluster, ApplicationId applicationId) { + private static Stream<String> dnsParts(SystemName systemName, ClusterSpec.Id cluster, ApplicationId applicationId) { return Stream.of( nullIfDefault(cluster.value()), + systemPart(systemName), nullIfDefault(applicationId.instance().value()), applicationId.application().value(), applicationId.tenant().value() @@ -180,5 +194,16 @@ public class ApplicationClusterEndpoint { private static String nullIfDefault(String string) { return Optional.of(string).filter(s -> !s.equals("default")).orElse(null); } + + private static String systemPart(SystemName systemName) { + return "cd".equals(systemName.value()) ? systemName.value() : null; + } + + @Override + public String toString() { + return "DnsName{" + + "name='" + name + '\'' + + '}'; + } } } diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ContainerEndpoint.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ContainerEndpoint.java index a114f9d40ef..cd31eb2a49a 100644 --- a/config-model-api/src/main/java/com/yahoo/config/model/api/ContainerEndpoint.java +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ContainerEndpoint.java @@ -3,6 +3,9 @@ package com.yahoo.config.model.api; import java.util.List; import java.util.Objects; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.OptionalLong; /** * ContainerEndpoint tracks the service names that a Container Cluster should be @@ -16,11 +19,17 @@ public class ContainerEndpoint { private final String clusterId; private final ApplicationClusterEndpoint.Scope scope; private final List<String> names; + private final OptionalInt weight; public ContainerEndpoint(String clusterId, ApplicationClusterEndpoint.Scope scope, List<String> names) { + this(clusterId, scope, names, OptionalInt.empty()); + } + + public ContainerEndpoint(String clusterId, ApplicationClusterEndpoint.Scope scope, List<String> names, OptionalInt weight) { this.clusterId = Objects.requireNonNull(clusterId); this.scope = Objects.requireNonNull(scope); this.names = List.copyOf(Objects.requireNonNull(names)); + this.weight = weight; } public String clusterId() { @@ -35,6 +44,10 @@ public class ContainerEndpoint { return scope; } + public OptionalInt weight() { + return weight; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java index f7e8afc2d94..f6a0eb9ce8d 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java @@ -210,6 +210,7 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat for(String suffix : deployState.getProperties().zoneDnsSuffixes()) { // L4 ApplicationClusterEndpoint.DnsName l4Name = ApplicationClusterEndpoint.DnsName.sharedL4NameFrom( + deployState.zone().system(), ClusterSpec.Id.from(getName()), deployState.getProperties().applicationId(), suffix); @@ -223,6 +224,7 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat // L7 ApplicationClusterEndpoint.DnsName l7Name = ApplicationClusterEndpoint.DnsName.sharedNameFrom( + deployState.zone().system(), ClusterSpec.Id.from(getName()), deployState.getProperties().applicationId(), suffix); @@ -242,6 +244,7 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat .forEach(ce -> ce.names().forEach( name -> endpoints.add(ApplicationClusterEndpoint.builder() .scope(ce.scope()) + .weight(Long.valueOf(ce.weight().orElse(1)).intValue()) // Default to weight=1 if not set .sharedL4Routing() .dnsName(ApplicationClusterEndpoint.DnsName.from(name)) .hosts(hosts) diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java index 2016cea02a9..87dd91a8a11 100755 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java @@ -39,9 +39,13 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Objects; +import java.util.OptionalInt; +import java.util.OptionalLong; import java.util.Set; import java.util.stream.Collectors; +import static com.yahoo.config.provision.SystemName.cd; +import static com.yahoo.config.provision.SystemName.main; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasKey; @@ -358,32 +362,66 @@ public class ContainerClusterTest { @Test public void generatesCorrectRoutingInfo() { - - assertNames(ApplicationId.from("t1", "a1", "i1"), + // main system: + assertNames(main, + ApplicationId.from("t1", "a1", "i1"), Set.of(), List.of("search-cluster.i1.a1.t1.endpoint.suffix", "search-cluster--i1--a1--t1.endpoint.suffix")); - assertNames(ApplicationId.from("t1", "a1", "default"), + assertNames(main, + ApplicationId.from("t1", "a1", "default"), Set.of(), List.of("search-cluster.a1.t1.endpoint.suffix", "search-cluster--a1--t1.endpoint.suffix")); - assertNames(ApplicationId.from("t1", "default", "default"), + assertNames(main, + ApplicationId.from("t1", "default", "default"), Set.of(), List.of("search-cluster.default.t1.endpoint.suffix", "search-cluster--default--t1.endpoint.suffix")); - assertNames(ApplicationId.from("t1", "a1", "default"), + assertNames(main, + ApplicationId.from("t1", "a1", "default"), Set.of(new ContainerEndpoint("not-in-this-cluster", ApplicationClusterEndpoint.Scope.global, List.of("foo", "bar"))), List.of("search-cluster.a1.t1.endpoint.suffix", "search-cluster--a1--t1.endpoint.suffix")); - assertNames(ApplicationId.from("t1", "a1", "default"), + assertNames(main, + ApplicationId.from("t1", "a1", "default"), Set.of(new ContainerEndpoint("search-cluster", ApplicationClusterEndpoint.Scope.global, List.of("rotation-1.x.y.z", "rotation-2.x.y.z")), - new ContainerEndpoint("search-cluster", ApplicationClusterEndpoint.Scope.application, List.of("app-rotation.x.y.z"))), + new ContainerEndpoint("search-cluster", ApplicationClusterEndpoint.Scope.application, List.of("app-rotation.x.y.z"), OptionalInt.of(3))), List.of("search-cluster.a1.t1.endpoint.suffix", "search-cluster--a1--t1.endpoint.suffix", "rotation-1.x.y.z", "rotation-2.x.y.z", "app-rotation.x.y.z")); + + // cd system: + assertNames(cd, + ApplicationId.from("t1", "a1", "i1"), + Set.of(), + List.of("search-cluster.cd.i1.a1.t1.endpoint.suffix", "search-cluster--cd--i1--a1--t1.endpoint.suffix")); + + assertNames(cd, + ApplicationId.from("t1", "a1", "default"), + Set.of(), + List.of("search-cluster.cd.a1.t1.endpoint.suffix", "search-cluster--cd--a1--t1.endpoint.suffix")); + + assertNames(cd, + ApplicationId.from("t1", "default", "default"), + Set.of(), + List.of("search-cluster.cd.default.t1.endpoint.suffix", "search-cluster--cd--default--t1.endpoint.suffix")); + + assertNames(cd, + ApplicationId.from("t1", "a1", "default"), + Set.of(new ContainerEndpoint("not-in-this-cluster", ApplicationClusterEndpoint.Scope.global, List.of("foo", "bar"))), + List.of("search-cluster.cd.a1.t1.endpoint.suffix", "search-cluster--cd--a1--t1.endpoint.suffix")); + + assertNames(cd, + ApplicationId.from("t1", "a1", "default"), + Set.of(new ContainerEndpoint("search-cluster", ApplicationClusterEndpoint.Scope.global, List.of("rotation-1.x.y.z", "rotation-2.x.y.z")), + new ContainerEndpoint("search-cluster", ApplicationClusterEndpoint.Scope.application, List.of("app-rotation.x.y.z"), OptionalInt.of(3))), + List.of("search-cluster.cd.a1.t1.endpoint.suffix", "search-cluster--cd--a1--t1.endpoint.suffix", "rotation-1.x.y.z", "rotation-2.x.y.z", "app-rotation.x.y.z")); + } - private void assertNames(ApplicationId appId, Set<ContainerEndpoint> globalEndpoints, List<String> expectedNames) { + private void assertNames(SystemName systemName, ApplicationId appId, Set<ContainerEndpoint> globalEndpoints, List<String> expectedNames) { + Zone zone = new Zone(systemName, Environment.defaultEnvironment(), RegionName.defaultName()); DeployState state = new DeployState.Builder() - .zone(Zone.defaultZone()) + .zone(zone) .endpoints(globalEndpoints) .properties(new TestProperties() .setHostedVespa(true) @@ -396,7 +434,19 @@ public class ContainerClusterTest { cluster.doPrepare(state); List<ApplicationClusterEndpoint> endpoints = cluster.endpoints(); assertEquals(expectedNames.size(), endpoints.size()); - expectedNames.forEach(expected -> assertTrue("Endpoint not matched " + expected, endpoints.stream().anyMatch(e -> Objects.equals(e.dnsName().value(), expected)))); + expectedNames.forEach(expected -> assertTrue("Endpoint not matched " + expected + " was: " + endpoints, endpoints.stream().anyMatch(e -> Objects.equals(e.dnsName().value(), expected)))); + + List<ContainerEndpoint> endpointsWithWeight = + globalEndpoints.stream().filter(endpoint -> endpoint.weight().isPresent()).collect(Collectors.toList()); + endpointsWithWeight.stream() + .filter(ce -> ce.weight().isPresent()) + .forEach(ce -> assertTrue(endpointsMatch(ce, endpoints))); + } + + private boolean endpointsMatch(ContainerEndpoint configuredEndpoint, List<ApplicationClusterEndpoint> clusterEndpoints) { + return clusterEndpoints.stream().anyMatch(e -> + configuredEndpoint.names().contains(e.dnsName().value()) && + configuredEndpoint.weight().getAsInt() == e.weight()); } private void verifyTesterApplicationInstalledBundles(Zone zone, List<String> expectedBundleNames) { diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializer.java index 55986e71b3d..3bbf98357f5 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializer.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializer.java @@ -30,6 +30,7 @@ public class ContainerEndpointSerializer { private static final String clusterIdField = "clusterId"; private static final String scopeField = "scope"; private static final String namesField = "names"; + private static final String weightField = "weight"; private ContainerEndpointSerializer() {} @@ -39,7 +40,7 @@ public class ContainerEndpointSerializer { // TODO: Remove default assignment after 7.500 final var scope = SlimeUtils.optionalString(inspector.field(scopeField)).orElse(ApplicationClusterEndpoint.Scope.global.name()); final var namesInspector = inspector.field(namesField); - + final var weight = SlimeUtils.optionalInteger(inspector.field(weightField)); if (clusterId.isEmpty()) { throw new IllegalStateException("'clusterId' missing on serialized ContainerEndpoint"); } @@ -59,7 +60,7 @@ public class ContainerEndpointSerializer { names.add(containerName); }); - return new ContainerEndpoint(clusterId, ApplicationClusterEndpoint.Scope.valueOf(scope), names); + return new ContainerEndpoint(clusterId, ApplicationClusterEndpoint.Scope.valueOf(scope), names, weight); } public static List<ContainerEndpoint> endpointListFromSlime(Slime slime) { @@ -81,7 +82,7 @@ public class ContainerEndpointSerializer { public static void endpointToSlime(Cursor cursor, ContainerEndpoint endpoint) { cursor.setString(clusterIdField, endpoint.clusterId()); cursor.setString(scopeField, endpoint.scope().name()); - + endpoint.weight().ifPresent(w -> cursor.setLong(weightField, w)); final var namesInspector = cursor.setArray(namesField); endpoint.names().forEach(namesInspector::addString); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java index 2c97d0b9382..cd824967fc3 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/PrepareParamsTest.java @@ -43,6 +43,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.OptionalInt; +import java.util.OptionalLong; import java.util.stream.Collectors; import static org.hamcrest.CoreMatchers.is; @@ -90,18 +92,21 @@ public class PrepareParamsTest { public void testCorrectParsingWithContainerEndpoints() throws IOException { var endpoints = List.of(new ContainerEndpoint("qrs1", ApplicationClusterEndpoint.Scope.global, List.of("c1.example.com", - "c2.example.com")), + "c2.example.com"), OptionalInt.of(3)), new ContainerEndpoint("qrs2",ApplicationClusterEndpoint.Scope.global, List.of("c3.example.com", "c4.example.com"))); var param = "[\n" + " {\n" + " \"clusterId\": \"qrs1\",\n" + - " \"names\": [\"c1.example.com\", \"c2.example.com\"]\n" + + " \"names\": [\"c1.example.com\", \"c2.example.com\"],\n" + + " \"scope\": \"global\",\n" + + " \"weight\": 3\n" + " },\n" + " {\n" + " \"clusterId\": \"qrs2\",\n" + - " \"names\": [\"c3.example.com\", \"c4.example.com\"]\n" + + " \"names\": [\"c3.example.com\", \"c4.example.com\"],\n" + + " \"scope\": \"global\"\n" + " }\n" + "]"; diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializerTest.java index 2d767cfded4..2b746a9c1c6 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializerTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/ContainerEndpointSerializerTest.java @@ -7,6 +7,8 @@ import com.yahoo.slime.Slime; import org.junit.Test; import java.util.List; +import java.util.OptionalInt; +import java.util.OptionalLong; import static org.junit.Assert.assertEquals; @@ -50,7 +52,7 @@ public class ContainerEndpointSerializerTest { @Test public void writeReadSingleEndpoint() { - final var endpoint = new ContainerEndpoint("foo", ApplicationClusterEndpoint.Scope.global, List.of("a", "b")); + final var endpoint = new ContainerEndpoint("foo", ApplicationClusterEndpoint.Scope.global, List.of("a", "b"), OptionalInt.of(1)); final var serialized = new Slime(); ContainerEndpointSerializer.endpointToSlime(serialized.setObject(), endpoint); final var deserialized = ContainerEndpointSerializer.endpointFromSlime(serialized.get()); |