diff options
author | jonmv <venstad@gmail.com> | 2023-01-18 18:59:51 +0100 |
---|---|---|
committer | jonmv <venstad@gmail.com> | 2023-01-18 18:59:51 +0100 |
commit | 669ae1bc6572c198e609bc20cacbdc592e8e2731 (patch) | |
tree | 0ecf6e5d73433301117b6ecebdd241eec472460a /config-provisioning/src | |
parent | c47ed544a31a6b56f518901247212a47d8eb9d31 (diff) |
Revert "Merge pull request #25624 from vespa-engine/revert-25617-jonmv/private-endpoints"
This reverts commit c47ed544a31a6b56f518901247212a47d8eb9d31, reversing
changes made to e0191b4d49048f9398395dc8c1c60dfcb383f705.
Diffstat (limited to 'config-provisioning/src')
6 files changed, 197 insertions, 53 deletions
diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java index 213166447ca..9e8388b6442 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterMembership.java @@ -20,7 +20,7 @@ public class ClusterMembership { private final String stringValue; private ClusterMembership(String stringValue, Version vespaVersion, Optional<DockerImage> dockerImageRepo, - LoadBalancerSettings loadBalancerSettings) { + ZoneEndpoint zoneEndpoint) { String[] components = stringValue.split("/"); if (components.length < 4) throw new RuntimeException("Could not parse '" + stringValue + "' to a cluster membership. " + @@ -49,7 +49,7 @@ public class ClusterMembership { .exclusive(exclusive) .combinedId(combinedId.map(ClusterSpec.Id::from)) .dockerImageRepository(dockerImageRepo) - .loadBalancerSettings(loadBalancerSettings) + .loadBalancerSettings(zoneEndpoint) .stateful(stateful) .build(); this.index = Integer.parseInt(components[3]); @@ -125,12 +125,12 @@ public class ClusterMembership { public String toString() { return stringValue(); } public static ClusterMembership from(String stringValue, Version vespaVersion, Optional<DockerImage> dockerImageRepo) { - return from(stringValue, vespaVersion, dockerImageRepo, LoadBalancerSettings.empty); + return from(stringValue, vespaVersion, dockerImageRepo, ZoneEndpoint.defaultEndpoint); } public static ClusterMembership from(String stringValue, Version vespaVersion, Optional<DockerImage> dockerImageRepo, - LoadBalancerSettings loadBalancerSettings) { - return new ClusterMembership(stringValue, vespaVersion, dockerImageRepo, loadBalancerSettings); + ZoneEndpoint zoneEndpoint) { + return new ClusterMembership(stringValue, vespaVersion, dockerImageRepo, zoneEndpoint); } public static ClusterMembership from(ClusterSpec cluster, int index) { diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java index 153b305dc01..196255a8342 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/ClusterSpec.java @@ -24,12 +24,12 @@ public final class ClusterSpec { private final boolean exclusive; private final Optional<Id> combinedId; private final Optional<DockerImage> dockerImageRepo; - private final LoadBalancerSettings loadBalancerSettings; + private final ZoneEndpoint zoneEndpoint; private final boolean stateful; private ClusterSpec(Type type, Id id, Optional<Group> groupId, Version vespaVersion, boolean exclusive, Optional<Id> combinedId, Optional<DockerImage> dockerImageRepo, - LoadBalancerSettings loadBalancerSettings, boolean stateful) { + ZoneEndpoint zoneEndpoint, boolean stateful) { this.type = type; this.id = id; this.groupId = groupId; @@ -47,7 +47,7 @@ public final class ClusterSpec { if (type.isContent() && !stateful) { throw new IllegalArgumentException("Cluster of type " + type + " must be stateful"); } - this.loadBalancerSettings = Objects.requireNonNull(loadBalancerSettings); + this.zoneEndpoint = Objects.requireNonNull(zoneEndpoint); this.stateful = stateful; } @@ -63,8 +63,8 @@ public final class ClusterSpec { /** Returns the docker image (repository + vespa version) we want this cluster to run */ public Optional<String> dockerImage() { return dockerImageRepo.map(repo -> repo.withTag(vespaVersion).asString()); } - /** Returns any additional load balancer settings for application container clusters. */ - public LoadBalancerSettings loadBalancerSettings() { return loadBalancerSettings; } + /** Returns any additional zone endpoint settings for application container clusters. */ + public ZoneEndpoint zoneEndpoint() { return zoneEndpoint; } /** Returns the version of Vespa that we want this cluster to run */ public Version vespaVersion() { return vespaVersion; } @@ -87,15 +87,15 @@ public final class ClusterSpec { public boolean isStateful() { return stateful; } public ClusterSpec with(Optional<Group> newGroup) { - return new ClusterSpec(type, id, newGroup, vespaVersion, exclusive, combinedId, dockerImageRepo, loadBalancerSettings, stateful); + return new ClusterSpec(type, id, newGroup, vespaVersion, exclusive, combinedId, dockerImageRepo, zoneEndpoint, stateful); } public ClusterSpec withExclusivity(boolean exclusive) { - return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, combinedId, dockerImageRepo, loadBalancerSettings, stateful); + return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, combinedId, dockerImageRepo, zoneEndpoint, stateful); } public ClusterSpec exclusive(boolean exclusive) { - return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, combinedId, dockerImageRepo, loadBalancerSettings, stateful); + return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, combinedId, dockerImageRepo, zoneEndpoint, stateful); } /** Creates a ClusterSpec when requesting a cluster */ @@ -119,7 +119,7 @@ public final class ClusterSpec { private Version vespaVersion; private boolean exclusive = false; private Optional<Id> combinedId = Optional.empty(); - private LoadBalancerSettings loadBalancerSettings = LoadBalancerSettings.empty; + private ZoneEndpoint zoneEndpoint = ZoneEndpoint.defaultEndpoint; private boolean stateful; private Builder(Type type, Id id, boolean specification) { @@ -135,7 +135,7 @@ public final class ClusterSpec { if (vespaVersion == null) throw new IllegalArgumentException("vespaVersion is required to be set when creating a ClusterSpec with specification()"); } else if (groupId.isPresent()) throw new IllegalArgumentException("groupId is not allowed to be set when creating a ClusterSpec with request()"); - return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, combinedId, dockerImageRepo, loadBalancerSettings, stateful); + return new ClusterSpec(type, id, groupId, vespaVersion, exclusive, combinedId, dockerImageRepo, zoneEndpoint, stateful); } public Builder group(Group groupId) { @@ -168,8 +168,8 @@ public final class ClusterSpec { return this; } - public Builder loadBalancerSettings(LoadBalancerSettings loadBalancerSettings) { - this.loadBalancerSettings = loadBalancerSettings; + public Builder loadBalancerSettings(ZoneEndpoint zoneEndpoint) { + this.zoneEndpoint = zoneEndpoint; return this; } @@ -198,12 +198,12 @@ public final class ClusterSpec { vespaVersion.equals(that.vespaVersion) && combinedId.equals(that.combinedId) && dockerImageRepo.equals(that.dockerImageRepo) && - loadBalancerSettings.equals(that.loadBalancerSettings); + zoneEndpoint.equals(that.zoneEndpoint); } @Override public int hashCode() { - return Objects.hash(type, id, groupId, vespaVersion, exclusive, combinedId, dockerImageRepo, loadBalancerSettings, stateful); + return Objects.hash(type, id, groupId, vespaVersion, exclusive, combinedId, dockerImageRepo, zoneEndpoint, stateful); } /** diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/LoadBalancerSettings.java b/config-provisioning/src/main/java/com/yahoo/config/provision/LoadBalancerSettings.java deleted file mode 100644 index 723de25fa87..00000000000 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/LoadBalancerSettings.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.yahoo.config.provision; - -import java.util.List; - -/** - * Settings for a load balancer provisioned for an application container cluster. - * - * @author jonmv - */ -public record LoadBalancerSettings(List<String> allowedUrns) { - - public static final LoadBalancerSettings empty = new LoadBalancerSettings(List.of()); - - public LoadBalancerSettings(List<String> allowedUrns) { - this.allowedUrns = List.copyOf(allowedUrns); - } - - public boolean isEmpty() { return allowedUrns.isEmpty(); } - -} diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/ZoneEndpoint.java b/config-provisioning/src/main/java/com/yahoo/config/provision/ZoneEndpoint.java new file mode 100644 index 00000000000..10e22f8df06 --- /dev/null +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/ZoneEndpoint.java @@ -0,0 +1,132 @@ +package com.yahoo.config.provision; + +import ai.vespa.validation.Validation; + +import java.util.List; +import java.util.Objects; + +/** + * Settings for a zone endpoint of a deployment. + * + * TODO: Fix isEmpty + * Inline empty and constructor + * + * @author jonmv + */ +public class ZoneEndpoint { + + public static final ZoneEndpoint defaultEndpoint = new ZoneEndpoint(true, false, List.of()); + + private final boolean isPublicEndpoint; + private final boolean isPrivateEndpoint; + private final List<AllowedUrn> allowedUrns; + + public ZoneEndpoint(List<String> allowedUrns) { + this(true, true, allowedUrns.stream().map(arn -> new AllowedUrn(AccessType.awsPrivateLink, arn)).toList()); + } + + public ZoneEndpoint(boolean isPublicEndpoint, boolean isPrivateEndpoint, List<AllowedUrn> allowedUrns) { + if ( ! allowedUrns.isEmpty() && ! isPrivateEndpoint) + throw new IllegalArgumentException("cannot list allowed urns, without also enabling private visibility"); + this.isPublicEndpoint = isPublicEndpoint; + this.isPrivateEndpoint = isPrivateEndpoint; + this.allowedUrns = List.copyOf(allowedUrns); + } + + /** Whether this has an endpoint which is visible from the public internet. */ + public boolean isPublicEndpoint() { + return isPublicEndpoint; + } + + /** Whether this has an endpoint which is visible through private DNS of the cloud. */ + public boolean isPrivateEndpoint() { + return isPrivateEndpoint; + } + + /** List of allowed URNs, for specified private access types. */ + public List<AllowedUrn> allowedUrns() { + return allowedUrns; + } + + /** List of URNs for the given access type. */ + public List<String> allowedUrnsWith(AccessType type) { + return allowedUrns.stream().filter(urn -> urn.type == type).map(AllowedUrn::urn).toList(); + } + + public boolean isDefault() { + return equals(defaultEndpoint); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ZoneEndpoint that = (ZoneEndpoint) o; + return isPublicEndpoint == that.isPublicEndpoint && isPrivateEndpoint == that.isPrivateEndpoint && allowedUrns.equals(that.allowedUrns); + } + + @Override + public int hashCode() { + return Objects.hash(isPublicEndpoint, isPrivateEndpoint, allowedUrns); + } + + @Override + public String toString() { + return "ZoneEndpoint{" + + "isPublicEndpoint=" + isPublicEndpoint + + ", isPrivateEndpoint=" + isPrivateEndpoint + + ", allowedUrns=" + allowedUrns + + '}'; + } + + public enum AccessType { + awsPrivateLink, + gcpServiceConnect, + } + + /** A URN allowed to access this (private) endpoint, through a {@link AccessType} method. */ + public static class AllowedUrn { + + private final AccessType type; + private final String urn; + + public AllowedUrn(AccessType type, String urn) { + this.type = Objects.requireNonNull(type); + this.urn = Validation.requireNonBlank(urn, "URN"); + } + + /** Type of private connection. */ + public AccessType type() { + return type; + } + + /** URN allowed to access this private endpoint. */ + public String urn() { + return urn; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AllowedUrn that = (AllowedUrn) o; + return type == that.type && urn.equals(that.urn); + } + + @Override + public int hashCode() { + return Objects.hash(type, urn); + } + + @Override + public String toString() { + return "'" + urn + "' through '" + + switch (type) { + case awsPrivateLink -> "aws-private-link"; + case gcpServiceConnect -> "gcp-service-connect"; + } + "'"; + } + + } + +} diff --git a/config-provisioning/src/main/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializer.java b/config-provisioning/src/main/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializer.java index 01bb0ca45ff..64e8a7feb94 100644 --- a/config-provisioning/src/main/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializer.java +++ b/config-provisioning/src/main/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializer.java @@ -6,8 +6,10 @@ import com.yahoo.config.provision.AllocatedHosts; import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.HostSpec; -import com.yahoo.config.provision.LoadBalancerSettings; import com.yahoo.config.provision.NodeResources; +import com.yahoo.config.provision.ZoneEndpoint; +import com.yahoo.config.provision.ZoneEndpoint.AllowedUrn; +import com.yahoo.config.provision.ZoneEndpoint.AccessType; import com.yahoo.slime.ArrayTraverser; import com.yahoo.slime.Cursor; import com.yahoo.slime.Inspector; @@ -40,8 +42,12 @@ public class AllocatedHostsSerializer { private static final String hostSpecKey = "hostSpec"; private static final String hostSpecHostNameKey = "hostName"; private static final String hostSpecMembershipKey = "membership"; - private static final String loadBalancerSettingsKey = "loadBalancerSettings"; - private static final String allowedUrnsKey = "allowedUrns"; + private static final String loadBalancerSettingsKey = "zoneEndpoint"; + private static final String publicField = "public"; + private static final String privateField = "private"; + private static final String allowedUrnsField = "allowedUrns"; + private static final String accessTypeField = "type"; + private static final String urnField = "urn"; private static final String realResourcesKey = "realResources"; private static final String advertisedResourcesKey = "advertisedResources"; @@ -85,9 +91,8 @@ public class AllocatedHostsSerializer { host.membership().ifPresent(membership -> { object.setString(hostSpecMembershipKey, membership.stringValue()); object.setString(hostSpecVespaVersionKey, membership.cluster().vespaVersion().toFullString()); - if ( ! membership.cluster().loadBalancerSettings().isEmpty()) - membership.cluster().loadBalancerSettings().allowedUrns() - .forEach(object.setObject(loadBalancerSettingsKey).setArray(allowedUrnsKey)::addString); + if ( ! membership.cluster().zoneEndpoint().isDefault()) + toSlime(object.setObject(loadBalancerSettingsKey), membership.cluster().zoneEndpoint()); membership.cluster().dockerImageRepo().ifPresent(repo -> object.setString(hostSpecDockerImageRepoKey, repo.untagged())); }); toSlime(host.realResources(), object.setObject(realResourcesKey)); @@ -222,13 +227,41 @@ public class AllocatedHostsSerializer { object.field(hostSpecDockerImageRepoKey).valid() ? Optional.of(DockerImage.fromString(object.field(hostSpecDockerImageRepoKey).asString())) : Optional.empty(), - object.field(loadBalancerSettingsKey).valid() - ? new LoadBalancerSettings(SlimeUtils.entriesStream(object.field(loadBalancerSettingsKey).field(allowedUrnsKey)) - .map(Inspector::asString) - .toList()) - : LoadBalancerSettings.empty); + zoneEndpoint(object.field(loadBalancerSettingsKey))); } + private static void toSlime(Cursor settingsObject, ZoneEndpoint settings) { + settingsObject.setBool(publicField, settings.isPublicEndpoint()); + settingsObject.setBool(privateField, settings.isPrivateEndpoint()); + if (settings.isPrivateEndpoint()) { + Cursor allowedUrnsArray = settingsObject.setArray(allowedUrnsField); + for (AllowedUrn urn : settings.allowedUrns()) { + Cursor urnObject = allowedUrnsArray.addObject(); + urnObject.setString(urnField, urn.urn()); + urnObject.setString(accessTypeField, + switch (urn.type()) { + case awsPrivateLink -> "awsPrivateLink"; + case gcpServiceConnect -> "gcpServiceConnect"; + }); + } + } + } + + private static ZoneEndpoint zoneEndpoint(Inspector settingsObject) { + if ( ! settingsObject.valid()) return ZoneEndpoint.defaultEndpoint; + return new ZoneEndpoint(settingsObject.field(publicField).asBool(), + settingsObject.field(privateField).asBool(), + SlimeUtils.entriesStream(settingsObject.field(allowedUrnsField)) + .map(urnObject -> new AllowedUrn(switch (urnObject.field(accessTypeField).asString()) { + case "awsPrivateLink" -> AccessType.awsPrivateLink; + case "gcpServiceConnect" -> AccessType.gcpServiceConnect; + default -> throw new IllegalArgumentException("unknown service access type in '" + urnObject + "'"); + }, + urnObject.field(urnField).asString())) + .toList()); + } + + private static Optional<String> optionalString(Inspector inspector) { if ( ! inspector.valid()) return Optional.empty(); return Optional.of(inspector.asString()); diff --git a/config-provisioning/src/test/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializerTest.java b/config-provisioning/src/test/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializerTest.java index bcb3b8cd4aa..3404d7ed55e 100644 --- a/config-provisioning/src/test/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializerTest.java +++ b/config-provisioning/src/test/java/com/yahoo/config/provision/serialization/AllocatedHostsSerializerTest.java @@ -6,9 +6,9 @@ import com.yahoo.config.provision.AllocatedHosts; import com.yahoo.config.provision.ClusterMembership; import com.yahoo.config.provision.DockerImage; import com.yahoo.config.provision.HostSpec; -import com.yahoo.config.provision.LoadBalancerSettings; import com.yahoo.config.provision.NetworkPorts; import com.yahoo.config.provision.NodeResources; +import com.yahoo.config.provision.ZoneEndpoint; import org.junit.jupiter.api.Test; import java.io.IOException; @@ -20,7 +20,6 @@ import java.util.Set; import static com.yahoo.config.provision.serialization.AllocatedHostsSerializer.fromJson; import static com.yahoo.config.provision.serialization.AllocatedHostsSerializer.toJson; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; /** * @author bratseth @@ -69,7 +68,7 @@ public class AllocatedHostsSerializerTest { bigSlowDiskSpeedNode, anyDiskSpeedNode, ClusterMembership.from("container/test/0/0", Version.fromString("6.73.1"), - Optional.empty(), new LoadBalancerSettings(List.of("burn"))), + Optional.empty(), new ZoneEndpoint(List.of("burn"))), Optional.empty(), Optional.empty(), Optional.empty())); |