summaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java90
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/AbstractNameServiceRequest.java33
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecord.java19
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/CreateRecords.java27
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/NameServiceRequest.java3
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/dns/RemoveRecords.java25
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/NameServiceQueueSerializer.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializer.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicy.java18
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java18
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/NameServiceQueueSerializerTest.java3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java58
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java6
17 files changed, 159 insertions, 165 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 7da61f9bc63..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;
@@ -275,7 +280,8 @@ public class InternalStepRunner implements StepRunner {
switch (e.type()) {
case CERT_NOT_AVAILABLE:
// Same as CERTIFICATE_NOT_READY above, only from the controller
- logger.log("Validating CA signed certificate requested for app: not yet available");
+ logger.log("Creating a CA signed certificate for the application. " +
+ "This may take up to " + timeouts.endpointCertificate() + " on first deployment.");
if (startTime.plus(timeouts.endpointCertificate()).isBefore(controller.clock().instant())) {
logger.log(WARNING, "CA signed certificate for app not available within " +
timeouts.endpointCertificate() + ": " + Exceptions.toMessageString(e));
@@ -350,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);
}
@@ -475,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);
@@ -501,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 */
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
index 7edb458c154..95c22480c0f 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentContext.java
@@ -277,7 +277,8 @@ public class DeploymentContext {
Optional.empty(),
Set.of(EndpointId.of("default")),
Set.of(),
- new RoutingPolicy.Status(false, RoutingStatus.DEFAULT)));
+ new RoutingPolicy.Status(false, RoutingStatus.DEFAULT),
+ true));
tester.controller().curator().writeRoutingPolicies(instanceId, List.copyOf(policies.values()));
return this;
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
index 6f859ff3d15..5704af75cb9 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ConfigServerMock.java
@@ -12,6 +12,8 @@ import com.yahoo.config.provision.CloudAccount;
import com.yahoo.config.provision.ClusterResources;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.DockerImage;
+import com.yahoo.config.provision.EndpointsChecker.Availability;
+import com.yahoo.config.provision.EndpointsChecker.Endpoint;
import com.yahoo.config.provision.Environment;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.NodeResources;
@@ -42,8 +44,10 @@ import com.yahoo.vespa.hosted.controller.api.integration.configserver.QuotaUsage
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ServiceConvergence;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.TestReport;
import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterCloud;
+import com.yahoo.vespa.hosted.controller.api.integration.dns.NameService;
import com.yahoo.vespa.hosted.controller.api.integration.noderepository.RestartFilter;
import com.yahoo.vespa.hosted.controller.api.integration.secrets.TenantSecretStore;
+import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockTesterCloud;
import com.yahoo.vespa.hosted.controller.application.SystemApplication;
import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
@@ -82,6 +86,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
*/
public class ConfigServerMock extends AbstractComponent implements ConfigServer {
+ private final MockTesterCloud mockTesterCloud;
private final Map<DeploymentId, Application> applications = new LinkedHashMap<>();
private final Set<ZoneId> inactiveZones = new HashSet<>();
private final Map<DeploymentId, EndpointStatus> endpoints = new HashMap<>();
@@ -105,9 +110,9 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
private Consumer<ApplicationId> prepareException = null;
private Supplier<String> log = () -> "INFO - All good";
- @Inject
- public ConfigServerMock(ZoneRegistryMock zoneRegistry) {
+ public ConfigServerMock(ZoneRegistryMock zoneRegistry, NameService nameService) {
bootstrap(zoneRegistry.zones().all().ids(), SystemApplication.notController());
+ this.mockTesterCloud = new MockTesterCloud(nameService);
}
/** Assigns a reserved tenant node to the given deployment, with initial versions. */
@@ -370,8 +375,10 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
public Optional<TestReport> getTestReport(DeploymentId deployment) {
return Optional.ofNullable(testReport.get(deployment));
}
- public void setTestReport(DeploymentId deploymentId, TestReport report) {
- testReport.put(deploymentId, report);
+
+ @Override
+ public Availability verifyEndpoints(DeploymentId deploymentId, List<Endpoint> zoneEndpoints) {
+ return mockTesterCloud.verifyEndpoints(deploymentId, zoneEndpoints); // Wraps the same name service mock, which is updated by test harness.
}
/** Add any of given loadBalancers that do not already exist to the load balancers in zone */
@@ -419,7 +426,8 @@ public class ConfigServerMock extends AbstractComponent implements ConfigServer
LoadBalancer.State.active,
Optional.of("dns-zone-1"),
Optional.empty(),
- Optional.of(new PrivateServiceInfo("service", List.of(new AllowedUrn(AccessType.awsPrivateLink, "arne")))))));
+ Optional.of(new PrivateServiceInfo("service", List.of(new AllowedUrn(AccessType.awsPrivateLink, "arne")))),
+ true)));
}
Application application = applications.get(id);
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
index 382a697c4cd..0ba8866c990 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/ServiceRegistryMock.java
@@ -100,8 +100,8 @@ public class ServiceRegistryMock extends AbstractComponent implements ServiceReg
public ServiceRegistryMock(SystemName system) {
this.zoneRegistryMock = new ZoneRegistryMock(system);
- this.configServerMock = new ConfigServerMock(zoneRegistryMock);
- this.mockTesterCloud = new MockTesterCloud(nameService());
+ this.configServerMock = new ConfigServerMock(zoneRegistryMock, memoryNameService);
+ this.mockTesterCloud = new MockTesterCloud(memoryNameService);
this.clock.setInstant(Instant.ofEpochSecond(1600000000));
this.controllerVersion = new ControllerVersion(Version.fromString("6.1.0"), "badb01", clock.instant());
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/NameServiceQueueSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/NameServiceQueueSerializerTest.java
index 582514163e1..ad9ebd2a968 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/NameServiceQueueSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/NameServiceQueueSerializerTest.java
@@ -12,6 +12,7 @@ import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
import com.yahoo.vespa.hosted.controller.dns.CreateRecord;
import com.yahoo.vespa.hosted.controller.dns.CreateRecords;
import com.yahoo.vespa.hosted.controller.dns.NameServiceQueue;
+import com.yahoo.vespa.hosted.controller.dns.NameServiceRequest;
import com.yahoo.vespa.hosted.controller.dns.RemoveRecords;
import org.junit.jupiter.api.Test;
@@ -32,7 +33,7 @@ public class NameServiceQueueSerializerTest {
Optional<TenantAndApplicationId> owner = Optional.of(TenantAndApplicationId.from("t", "a"));
var record1 = new Record(Record.Type.CNAME, RecordName.from("cname.example.com"), RecordData.from("example.com"));
var record2 = new Record(Record.Type.TXT, RecordName.from("txt.example.com"), RecordData.from("text"));
- var requests = List.of(
+ var requests = List.<NameServiceRequest>of(
new CreateRecord(owner, record1),
new CreateRecords(owner, List.of(record2)),
new CreateRecords(owner, List.of(new Record(Record.Type.ALIAS, RecordName.from("alias.example.com"),
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java
index 6285c5c4aac..8e0b1dd1d4e 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/RoutingPolicySerializerTest.java
@@ -32,35 +32,38 @@ public class RoutingPolicySerializerTest {
var instanceEndpoints = Set.of(EndpointId.of("r1"), EndpointId.of("r2"));
var applicationEndpoints = Set.of(EndpointId.of("a1"));
var id1 = new RoutingPolicyId(owner,
- ClusterSpec.Id.from("my-cluster1"),
- ZoneId.from("prod", "us-north-1"));
+ ClusterSpec.Id.from("my-cluster1"),
+ ZoneId.from("prod", "us-north-1"));
var id2 = new RoutingPolicyId(owner,
- ClusterSpec.Id.from("my-cluster2"),
- ZoneId.from("prod", "us-north-2"));
+ ClusterSpec.Id.from("my-cluster2"),
+ ZoneId.from("prod", "us-north-2"));
var policies = List.of(new RoutingPolicy(id1,
- Optional.of(HostName.of("long-and-ugly-name")),
- Optional.empty(),
- Optional.of("zone1"),
- instanceEndpoints,
- applicationEndpoints,
- new RoutingPolicy.Status(true, RoutingStatus.DEFAULT)),
- new RoutingPolicy(id2,
- Optional.of(HostName.of("long-and-ugly-name-2")),
- Optional.empty(),
- Optional.empty(),
- instanceEndpoints,
- Set.of(),
- new RoutingPolicy.Status(false,
- new RoutingStatus(RoutingStatus.Value.out,
- RoutingStatus.Agent.tenant,
- Instant.ofEpochSecond(123)))),
- new RoutingPolicy(id1,
- Optional.empty(),
- Optional.of("127.0.0.1"),
- Optional.of("zone2"),
- instanceEndpoints,
- applicationEndpoints,
- new RoutingPolicy.Status(true, RoutingStatus.DEFAULT)));
+ Optional.of(HostName.of("long-and-ugly-name")),
+ Optional.empty(),
+ Optional.of("zone1"),
+ Set.of(),
+ Set.of(),
+ new RoutingPolicy.Status(true, RoutingStatus.DEFAULT),
+ false),
+ new RoutingPolicy(id2,
+ Optional.of(HostName.of("long-and-ugly-name-2")),
+ Optional.empty(),
+ Optional.empty(),
+ instanceEndpoints,
+ Set.of(),
+ new RoutingPolicy.Status(false,
+ new RoutingStatus(RoutingStatus.Value.out,
+ RoutingStatus.Agent.tenant,
+ Instant.ofEpochSecond(123))),
+ true),
+ new RoutingPolicy(id1,
+ Optional.empty(),
+ Optional.of("127.0.0.1"),
+ Optional.of("zone2"),
+ instanceEndpoints,
+ applicationEndpoints,
+ new RoutingPolicy.Status(true, RoutingStatus.DEFAULT),
+ true));
var serialized = serializer.fromSlime(owner, serializer.toSlime(policies));
assertEquals(policies.size(), serialized.size());
for (Iterator<RoutingPolicy> it1 = policies.iterator(), it2 = serialized.iterator(); it1.hasNext(); ) {
@@ -73,6 +76,7 @@ public class RoutingPolicySerializerTest {
assertEquals(expected.instanceEndpoints(), actual.instanceEndpoints());
assertEquals(expected.applicationEndpoints(), actual.applicationEndpoints());
assertEquals(expected.status(), actual.status());
+ assertEquals(expected.isPublic(), actual.isPublic());
}
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
index 03322d7c962..1efafb81685 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java
@@ -364,8 +364,8 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest {
ControllerTester wrapped = new ControllerTester(tester);
wrapped.upgradeSystem(Version.fromString("7.1"));
new DeploymentTester(wrapped).newDeploymentContext(ApplicationId.from(tenantName, applicationName, InstanceName.defaultName()))
- .submit()
- .deploy();
+ .submit()
+ .deploy();
tester.assertResponse(request("/application/v4/tenant/scoober", GET).roles(Role.reader(tenantName)),
(response) -> assertFalse(response.getBodyAsString().contains("archiveAccessRole")),
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java
index 9c1253c7520..2932860efaa 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/RoutingPoliciesTest.java
@@ -497,7 +497,8 @@ public class RoutingPoliciesTest {
LoadBalancer.State.active,
Optional.of("dns-zone-1"),
Optional.empty(),
- Optional.empty());
+ Optional.empty(),
+ true);
tester.controllerTester().configServer().putLoadBalancers(zone1, List.of(loadBalancer));
// Application redeployment preserves DNS record
@@ -952,7 +953,8 @@ public class RoutingPoliciesTest {
LoadBalancer.State.active,
Optional.of("dns-zone-1").filter(__ -> lbHostname.isPresent()),
Optional.empty(),
- Optional.empty()));
+ Optional.empty(),
+ true));
}
return loadBalancers;
}