diff options
Diffstat (limited to 'controller-server/src/main/java/com')
10 files changed, 101 insertions, 124 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java index 1c5f98cb7f9..14f2b38f24a 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java @@ -2,13 +2,16 @@ package com.yahoo.vespa.hosted.controller.deployment; import ai.vespa.http.DomainName; -import com.google.common.net.InetAddresses; +import ai.vespa.http.HttpURL; import com.yahoo.component.Version; import com.yahoo.config.application.api.DeploymentSpec; import com.yahoo.config.application.api.Notifications; import com.yahoo.config.application.api.Notifications.When; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.ClusterSpec; +import com.yahoo.config.provision.EndpointsChecker; +import com.yahoo.config.provision.EndpointsChecker.Availability; +import com.yahoo.config.provision.EndpointsChecker.Status; import com.yahoo.config.provision.HostName; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.zone.RoutingMethod; @@ -45,6 +48,7 @@ import com.yahoo.yolean.Exceptions; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.io.UncheckedIOException; +import java.net.InetAddress; import java.security.cert.CertificateExpiredException; import java.security.cert.CertificateNotYetValidException; import java.security.cert.X509Certificate; @@ -87,6 +91,7 @@ import static com.yahoo.vespa.hosted.controller.deployment.Step.deployReal; import static com.yahoo.vespa.hosted.controller.deployment.Step.deployTester; import static com.yahoo.vespa.hosted.controller.deployment.Step.installTester; import static com.yahoo.vespa.hosted.controller.deployment.Step.report; +import static com.yahoo.yolean.Exceptions.uncheck; import static java.util.Objects.requireNonNull; import static java.util.logging.Level.FINE; import static java.util.logging.Level.INFO; @@ -351,13 +356,13 @@ public class InternalStepRunner implements StepRunner { } if (summary.converged()) { controller.jobController().locked(id, lockedRun -> lockedRun.withSummary(null)); - if (endpointsAvailable(id.application(), id.type().zone(), logger)) { - if (containersAreUp(id.application(), id.type().zone(), logger)) { + Availability availability = endpointsAvailable(id.application(), id.type().zone(), logger); + if (availability.status() == Status.available) { logger.log("Installation succeeded!"); return Optional.of(running); - } } - else if (timedOut(id, deployment.get(), timeouts.endpoint())) { + logger.log(availability.message()); + if (availability.status() == Status.endpointsUnavailable && timedOut(id, deployment.get(), timeouts.endpoint())) { logger.log(WARNING, "Endpoints failed to show up within " + timeouts.endpoint().toMinutes() + " minutes!"); return Optional.of(error); } @@ -476,21 +481,6 @@ public class InternalStepRunner implements StepRunner { return Optional.empty(); } - /** Returns true iff all calls to endpoint in the deployment give 100 consecutive 200 OK responses on /status.html. */ - private boolean containersAreUp(ApplicationId id, ZoneId zoneId, DualLogger logger) { - var endpoints = controller.routing().readTestRunnerEndpointsOf(Set.of(new DeploymentId(id, zoneId))); - if ( ! endpoints.containsKey(zoneId)) - return false; - - return endpoints.get(zoneId).parallelStream().allMatch(endpoint -> { - boolean ready = controller.jobController().cloud().ready(endpoint.url()); - if (!ready) { - logger.log("Failed to get 100 consecutive OKs from " + endpoint); - } - return ready; - }); - } - /** Returns true iff all containers in the tester deployment give 100 consecutive 200 OK responses on /status.html. */ private boolean testerContainersAreUp(ApplicationId id, ZoneId zoneId, DualLogger logger) { DeploymentId deploymentId = new DeploymentId(id, zoneId); @@ -502,50 +492,25 @@ public class InternalStepRunner implements StepRunner { } } - private boolean endpointsAvailable(ApplicationId id, ZoneId zone, DualLogger logger) { + private Availability endpointsAvailable(ApplicationId id, ZoneId zone, DualLogger logger) { DeploymentId deployment = new DeploymentId(id, zone); Map<ZoneId, List<Endpoint>> endpoints = controller.routing().readTestRunnerEndpointsOf(Set.of(deployment)); - if ( ! endpoints.containsKey(zone)) { - logger.log("Endpoints not yet ready."); - return false; - } - for (var endpoint : endpoints.get(zone)) { - DomainName endpointName = DomainName.of(endpoint.dnsName()); - var ipAddress = controller.jobController().cloud().resolveHostName(endpointName); - if (ipAddress.isEmpty()) { - logger.log(INFO, "DNS lookup yielded no IP address for '" + endpointName + "'."); - return false; - } - DeploymentRoutingContext context = controller.routing().of(deployment); - if (context.routingMethod() == RoutingMethod.exclusive) { - RoutingPolicy policy = context.routingPolicy(ClusterSpec.Id.from(endpoint.name())) - .orElseThrow(() -> new IllegalStateException(endpoint + " has no matching policy")); - if (policy.ipAddress().isPresent()) { - if (ipAddress.equals(policy.ipAddress().map(InetAddresses::forString))) continue; - logger.log(INFO, "IP address of '" + endpointName + "' (" + - ipAddress.map(InetAddresses::toAddrString).get() + ") and load balancer " - + "' (" + policy.ipAddress().orElseThrow() + ") are not equal"); - return false; - } - - var cNameValue = controller.jobController().cloud().resolveCname(endpointName); - if ( ! cNameValue.map(policy.canonicalName().get()::equals).orElse(false)) { - logger.log(INFO, "CNAME '" + endpointName + "' points at " + - cNameValue.map(name -> "'" + name + "'").orElse("nothing") + - " but should point at load balancer '" + policy.canonicalName() + "'"); - return false; - } - var loadBalancerAddress = controller.jobController().cloud().resolveHostName(policy.canonicalName().get()); - if ( ! loadBalancerAddress.equals(ipAddress)) { - logger.log(INFO, "IP address of CNAME '" + endpointName + "' (" + ipAddress.get() + ") and load balancer '" + - policy.canonicalName().get() + "' (" + loadBalancerAddress.orElse(null) + ") are not equal"); - return false; - } - } - } - logEndpoints(endpoints, logger); - return true; + DeploymentRoutingContext context = controller.routing().of(deployment); + boolean resolveEndpoints = context.routingMethod() == RoutingMethod.exclusive; + return controller.serviceRegistry().testerCloud().verifyEndpoints( + deployment, + endpoints.getOrDefault(zone, List.of()) + .stream() + .map(endpoint -> { + ClusterSpec.Id cluster = ClusterSpec.Id.from(endpoint.name()); + RoutingPolicy policy = context.routingPolicy(cluster).get(); + return new EndpointsChecker.Endpoint(cluster, + HttpURL.from(endpoint.url()), + policy.ipAddress().filter(__ -> resolveEndpoints).map(uncheck(InetAddress::getByName)), + policy.canonicalName().filter(__ -> resolveEndpoints), + policy.isPublic()); + }).toList()); } private void logEndpoints(Map<ZoneId, List<Endpoint>> zoneEndpoints, DualLogger logger) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/AbstractNameServiceRequest.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/AbstractNameServiceRequest.java new file mode 100644 index 00000000000..9d21f5b26bd --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/AbstractNameServiceRequest.java @@ -0,0 +1,33 @@ +package com.yahoo.vespa.hosted.controller.dns; + +import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName; +import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId; + +import java.util.Optional; + +import static java.util.Objects.requireNonNull; + +/** + * @author jonmv + */ +public abstract class AbstractNameServiceRequest implements NameServiceRequest { + + private final Optional<TenantAndApplicationId> owner; + private final RecordName name; + + AbstractNameServiceRequest(Optional<TenantAndApplicationId> owner, RecordName name) { + this.owner = requireNonNull(owner); + this.name = requireNonNull(name); + } + + @Override + public RecordName name() { + return name; + } + + @Override + public Optional<TenantAndApplicationId> owner() { + return owner; + } + +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecord.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecord.java index f1e4ca3b82b..6f4ee3dfc06 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecord.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecord.java @@ -15,14 +15,13 @@ import java.util.Optional; * * @author mpolden */ -public class CreateRecord implements NameServiceRequest { +public class CreateRecord extends AbstractNameServiceRequest { - private final Optional<TenantAndApplicationId> owner; private final Record record; /** DO NOT USE. Public for serialization purposes */ public CreateRecord(Optional<TenantAndApplicationId> owner, Record record) { - this.owner = Objects.requireNonNull(owner, "owner must be non-null"); + super(owner, record.name()); this.record = Objects.requireNonNull(record, "record must be non-null"); if (record.type() != Record.Type.CNAME && record.type() != Record.Type.A) { throw new IllegalArgumentException("Record of type " + record.type() + " is not supported: " + record); @@ -34,16 +33,6 @@ public class CreateRecord implements NameServiceRequest { } @Override - public Optional<RecordName> name() { - return Optional.of(record.name()); - } - - @Override - public Optional<TenantAndApplicationId> owner() { - return owner; - } - - @Override public void dispatchTo(NameService nameService) { List<Record> records = nameService.findRecords(record.type(), record.name()); records.forEach(r -> { @@ -67,12 +56,12 @@ public class CreateRecord implements NameServiceRequest { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CreateRecord that = (CreateRecord) o; - return owner.equals(that.owner) && record.equals(that.record); + return owner().equals(that.owner()) && record.equals(that.record); } @Override public int hashCode() { - return Objects.hash(owner, record); + return Objects.hash(owner(), record); } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecords.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecords.java index a668c408794..ef7b74a4d4b 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecords.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecords.java @@ -20,17 +20,14 @@ import java.util.stream.Collectors; * * @author mpolden */ -public class CreateRecords implements NameServiceRequest { +public class CreateRecords extends AbstractNameServiceRequest { - private final Optional<TenantAndApplicationId> owner; - private final RecordName name; private final Record.Type type; private final List<Record> records; /** DO NOT USE. Public for serialization purposes */ public CreateRecords(Optional<TenantAndApplicationId> owner, List<Record> records) { - this.owner = Objects.requireNonNull(owner, "owner must be non-null"); - this.name = requireOneOf(Record::name, records); + super(owner, requireOneOf(Record::name, records)); this.type = requireOneOf(Record::type, records); this.records = List.copyOf(Objects.requireNonNull(records, "records must be non-null")); if (type != Record.Type.ALIAS && type != Record.Type.TXT && type != Record.Type.DIRECT) { @@ -43,29 +40,19 @@ public class CreateRecords implements NameServiceRequest { } @Override - public Optional<RecordName> name() { - return Optional.of(name); - } - - @Override - public Optional<TenantAndApplicationId> owner() { - return owner; - } - - @Override public void dispatchTo(NameService nameService) { switch (type) { case ALIAS -> { var targets = records.stream().map(Record::data).map(AliasTarget::unpack).collect(Collectors.toSet()); - nameService.createAlias(name, targets); + nameService.createAlias(name(), targets); } case DIRECT -> { var targets = records.stream().map(Record::data).map(DirectTarget::unpack).collect(Collectors.toSet()); - nameService.createDirect(name, targets); + nameService.createDirect(name(), targets); } case TXT -> { var dataFields = records.stream().map(Record::data).toList(); - nameService.createTxtRecords(name, dataFields); + nameService.createTxtRecords(name(), dataFields); } } } @@ -80,12 +67,12 @@ public class CreateRecords implements NameServiceRequest { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CreateRecords that = (CreateRecords) o; - return owner.equals(that.owner) && records.equals(that.records); + return owner().equals(that.owner()) && records.equals(that.records); } @Override public int hashCode() { - return Objects.hash(owner, records); + return Objects.hash(owner(), records); } /** Find exactly one distinct value of field in given list */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceRequest.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceRequest.java index c43130ce4e9..dd3cca9a4fa 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceRequest.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceRequest.java @@ -14,7 +14,8 @@ import java.util.Optional; */ public interface NameServiceRequest { - Optional<RecordName> name(); + /** The record name this request pertains to. */ + RecordName name(); /** The application owning this request */ Optional<TenantAndApplicationId> owner(); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/RemoveRecords.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/RemoveRecords.java index 2ff2edf11f4..273136ba0a6 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/RemoveRecords.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/RemoveRecords.java @@ -21,11 +21,9 @@ import java.util.Optional; * * @author mpolden */ -public class RemoveRecords implements NameServiceRequest { +public class RemoveRecords extends AbstractNameServiceRequest { - private final Optional<TenantAndApplicationId> owner; private final Record.Type type; - private final RecordName name; private final Optional<RecordData> data; public RemoveRecords(Optional<TenantAndApplicationId> owner, Record.Type type, RecordName name) { @@ -38,9 +36,8 @@ public class RemoveRecords implements NameServiceRequest { /** DO NOT USE. Public for serialization purposes */ public RemoveRecords(Optional<TenantAndApplicationId> owner, Record.Type type, RecordName name, Optional<RecordData> data) { - this.owner = Objects.requireNonNull(owner, "owner must be non-null"); + super(owner, name); this.type = Objects.requireNonNull(type, "type must be non-null"); - this.name = Objects.requireNonNull(name, "name must be non-null"); this.data = Objects.requireNonNull(data, "data must be non-null"); } @@ -48,16 +45,6 @@ public class RemoveRecords implements NameServiceRequest { return type; } - @Override - public Optional<RecordName> name() { - return Optional.of(name); - } - - @Override - public Optional<TenantAndApplicationId> owner() { - return owner; - } - public Optional<RecordData> data() { return data; } @@ -66,7 +53,7 @@ public class RemoveRecords implements NameServiceRequest { public void dispatchTo(NameService nameService) { // Deletions require all records fields to match exactly, data may be incomplete even if present. To ensure // completeness we search for the record(s) first - List<Record> completeRecords = nameService.findRecords(type, name).stream() + List<Record> completeRecords = nameService.findRecords(type, name()).stream() .filter(record -> data.isEmpty() || matchingFqdnIn(data.get(), record)) .toList(); nameService.removeRecords(completeRecords); @@ -74,7 +61,7 @@ public class RemoveRecords implements NameServiceRequest { @Override public String toString() { - return "remove records of type " + type + ", by name " + name + + return "remove records of type " + type + ", by name " + name() + data.map(d -> " data " + d).orElse(""); } @@ -83,12 +70,12 @@ public class RemoveRecords implements NameServiceRequest { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RemoveRecords that = (RemoveRecords) o; - return owner.equals(that.owner) && type == that.type && name.equals(that.name) && data.equals(that.data); + return owner().equals(that.owner()) && type == that.type && name().equals(that.name()) && data.equals(that.data); } @Override public int hashCode() { - return Objects.hash(owner, type, name, data); + return Objects.hash(owner(), type, name(), data); } private static boolean matchingFqdnIn(RecordData data, Record record) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/NameServiceQueueSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/NameServiceQueueSerializer.java index 89230969164..d02d27b5293 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/NameServiceQueueSerializer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/NameServiceQueueSerializer.java @@ -92,7 +92,7 @@ public class NameServiceQueueSerializer { private void toSlime(Cursor object, RemoveRecords removeRecords) { object.setString(requestType, Request.removeRecords.name()); object.setString(typeField, removeRecords.type().name()); - removeRecords.name().ifPresent(name -> object.setString(nameField, name.asString())); + object.setString(nameField, removeRecords.name().asString()); removeRecords.data().ifPresent(data -> object.setString(dataField, data.asString())); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializer.java index 4d759056dfc..47b27aac79a 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializer.java @@ -50,6 +50,7 @@ public class RoutingPolicySerializer { private static final String agentField = "agent"; private static final String changedAtField = "changedAt"; private static final String statusField = "status"; + private static final String privateOnlyField = "private"; public Slime toSlime(List<RoutingPolicy> routingPolicies) { var slime = new Slime(); @@ -68,6 +69,7 @@ public class RoutingPolicySerializer { policy.applicationEndpoints().forEach(endpointId -> applicationEndpointsArray.addString(endpointId.id())); policyObject.setBool(loadBalancerActiveField, policy.status().isActive()); globalRoutingToSlime(policy.status().routingStatus(), policyObject.setObject(globalRoutingField)); + if ( ! policy.isPublic()) policyObject.setBool(privateOnlyField, true); }); return slime; } @@ -84,6 +86,7 @@ public class RoutingPolicySerializer { RoutingPolicyId id = new RoutingPolicyId(owner, ClusterSpec.Id.from(inspect.field(clusterField).asString()), ZoneId.from(inspect.field(zoneField).asString())); + boolean isPublic = ! inspect.field(privateOnlyField).asBool(); policies.add(new RoutingPolicy(id, SlimeUtils.optionalString(inspect.field(canonicalNameField)).map(DomainName::of), SlimeUtils.optionalString(inspect.field(ipAddressField)), @@ -91,7 +94,8 @@ public class RoutingPolicySerializer { instanceEndpoints, applicationEndpoints, new RoutingPolicy.Status(inspect.field(loadBalancerActiveField).asBool(), - globalRoutingFromSlime(inspect.field(globalRoutingField))))); + globalRoutingFromSlime(inspect.field(globalRoutingField))), + isPublic)); }); return Collections.unmodifiableList(policies); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java index c737f9b58ef..1c4916b9bed 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java @@ -361,7 +361,8 @@ public class RoutingPolicies { var newPolicy = new RoutingPolicy(policyId, loadBalancer.hostname(), loadBalancer.ipAddress(), dnsZone, allocation.instanceEndpointsOf(loadBalancer), allocation.applicationEndpointsOf(loadBalancer), - new RoutingPolicy.Status(isActive(loadBalancer), RoutingStatus.DEFAULT)); + new RoutingPolicy.Status(isActive(loadBalancer), RoutingStatus.DEFAULT), + loadBalancer.isPublic()); // Preserve global routing status for existing policy if (existingPolicy != null) { newPolicy = newPolicy.with(newPolicy.status().with(existingPolicy.status().routingStatus())); @@ -399,7 +400,7 @@ public class RoutingPolicies { while (controller.clock().instant().isBefore(doom)) { try (Mutex lock = controller.curator().lockNameServiceQueue()) { if (controller.curator().readNameServiceQueue().requests().stream() - .noneMatch(request -> request.name().equals(Optional.of(challenge.name())))) { + .noneMatch(request -> request.name().equals(challenge.name()))) { try { challenge.trigger().run(); } finally { nameServiceForwarderIn(deploymentId.zoneId()).removeRecords(Type.TXT, challenge.name(), Priority.normal, ownerOf(deploymentId)); } return; 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 3d43e42af27..38ecff452c8 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 @@ -7,7 +7,6 @@ import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.zone.RoutingMethod; import com.yahoo.text.Text; import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; -import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry; 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; @@ -29,11 +28,12 @@ public record RoutingPolicy(RoutingPolicyId id, Optional<String> dnsZone, Set<EndpointId> instanceEndpoints, Set<EndpointId> applicationEndpoints, - Status status) { + Status status, + boolean isPublic) { /** DO NOT USE. Public for serialization purposes */ public RoutingPolicy(RoutingPolicyId id, Optional<DomainName> canonicalName, Optional<String> ipAddress, Optional<String> dnsZone, - Set<EndpointId> instanceEndpoints, Set<EndpointId> applicationEndpoints, Status status) { + Set<EndpointId> instanceEndpoints, Set<EndpointId> applicationEndpoints, Status status, boolean isPublic) { this.id = Objects.requireNonNull(id, "id must be non-null"); this.canonicalName = Objects.requireNonNull(canonicalName, "canonicalName must be non-null"); this.ipAddress = Objects.requireNonNull(ipAddress, "ipAddress must be non-null"); @@ -41,10 +41,15 @@ public record RoutingPolicy(RoutingPolicyId id, this.instanceEndpoints = ImmutableSortedSet.copyOf(Objects.requireNonNull(instanceEndpoints, "instanceEndpoints must be non-null")); this.applicationEndpoints = ImmutableSortedSet.copyOf(Objects.requireNonNull(applicationEndpoints, "applicationEndpoints must be non-null")); this.status = Objects.requireNonNull(status, "status must be non-null"); + this.isPublic = isPublic; if (canonicalName.isEmpty() == ipAddress.isEmpty()) throw new IllegalArgumentException("Exactly 1 of canonicalName=%s and ipAddress=%s must be set".formatted( canonicalName.map(DomainName::value).orElse("<empty>"), ipAddress.orElse("<empty>"))); + if ( ! instanceEndpoints.isEmpty() && ! isPublic) + throw new IllegalArgumentException("Non-public zone endpoint cannot be part of any global endpoint, but was in: " + instanceEndpoints); + if ( ! applicationEndpoints.isEmpty() && ! isPublic) + throw new IllegalArgumentException("Non-public zone endpoint cannot be part of any application endpoint, but was in: " + applicationEndpoints); } /** The ID of this */ @@ -82,6 +87,11 @@ public record RoutingPolicy(RoutingPolicyId id, return status; } + /** Returns whether this has a load balancer which is available from public internet. */ + public boolean isPublic() { + return isPublic; + } + /** Returns whether this policy applies to given deployment */ public boolean appliesTo(DeploymentId deployment) { return id.owner().equals(deployment.applicationId()) && @@ -90,7 +100,7 @@ public record RoutingPolicy(RoutingPolicyId id, /** Returns a copy of this with status set to given status */ public RoutingPolicy with(Status status) { - return new RoutingPolicy(id, canonicalName, ipAddress, dnsZone, instanceEndpoints, applicationEndpoints, status); + return new RoutingPolicy(id, canonicalName, ipAddress, dnsZone, instanceEndpoints, applicationEndpoints, status, isPublic); } /** Returns the zone endpoints of this */ |