// 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 com.yahoo.config.provision.SystemName; 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 { @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, exclusive} private final DnsName dnsName; private final Scope scope; private final RoutingMethod routingMethod; private final int weight; private final List hostNames; private final String clusterId; private ApplicationClusterEndpoint(DnsName dnsName, Scope scope, RoutingMethod routingMethod, int weight, List hostNames, String clusterId) { this.dnsName = dnsName; this.scope = scope; this.routingMethod = routingMethod; this.weight = weight; this.hostNames = List.copyOf(hostNames); this.clusterId = clusterId; } public DnsName dnsName() { return dnsName; } public Scope scope() { return scope; } public RoutingMethod routingMethod() { return routingMethod; } public int weight() { return weight; } public List hostNames() { return hostNames; } public String clusterId() { return clusterId; } public static Builder builder() { return new Builder(); } public static class Builder { private DnsName dnsName; private Scope scope; private RoutingMethod routingMethod; private int weigth = 1; private List hosts; private String clusterId; 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 routingMethod(RoutingMethod routingMethod) { this.routingMethod = routingMethod; return this; } public Builder weight(int weigth) { this.weigth = weigth; return this; } public Builder hosts(List hosts) { this.hosts = List.copyOf(hosts); return this; } public Builder clusterId(String clusterId) { this.clusterId = clusterId; return this; } public ApplicationClusterEndpoint build() { return new ApplicationClusterEndpoint(dnsName, scope, routingMethod, weigth, hosts, clusterId); } } public static class DnsName implements Comparable { private static final int MAX_LABEL_LENGTH = 63; private final String name; private DnsName(String name) { this.name = name; } public String value() { return name; } 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(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(".")); return new DnsName(name + suffix); } public static DnsName from(String name) { return new DnsName(name); } private static Stream 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() ); } /** * 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); } private static String systemPart(SystemName systemName) { return "cd".equals(systemName.value()) ? systemName.value() : null; } @Override public String toString() { return "DnsName{" + "name='" + name + '\'' + '}'; } @Override public int compareTo(DnsName o) { return name.compareTo(o.name); } } }