diff options
Diffstat (limited to 'config-model-api/src/main')
6 files changed, 198 insertions, 3 deletions
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java b/config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java index 4ca9ee1dc2f..6f77dce8fc5 100644 --- a/config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/xml/DeploymentSpecXmlReader.java @@ -281,6 +281,7 @@ public class DeploymentSpecXmlReader { List<Endpoint.Target> targets = new ArrayList<>(); if (level == Endpoint.Level.application) { String region = requireStringAttribute("region", endpointElement); + int weightSum = 0; for (var instanceElement : XML.getChildren(endpointElement, "instance")) { String instanceName = instanceElement.getTextContent(); String weightFromAttribute = requireStringAttribute("weight", instanceElement); @@ -291,10 +292,12 @@ public class DeploymentSpecXmlReader { } catch (NumberFormatException e) { throw new IllegalArgumentException(msgPrefix + "invalid weight value '" + weightFromAttribute + "'"); } + weightSum += weight; targets.add(new Endpoint.Target(RegionName.from(region), InstanceName.from(instanceName), weight)); } + if (weightSum == 0) illegal(msgPrefix + "sum of all weights must be positive, got " + weightSum); } else { if (stringAttribute("region", endpointElement).isPresent()) illegal(msgPrefix + "invalid 'region' attribute"); for (var regionElement : XML.getChildren(endpointElement, "region")) { 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 new file mode 100644 index 00000000000..a91f95d71b1 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationClusterEndpoint.java @@ -0,0 +1,172 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +package com.yahoo.config.model.api; + +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.ClusterSpec; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Represents one endpoint for an application cluster + * + * @author mortent + */ +public class ApplicationClusterEndpoint { + public enum Scope {application, global, zone} + + public enum RoutingMethod {shared, sharedLayer4} + + private final DnsName dnsName; + private final Scope scope; + private final RoutingMethod routingMethod; + private final int weight; + private final List<String> hostNames; + + public ApplicationClusterEndpoint(DnsName dnsName, Scope scope, RoutingMethod routingMethod, int weight, List<String> hostNames) { + this.dnsName = dnsName; + this.scope = scope; + this.routingMethod = routingMethod; + this.weight = weight; + this.hostNames = List.copyOf(hostNames); + } + + public DnsName dnsName() { + return dnsName; + } + + public Scope scope() { + return scope; + } + + public RoutingMethod routingMethod() { + return routingMethod; + } + + public int weight() { + return weight; + } + + public List<String> hostNames() { + return hostNames; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + + private DnsName dnsName; + private Scope scope; + private RoutingMethod routingMethod; + private int weigth = 0; + private List<String> hosts; + + public Builder dnsName(DnsName name) { + this.dnsName = name; + return this; + } + + public Builder zoneScope() { + this.scope = Scope.zone; + return this; + } + + public Builder scope(Scope scope) { + this.scope = scope; + return this; + } + + public Builder sharedRouting() { + this.routingMethod = RoutingMethod.shared; + return this; + } + + public Builder sharedL4Routing() { + this.routingMethod = RoutingMethod.sharedLayer4; + return this; + } + + public Builder weight(int weigth) { + this.weigth = weigth; + return this; + } + + public Builder hosts(List<String> hosts) { + this.hosts = List.copyOf(hosts); + return this; + } + + public ApplicationClusterEndpoint build() { + return new ApplicationClusterEndpoint(dnsName, scope, routingMethod, weigth, hosts); + } + } + + public static class DnsName { + private static final int MAX_LABEL_LENGTH = 63; + + private final String name; + + private DnsName(String name) { + this.name = name; + } + + public String value() { + return name; + } + + // TODO: remove + public static DnsName sharedNameFrom(ClusterSpec.Id cluster, ApplicationId applicationId, String suffix) { + String name = dnsParts(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) + .filter(Objects::nonNull) // remove null values that were "default" + .map(DnsName::sanitize) + .collect(Collectors.joining(".")); + return new DnsName(name + suffix); + } + + public static DnsName from(String name) { + return new DnsName(name); + } + + private static Stream<String> dnsParts(ClusterSpec.Id cluster, ApplicationId applicationId) { + return Stream.of( + nullIfDefault(cluster.value()), + nullIfDefault(applicationId.instance().value()), + applicationId.application().value(), + applicationId.tenant().value() + ); + } + + /** + * Remove any invalid characters from the hostnames + */ + private static String sanitize(String id) { + return shortenIfNeeded(id.toLowerCase() + .replace('_', '-') + .replaceAll("[^a-z0-9-]*", "")); + } + + /** + * Truncate the given string at the front so its length does not exceed 63 characters. + */ + private static String shortenIfNeeded(String id) { + return id.substring(Math.max(0, id.length() - MAX_LABEL_LENGTH)); + } + + private static String nullIfDefault(String string) { + return Optional.of(string).filter(s -> !s.equals("default")).orElse(null); + } + } +} diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationClusterInfo.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationClusterInfo.java new file mode 100644 index 00000000000..2cd2e980761 --- /dev/null +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ApplicationClusterInfo.java @@ -0,0 +1,9 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +package com.yahoo.config.model.api; + +import java.util.List; + +public interface ApplicationClusterInfo { + List<ApplicationClusterEndpoint> endpoints(); +} 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 2b2b5e2c404..a114f9d40ef 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 @@ -14,10 +14,12 @@ import java.util.Objects; public class ContainerEndpoint { private final String clusterId; + private final ApplicationClusterEndpoint.Scope scope; private final List<String> names; - public ContainerEndpoint(String clusterId, List<String> names) { + public ContainerEndpoint(String clusterId, ApplicationClusterEndpoint.Scope scope, List<String> names) { this.clusterId = Objects.requireNonNull(clusterId); + this.scope = Objects.requireNonNull(scope); this.names = List.copyOf(Objects.requireNonNull(names)); } @@ -29,23 +31,28 @@ public class ContainerEndpoint { return names; } + public ApplicationClusterEndpoint.Scope scope() { + return scope; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ContainerEndpoint that = (ContainerEndpoint) o; return Objects.equals(clusterId, that.clusterId) && + Objects.equals(scope, that.scope) && Objects.equals(names, that.names); } @Override public int hashCode() { - return Objects.hash(clusterId, names); + return Objects.hash(clusterId, names, scope); } @Override public String toString() { - return String.format("container endpoint %s -> %s", clusterId, names); + return String.format("container endpoint %s -> %s [scope=%s]", clusterId, names, scope); } } diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/Model.java b/config-model-api/src/main/java/com/yahoo/config/model/api/Model.java index c1248ff556c..a7fd48bfea8 100644 --- a/config-model-api/src/main/java/com/yahoo/config/model/api/Model.java +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/Model.java @@ -79,4 +79,6 @@ public interface Model { /** Returns the set of document types in each cluster, that have an index for one of more fields. */ default Map<String, Set<String>> indexedDocumentTypesByCluster() { return Map.of(); } + /** Returns the set of container clusters */ + default Set<ApplicationClusterInfo> applicationClusterInfo() { return Set.of(); } } diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java index ff612ffc2b0..c5781c2805d 100644 --- a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java @@ -148,6 +148,8 @@ public interface ModelContext { default List<X509Certificate> operatorCertificates() { return List.of(); } default List<String> tlsCiphersOverride() { return List.of(); } + + default List<String> zoneDnsSuffixes() { return List.of(); } } @Retention(RetentionPolicy.RUNTIME) |