summaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorØyvind Grønnesby <oyving@verizonmedia.com>2021-11-23 16:19:20 +0100
committerØyvind Grønnesby <oyving@verizonmedia.com>2021-11-23 16:19:20 +0100
commitcd91614db66b12e84293b8ba938cf2103aa95b28 (patch)
tree0ce87ab3b7bd57e9f13aec9fdae2a56baeb6d22b /controller-server
parentb342a8edb3c9ce28d53abbf89f70c1c3900a9db0 (diff)
parentbaa10a7a7762766d4b32d484bc21283cac543993 (diff)
Merge remote-tracking branch 'origin/master' into ogronnesby/supported-plans
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java26
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Instance.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java132
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/AssignedRotation.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java7
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunner.java59
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java10
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemRoutingPolicyMaintainer.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java77
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiHandler.java117
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/RoutingPolicies.java14
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/DeploymentRoutingContext.java154
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/ExclusiveRoutingContext.java41
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/RoutingContext.java23
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/SharedRoutingContext.java48
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/Rotation.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/Rotation.java)2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationId.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationId.java)2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationLock.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationLock.java)2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepository.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepository.java)2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationState.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationState.java)2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationStatus.java (renamed from controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationStatus.java)2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java51
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java12
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java14
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/global-rotation-get.json2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java (renamed from controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java)2
-rw-r--r--controller-server/src/test/resources/test_runner_services.xml-cd (renamed from controller-server/src/test/resources/test_runner_services.xml-cd-osgi)0
-rw-r--r--controller-server/src/test/resources/test_runner_services.xml-cd-legacy22
33 files changed, 509 insertions, 344 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
index f3368d0918b..9e7c614d4e8 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java
@@ -351,6 +351,7 @@ public class ApplicationController {
TenantAndApplicationId applicationId = TenantAndApplicationId.from(job.application());
ZoneId zone = job.type().zone(controller.system());
+ DeploymentId deployment = new DeploymentId(job.application(), zone);
try (Lock deploymentLock = lockForDeployment(job.application(), zone)) {
Set<ContainerEndpoint> containerEndpoints;
@@ -364,7 +365,7 @@ public class ApplicationController {
Version platform = run.versions().sourcePlatform().filter(__ -> deploySourceVersions).orElse(run.versions().targetPlatform());
ApplicationVersion revision = run.versions().sourceApplication().filter(__ -> deploySourceVersions).orElse(run.versions().targetApplication());
- ApplicationPackage applicationPackage = new ApplicationPackage(applicationStore.get(new DeploymentId(job.application(), zone), revision));
+ ApplicationPackage applicationPackage = new ApplicationPackage(applicationStore.get(deployment, revision));
try (Lock lock = lock(applicationId)) {
LockedApplication application = new LockedApplication(requireApplication(applicationId), lock);
@@ -376,8 +377,7 @@ public class ApplicationController {
applicationPackage = applicationPackage.withTrustedCertificate(run.testerCertificate().get());
endpointCertificateMetadata = endpointCertificates.getMetadata(instance, zone, applicationPackage.deploymentSpec());
-
- containerEndpoints = controller.routing().containerEndpointsOf(application, job.application().instance(), zone);
+ containerEndpoints = controller.routing().of(deployment).prepare(application);
} // Release application lock while doing the deployment, which is a lengthy task.
@@ -391,7 +391,7 @@ public class ApplicationController {
// For direct deployments use the full deployment ID, but otherwise use just the tenant and application as
// the source since it's the same application, so it should have the same warnings
NotificationSource source = zone.environment().isManuallyDeployed() ?
- NotificationSource.from(new DeploymentId(job.application(), zone)) : NotificationSource.from(applicationId);
+ NotificationSource.from(deployment) : NotificationSource.from(applicationId);
List<String> warnings = Optional.ofNullable(result.prepareResponse().log)
.map(logs -> logs.stream()
.filter(log -> log.applicationPackage)
@@ -476,6 +476,7 @@ public class ApplicationController {
ZoneId zone, Version platform, Set<ContainerEndpoint> endpoints,
Optional<EndpointCertificateMetadata> endpointCertificateMetadata,
boolean dryRun) {
+ DeploymentId deployment = new DeploymentId(application, zone);
try {
Optional<DockerImage> dockerImageRepo = Optional.ofNullable(
dockerImageRepoFlag
@@ -490,7 +491,7 @@ public class ApplicationController {
.map(tenant -> ((AthenzTenant)tenant).domain());
if (zone.environment().isManuallyDeployed())
- controller.applications().applicationStore().putMeta(new DeploymentId(application, zone),
+ controller.applications().applicationStore().putMeta(deployment,
clock.instant(),
applicationPackage.metaDataZip());
@@ -502,9 +503,9 @@ public class ApplicationController {
.filter(tenant-> tenant instanceof CloudTenant)
.map(tenant -> ((CloudTenant) tenant).tenantSecretStores())
.orElse(List.of());
- List<X509Certificate> operatorCertificates = controller.supportAccess().activeGrantsFor(new DeploymentId(application, zone)).stream()
- .map(SupportAccessGrant::certificate)
- .collect(toList());
+ List<X509Certificate> operatorCertificates = controller.supportAccess().activeGrantsFor(deployment).stream()
+ .map(SupportAccessGrant::certificate)
+ .collect(toList());
ConfigServer.PreparedApplication preparedApplication =
configServer.deploy(new DeploymentData(application, zone, applicationPackage.zippedContent(), platform,
@@ -515,9 +516,10 @@ public class ApplicationController {
return new ActivateResult(new RevisionId(applicationPackage.hash()), preparedApplication.prepareResponse(),
applicationPackage.zippedContent().length);
} finally {
- // Even if prepare fails, a load balancer may have been provisioned. Always refresh routing policies so that
- // any DNS updates can be propagated as early as possible.
- controller.routing().policies().refresh(application, applicationPackage.deploymentSpec(), zone);
+ // Even if prepare fails, routing configuration may need to be updated
+ if ( ! application.instance().isTester()) {
+ controller.routing().of(deployment).configure(applicationPackage.deploymentSpec());
+ }
}
}
@@ -701,7 +703,7 @@ public class ApplicationController {
try {
configServer.deactivate(id);
} finally {
- controller.routing().policies().refresh(application.get().id().instance(instanceName), application.get().deploymentSpec(), zone);
+ controller.routing().of(id).configure(application.get().deploymentSpec());
if (zone.environment().isManuallyDeployed())
applicationStore.putMetaTombstone(id, clock.instant());
if (!zone.environment().isTest())
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Instance.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Instance.java
index ea2bcfcac4b..6e31c93dbdd 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Instance.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Instance.java
@@ -14,7 +14,7 @@ import com.yahoo.vespa.hosted.controller.application.Deployment;
import com.yahoo.vespa.hosted.controller.application.DeploymentActivity;
import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics;
import com.yahoo.vespa.hosted.controller.application.QuotaUsage;
-import com.yahoo.vespa.hosted.controller.rotation.RotationStatus;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationStatus;
import java.time.Instant;
import java.util.Collection;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
index 3794b69c023..4772dbeaab1 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/RoutingController.java
@@ -18,7 +18,6 @@ import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.flags.BooleanFlag;
import com.yahoo.vespa.flags.FetchVector;
import com.yahoo.vespa.flags.Flags;
-import com.yahoo.vespa.hosted.controller.api.application.v4.model.EndpointStatus;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ContainerEndpoint;
import com.yahoo.vespa.hosted.controller.api.integration.dns.Record;
@@ -31,10 +30,16 @@ import com.yahoo.vespa.hosted.controller.application.EndpointList;
import com.yahoo.vespa.hosted.controller.application.SystemApplication;
import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
import com.yahoo.vespa.hosted.controller.dns.NameServiceQueue.Priority;
-import com.yahoo.vespa.hosted.controller.rotation.RotationLock;
-import com.yahoo.vespa.hosted.controller.rotation.RotationRepository;
import com.yahoo.vespa.hosted.controller.routing.RoutingId;
import com.yahoo.vespa.hosted.controller.routing.RoutingPolicies;
+import com.yahoo.vespa.hosted.controller.routing.context.DeploymentRoutingContext;
+import com.yahoo.vespa.hosted.controller.routing.context.DeploymentRoutingContext.ExclusiveDeploymentRoutingContext;
+import com.yahoo.vespa.hosted.controller.routing.context.DeploymentRoutingContext.SharedDeploymentRoutingContext;
+import com.yahoo.vespa.hosted.controller.routing.context.ExclusiveRoutingContext;
+import com.yahoo.vespa.hosted.controller.routing.context.RoutingContext;
+import com.yahoo.vespa.hosted.controller.routing.context.SharedRoutingContext;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationLock;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationRepository;
import com.yahoo.vespa.hosted.rotation.config.RotationsConfig;
import java.nio.charset.StandardCharsets;
@@ -44,7 +49,6 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -77,6 +81,25 @@ public class RoutingController {
this.hideSharedRoutingEndpoint = Flags.HIDE_SHARED_ROUTING_ENDPOINT.bindTo(controller.flagSource());
}
+ /** Create a routing context for given deployment */
+ public DeploymentRoutingContext of(DeploymentId deployment) {
+ if (usesSharedRouting(deployment.zoneId())) {
+ return new SharedDeploymentRoutingContext(deployment,
+ this,
+ controller.serviceRegistry().configServer(),
+ controller.clock());
+ }
+ return new ExclusiveDeploymentRoutingContext(deployment, this);
+ }
+
+ /** Create a routing context for given zone */
+ public RoutingContext of(ZoneId zone) {
+ if (usesSharedRouting(zone)) {
+ return new SharedRoutingContext(zone, controller.serviceRegistry().configServer());
+ }
+ return new ExclusiveRoutingContext(zone, routingPolicies);
+ }
+
public RoutingPolicies policies() {
return routingPolicies;
}
@@ -145,16 +168,17 @@ public class RoutingController {
.collect(Collectors.toMap(t -> new DeploymentId(application.id().instance(t.instance()),
ZoneId.from(Environment.prod, t.region())),
t -> t.weight()));
- List<RoutingMethod> availableRoutingMethods = routingMethodsOfAll(deployments.keySet(), deploymentSpec);
- for (var routingMethod : availableRoutingMethods) {
- endpoints.add(Endpoint.of(application.id())
- .targetApplication(EndpointId.of(declaredEndpoint.endpointId()),
- ClusterSpec.Id.from(declaredEndpoint.containerId()),
- deployments)
- .routingMethod(routingMethod)
- .on(Port.fromRoutingMethod(routingMethod))
- .in(controller.system()));
- }
+ // An application endpoint can only target a single zone, so we just pick the zone of any deployment target
+ ZoneId zone = deployments.keySet().iterator().next().zoneId();
+ // Application endpoints are only supported when using direct routing methods
+ RoutingMethod routingMethod = usesSharedRouting(zone) ? RoutingMethod.sharedLayer4 : RoutingMethod.exclusive;
+ endpoints.add(Endpoint.of(application.id())
+ .targetApplication(EndpointId.of(declaredEndpoint.endpointId()),
+ ClusterSpec.Id.from(declaredEndpoint.containerId()),
+ deployments)
+ .routingMethod(routingMethod)
+ .on(Port.fromRoutingMethod(routingMethod))
+ .in(controller.system()));
}
return EndpointList.copyOf(endpoints);
}
@@ -216,43 +240,6 @@ public class RoutingController {
return Collections.unmodifiableList(endpointDnsNames);
}
- /** Change status of all global endpoints for given deployment */
- public void setGlobalRotationStatus(DeploymentId deployment, EndpointStatus status) {
- readDeclaredEndpointsOf(deployment.applicationId()).requiresRotation().primary().ifPresent(endpoint -> {
- try {
- controller.serviceRegistry().configServer().setGlobalRotationStatus(deployment, endpoint.upstreamIdOf(deployment), status);
- } catch (Exception e) {
- throw new RuntimeException("Failed to set rotation status of " + endpoint + " in " + deployment, e);
- }
- });
- }
-
- /** Get global endpoint status for given deployment */
- public Map<Endpoint, EndpointStatus> globalRotationStatus(DeploymentId deployment) {
- var routingEndpoints = new LinkedHashMap<Endpoint, EndpointStatus>();
- readDeclaredEndpointsOf(deployment.applicationId()).requiresRotation().primary().ifPresent(endpoint -> {
- var upstreamName = endpoint.upstreamIdOf(deployment);
- var status = controller.serviceRegistry().configServer().getGlobalRotationStatus(deployment, upstreamName);
- routingEndpoints.put(endpoint, status);
- });
- return Collections.unmodifiableMap(routingEndpoints);
- }
-
- /**
- * Assigns one or more global rotations to given application, if eligible. The given application is implicitly
- * stored, ensuring that the assigned rotation(s) are persisted when this returns.
- */
- private LockedApplication assignRotations(LockedApplication application, InstanceName instanceName) {
- try (RotationLock rotationLock = rotationRepository.lock()) {
- var rotations = rotationRepository.getOrAssignRotations(application.get().deploymentSpec(),
- application.get().require(instanceName),
- rotationLock);
- application = application.with(instanceName, instance -> instance.with(rotations));
- controller.applications().store(application); // store assigned rotation even if deployment fails
- }
- return application;
- }
-
/** Returns the global and application-level endpoints for given deployment, as container endpoints */
public Set<ContainerEndpoint> containerEndpointsOf(LockedApplication application, InstanceName instanceName, ZoneId zone) {
// Assign rotations to application
@@ -354,6 +341,36 @@ public class RoutingController {
Priority.normal));
}
+ /** Returns direct routing endpoints if any exist and feature flag is set for given application */
+ // TODO: Remove this when feature flag is removed, and in-line .direct() filter where relevant
+ public EndpointList directEndpoints(EndpointList endpoints, ApplicationId application) {
+ boolean hideSharedEndpoint = hideSharedRoutingEndpoint.with(FetchVector.Dimension.APPLICATION_ID, application.serializedForm()).value();
+ EndpointList directEndpoints = endpoints.direct();
+ if (hideSharedEndpoint && !directEndpoints.isEmpty()) {
+ return directEndpoints;
+ }
+ return endpoints;
+ }
+
+ /**
+ * Assigns one or more global rotations to given application, if eligible. The given application is implicitly
+ * stored, ensuring that the assigned rotation(s) are persisted when this returns.
+ */
+ private LockedApplication assignRotations(LockedApplication application, InstanceName instanceName) {
+ try (RotationLock rotationLock = rotationRepository.lock()) {
+ var rotations = rotationRepository.getOrAssignRotations(application.get().deploymentSpec(),
+ application.get().require(instanceName),
+ rotationLock);
+ application = application.with(instanceName, instance -> instance.with(rotations));
+ controller.applications().store(application); // store assigned rotation even if deployment fails
+ }
+ return application;
+ }
+
+ private boolean usesSharedRouting(ZoneId zone) {
+ return controller.zoneRegistry().routingMethods(zone).stream().anyMatch(RoutingMethod::isShared);
+ }
+
/** Returns the routing methods that are available across all given deployments */
private List<RoutingMethod> routingMethodsOfAll(Collection<DeploymentId> deployments, DeploymentSpec deploymentSpec) {
var deploymentsByMethod = new HashMap<RoutingMethod, Set<DeploymentId>>();
@@ -437,23 +454,12 @@ public class RoutingController {
}
/** Create a common name based on a hash of given application. This must be less than 64 characters long. */
- private String commonNameHashOf(ApplicationId application, SystemName system) {
+ private static String commonNameHashOf(ApplicationId application, SystemName system) {
HashCode sha1 = Hashing.sha1().hashString(application.serializedForm(), StandardCharsets.UTF_8);
String base32 = BaseEncoding.base32().omitPadding().lowerCase().encode(sha1.asBytes());
return 'v' + base32 + Endpoint.internalDnsSuffix(system);
}
- /** Returns direct routing endpoints if any exist and feature flag is set for given application */
- // TODO: Remove this when feature flag is removed, and in-line .direct() filter where relevant
- public EndpointList directEndpoints(EndpointList endpoints, ApplicationId application) {
- boolean hideSharedEndpoint = hideSharedRoutingEndpoint.with(FetchVector.Dimension.APPLICATION_ID, application.serializedForm()).value();
- EndpointList directEndpoints = endpoints.direct();
- if (hideSharedEndpoint && !directEndpoints.isEmpty()) {
- return directEndpoints;
- }
- return endpoints;
- }
-
private static String asString(Endpoint.Scope scope) {
switch (scope) {
case application: return "application";
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/AssignedRotation.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/AssignedRotation.java
index 1596456b7cc..ab9304e75f3 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/AssignedRotation.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/AssignedRotation.java
@@ -3,7 +3,7 @@ package com.yahoo.vespa.hosted.controller.application;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.RegionName;
-import com.yahoo.vespa.hosted.controller.rotation.RotationId;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationId;
import java.util.Collection;
import java.util.Objects;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
index c736863a57e..aee7c1052be 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/Endpoint.java
@@ -60,7 +60,7 @@ public class Endpoint {
this.instance = requireInstance(instanceName, scope);
this.url = url;
this.targets = List.copyOf(requireTargets(targets, application, instanceName, scope, certificateName));
- this.scope = scope;
+ this.scope = requireScope(scope, routingMethod);
this.legacy = legacy;
this.routingMethod = routingMethod;
this.tls = port.tls;
@@ -329,6 +329,11 @@ public class Endpoint {
return instanceName;
}
+ private static Scope requireScope(Scope scope, RoutingMethod routingMethod) {
+ if (scope == Scope.application && !routingMethod.isDirect()) throw new IllegalArgumentException("Routing method " + routingMethod + " does not support " + scope + "-scoped endpoints");
+ return scope;
+ }
+
private static List<Target> requireTargets(List<Target> targets, TenantAndApplicationId application, Optional<InstanceName> instanceName, Scope scope, boolean certificateName) {
if (!certificateName && targets.isEmpty()) throw new IllegalArgumentException("At least one target must be given for " + scope + " endpoints");
if (scope == Scope.zone && targets.size() != 1) throw new IllegalArgumentException("Exactly one target must be given for " + scope + " endpoints");
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 4fcd6b10efa..9789bbd4da2 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
@@ -41,15 +41,16 @@ import com.yahoo.vespa.hosted.controller.api.integration.deployment.TesterId;
import com.yahoo.vespa.hosted.controller.api.integration.organization.DeploymentFailureMails;
import com.yahoo.vespa.hosted.controller.api.integration.organization.Mail;
import com.yahoo.vespa.hosted.controller.application.ActivateResult;
-import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.Deployment;
import com.yahoo.vespa.hosted.controller.application.Endpoint;
import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
+import com.yahoo.vespa.hosted.controller.application.pkg.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.config.ControllerConfig;
import com.yahoo.vespa.hosted.controller.maintenance.JobRunner;
import com.yahoo.vespa.hosted.controller.notification.Notification;
import com.yahoo.vespa.hosted.controller.notification.NotificationSource;
-import com.yahoo.vespa.hosted.controller.routing.RoutingPolicyId;
+import com.yahoo.vespa.hosted.controller.routing.RoutingPolicy;
+import com.yahoo.vespa.hosted.controller.routing.context.DeploymentRoutingContext;
import com.yahoo.yolean.Exceptions;
import javax.security.auth.x500.X500Principal;
@@ -477,12 +478,12 @@ public class InternalStepRunner implements StepRunner {
}
private boolean endpointsAvailable(ApplicationId id, ZoneId zone, DualLogger logger) {
- var endpoints = controller.routing().readZoneEndpointsOf(Set.of(new DeploymentId(id, zone)));
+ DeploymentId deployment = new DeploymentId(id, zone);
+ Map<ZoneId, List<Endpoint>> endpoints = controller.routing().readZoneEndpointsOf(Set.of(deployment));
if ( ! endpoints.containsKey(zone)) {
logger.log("Endpoints not yet ready.");
return false;
}
- var policies = controller.routing().policies().get(new DeploymentId(id, zone));
for (var endpoint : endpoints.get(zone)) {
HostName endpointName = HostName.from(endpoint.dnsName());
var ipAddress = controller.jobController().cloud().resolveHostName(endpointName);
@@ -490,10 +491,10 @@ public class InternalStepRunner implements StepRunner {
logger.log(INFO, "DNS lookup yielded no IP address for '" + endpointName + "'.");
return false;
}
- if (endpoint.routingMethod() == RoutingMethod.exclusive) {
- var policy = policies.get(new RoutingPolicyId(id, ClusterSpec.Id.from(endpoint.name()), zone));
- if (policy == null)
- throw new IllegalStateException(endpoint + " has no matching policy in " + policies);
+ 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"));
var cNameValue = controller.jobController().cloud().resolveCname(endpointName);
if ( ! cNameValue.map(policy.canonicalName()::equals).orElse(false)) {
@@ -848,11 +849,9 @@ public class InternalStepRunner implements StepRunner {
ZoneId zone = id.type().zone(controller.system());
boolean useTesterCertificate = useTesterCertificate(id);
- boolean useOsgiBasedTestRuntime = testerPlatformVersion(id).isAfter(new Version(7, 247, 11));
byte[] servicesXml = servicesXml( ! controller.system().isPublic(),
useTesterCertificate,
- useOsgiBasedTestRuntime,
testerResourcesFor(zone, spec.requireInstance(id.application().instance())),
controller.controllerConfig().steprunner().testerapp());
byte[] testPackage = controller.applications().applicationStore().getTester(id.application().tenant(), id.application().application(), version);
@@ -904,9 +903,8 @@ public class InternalStepRunner implements StepRunner {
}
/** Returns the generated services.xml content for the tester application. */
- static byte[] servicesXml(
- boolean systemUsesAthenz, boolean useTesterCertificate, boolean useOsgiBasedTestRuntime,
- NodeResources resources, ControllerConfig.Steprunner.Testerapp config) {
+ static byte[] servicesXml(boolean systemUsesAthenz, boolean useTesterCertificate,
+ NodeResources resources, ControllerConfig.Steprunner.Testerapp config) {
int jdiscMemoryGb = 2; // 2Gb memory for tester application (excessive?).
int jdiscMemoryPct = (int) Math.ceil(100 * jdiscMemoryGb / resources.memoryGb());
@@ -920,24 +918,6 @@ public class InternalStepRunner implements StepRunner {
String runtimeProviderClass = config.runtimeProviderClass();
String tenantCdBundle = config.tenantCdBundle();
- String extraJUnitComponents =
- "\n" +
- " <component id=\"" + runtimeProviderClass + "\" bundle=\"" + tenantCdBundle + "\" />\n" +
- "\n" +
- " <component id=\"com.yahoo.vespa.testrunner.JunitRunner\" bundle=\"vespa-osgi-testrunner\">\n" +
- " <config name=\"com.yahoo.vespa.testrunner.junit-test-runner\">\n" +
- " <artifactsPath>artifacts</artifactsPath>\n" +
- " <useAthenzCredentials>" + systemUsesAthenz + "</useAthenzCredentials>\n" +
- " </config>\n" +
- " </component>\n" +
- "\n" +
- " <component id=\"com.yahoo.vespa.testrunner.VespaCliTestRunner\" bundle=\"vespa-osgi-testrunner\">\n" +
- " <config name=\"com.yahoo.vespa.testrunner.vespa-cli-test-runner\">\n" +
- " <artifactsPath>artifacts</artifactsPath>\n" +
- " <useAthenzCredentials>" + systemUsesAthenz + "</useAthenzCredentials>\n" +
- " </config>\n" +
- " </component>\n";
-
String servicesXml =
"<?xml version='1.0' encoding='UTF-8'?>\n" +
"<services xmlns:deploy='vespa' version='1.0'>\n" +
@@ -955,7 +935,22 @@ public class InternalStepRunner implements StepRunner {
" <handler id=\"com.yahoo.vespa.testrunner.TestRunnerHandler\" bundle=\"vespa-osgi-testrunner\">\n" +
" <binding>http://*/tester/v1/*</binding>\n" +
" </handler>\n" +
- (useOsgiBasedTestRuntime ? extraJUnitComponents : "") +
+ "\n" +
+ " <component id=\"" + runtimeProviderClass + "\" bundle=\"" + tenantCdBundle + "\" />\n" +
+ "\n" +
+ " <component id=\"com.yahoo.vespa.testrunner.JunitRunner\" bundle=\"vespa-osgi-testrunner\">\n" +
+ " <config name=\"com.yahoo.vespa.testrunner.junit-test-runner\">\n" +
+ " <artifactsPath>artifacts</artifactsPath>\n" +
+ " <useAthenzCredentials>" + systemUsesAthenz + "</useAthenzCredentials>\n" +
+ " </config>\n" +
+ " </component>\n" +
+ "\n" +
+ " <component id=\"com.yahoo.vespa.testrunner.VespaCliTestRunner\" bundle=\"vespa-osgi-testrunner\">\n" +
+ " <config name=\"com.yahoo.vespa.testrunner.vespa-cli-test-runner\">\n" +
+ " <artifactsPath>artifacts</artifactsPath>\n" +
+ " <useAthenzCredentials>" + systemUsesAthenz + "</useAthenzCredentials>\n" +
+ " </config>\n" +
+ " </component>\n" +
"\n" +
" <nodes count=\"1\" allocated-memory=\"" + jdiscMemoryPct + "%\">\n" +
" " + resourceString + "\n" +
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java
index 6ba8ad5bf36..ebda767d79e 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/JobController.java
@@ -578,16 +578,8 @@ public class JobController {
});
}
- // TODO(mpolden): Eliminate duplication in this and ApplicationController#deactivate
public void deactivateTester(TesterId id, JobType type) {
- var zone = type.zone(controller.system());
- try {
- controller.serviceRegistry().configServer().deactivate(new DeploymentId(id.id(), zone));
- } finally {
- // Passing an empty DeploymentSpec here is fine as it's used for registering global endpoint names, and
- // tester instances have none.
- controller.routing().policies().refresh(id.id(), DeploymentSpec.empty, zone);
- }
+ controller.serviceRegistry().configServer().deactivate(new DeploymentId(id.id(), type.zone(controller.system())));
}
private void prunePackages(TenantAndApplicationId id) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java
index 47df7a9da92..2939d10f99e 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/MetricsReporter.java
@@ -16,7 +16,7 @@ import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics;
import com.yahoo.vespa.hosted.controller.auditlog.AuditLog;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentStatusList;
import com.yahoo.vespa.hosted.controller.deployment.JobList;
-import com.yahoo.vespa.hosted.controller.rotation.RotationLock;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationLock;
import com.yahoo.vespa.hosted.controller.versions.NodeVersion;
import com.yahoo.vespa.hosted.controller.versions.VersionStatus;
import com.yahoo.vespa.hosted.controller.versions.VespaVersion;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemRoutingPolicyMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemRoutingPolicyMaintainer.java
index 1d5d444a32c..5acb21917eb 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemRoutingPolicyMaintainer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/SystemRoutingPolicyMaintainer.java
@@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.controller.maintenance;
import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.vespa.hosted.controller.Controller;
+import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.application.SystemApplication;
import com.yahoo.vespa.hosted.controller.routing.RoutingPolicy;
@@ -25,7 +26,8 @@ public class SystemRoutingPolicyMaintainer extends ControllerMaintainer {
for (var zone : controller().zoneRegistry().zones().reachable().ids()) {
for (var application : SystemApplication.values()) {
if (!application.hasEndpoint()) continue;
- controller().routing().policies().refresh(application.id(), DeploymentSpec.empty, zone);
+ DeploymentId deployment = new DeploymentId(application.id(), zone);
+ controller().routing().of(deployment).configure(DeploymentSpec.empty);
}
}
return 1.0;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
index e8a7f7729fb..4b060846090 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java
@@ -31,9 +31,9 @@ import com.yahoo.vespa.hosted.controller.application.EndpointId;
import com.yahoo.vespa.hosted.controller.application.QuotaUsage;
import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
import com.yahoo.vespa.hosted.controller.metric.ApplicationMetrics;
-import com.yahoo.vespa.hosted.controller.rotation.RotationId;
-import com.yahoo.vespa.hosted.controller.rotation.RotationState;
-import com.yahoo.vespa.hosted.controller.rotation.RotationStatus;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationId;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationState;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationStatus;
import java.security.PublicKey;
import java.time.Instant;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
index 5cd5a70e4a4..1046737d860 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java
@@ -44,7 +44,6 @@ import com.yahoo.vespa.hosted.controller.Instance;
import com.yahoo.vespa.hosted.controller.LockedTenant;
import com.yahoo.vespa.hosted.controller.NotExistsException;
import com.yahoo.vespa.hosted.controller.api.application.v4.EnvironmentResource;
-import com.yahoo.vespa.hosted.controller.api.application.v4.model.EndpointStatus;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.ProtonMetrics;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.configserverbindings.RefeedAction;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.configserverbindings.RestartAction;
@@ -97,10 +96,11 @@ import com.yahoo.vespa.hosted.controller.maintenance.ResourceMeterMaintainer;
import com.yahoo.vespa.hosted.controller.notification.Notification;
import com.yahoo.vespa.hosted.controller.notification.NotificationSource;
import com.yahoo.vespa.hosted.controller.persistence.SupportAccessSerializer;
-import com.yahoo.vespa.hosted.controller.rotation.RotationId;
-import com.yahoo.vespa.hosted.controller.rotation.RotationState;
-import com.yahoo.vespa.hosted.controller.rotation.RotationStatus;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationId;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationState;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationStatus;
import com.yahoo.vespa.hosted.controller.routing.RoutingStatus;
+import com.yahoo.vespa.hosted.controller.routing.context.DeploymentRoutingContext;
import com.yahoo.vespa.hosted.controller.security.AccessControlRequests;
import com.yahoo.vespa.hosted.controller.security.Credentials;
import com.yahoo.vespa.hosted.controller.support.access.SupportAccess;
@@ -1292,18 +1292,22 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
request.getUri()).toString());
}
}
- // Add dummy values for not-yet-existent prod deployments.
- status.jobSteps().keySet().stream()
- .filter(job -> job.application().instance().equals(instance.name()))
- .filter(job -> job.type().isProduction() && job.type().isDeployment())
+ // Add dummy values for not-yet-existent prod deployments, and running dev/perf deployments.
+ Stream.concat(status.jobSteps().keySet().stream()
+ .filter(job -> job.application().instance().equals(instance.name()))
+ .filter(job -> job.type().isProduction() && job.type().isDeployment()),
+ controller.jobController().active(instance.id()).stream()
+ .map(run -> run.id().job())
+ .filter(job -> job.type().environment().isManuallyDeployed()))
.map(job -> job.type().zone(controller.system()))
.filter(zone -> ! instance.deployments().containsKey(zone))
.forEach(zone -> {
- Cursor deploymentObject = instancesArray.addObject();
- deploymentObject.setString("environment", zone.environment().value());
- deploymentObject.setString("region", zone.region().value());
+ Cursor deploymentObject = instancesArray.addObject();
+ deploymentObject.setString("environment", zone.environment().value());
+ deploymentObject.setString("region", zone.region().value());
});
+
// TODO jonmv: Remove when clients are updated
application.deployKeys().stream().findFirst().ifPresent(key -> object.setString("pemDeployKey", KeyUtils.toPem(key)));
@@ -1532,49 +1536,32 @@ public class ApplicationApiHandler extends AuditLoggingRequestHandler {
if (deployment == null) {
throw new NotExistsException(instance + " has no deployment in " + zone);
}
-
- // The order here matters because setGlobalRotationStatus involves an external request that may fail.
- // TODO(mpolden): Set only one of these when only one kind of global endpoints are supported per zone.
- var deploymentId = new DeploymentId(instance.id(), zone);
- setGlobalRotationStatus(deploymentId, inService, request);
- setGlobalEndpointStatus(deploymentId, inService, request);
-
+ DeploymentId deploymentId = new DeploymentId(instance.id(), zone);
+ RoutingStatus.Agent agent = isOperator(request) ? RoutingStatus.Agent.operator : RoutingStatus.Agent.tenant;
+ RoutingStatus.Value status = inService ? RoutingStatus.Value.in : RoutingStatus.Value.out;
+ controller.routing().of(deploymentId).setRoutingStatus(status, agent);
return new MessageResponse(Text.format("Successfully set %s in %s %s service",
instance.id().toShortString(), zone, inService ? "in" : "out of"));
}
- /** Set the global endpoint status for given deployment. This only applies to global endpoints backed by a cloud service */
- private void setGlobalEndpointStatus(DeploymentId deployment, boolean inService, HttpRequest request) {
- var agent = isOperator(request) ? RoutingStatus.Agent.operator : RoutingStatus.Agent.tenant;
- var status = inService ? RoutingStatus.Value.in : RoutingStatus.Value.out;
- controller.routing().policies().setRoutingStatus(deployment, status, agent);
- }
-
- /** Set the global rotation status for given deployment. This only applies to global endpoints backed by a rotation */
- private void setGlobalRotationStatus(DeploymentId deployment, boolean inService, HttpRequest request) {
- var requestData = toSlime(request.getData()).get();
- var reason = mandatory("reason", requestData).asString();
- var agent = isOperator(request) ? RoutingStatus.Agent.operator : RoutingStatus.Agent.tenant;
- long timestamp = controller.clock().instant().getEpochSecond();
- var status = inService ? EndpointStatus.Status.in : EndpointStatus.Status.out;
- var endpointStatus = new EndpointStatus(status, reason, agent.name(), timestamp);
- controller.routing().setGlobalRotationStatus(deployment, endpointStatus);
- }
-
private HttpResponse getGlobalRotationOverride(String tenantName, String applicationName, String instanceName, String environment, String region) {
DeploymentId deploymentId = new DeploymentId(ApplicationId.from(tenantName, applicationName, instanceName),
requireZone(environment, region));
Slime slime = new Slime();
Cursor array = slime.setObject().setArray("globalrotationoverride");
- controller.routing().globalRotationStatus(deploymentId)
- .forEach((endpoint, status) -> {
- array.addString(endpoint.upstreamIdOf(deploymentId));
- Cursor statusObject = array.addObject();
- statusObject.setString("status", status.getStatus().name());
- statusObject.setString("reason", status.getReason() == null ? "" : status.getReason());
- statusObject.setString("agent", status.getAgent() == null ? "" : status.getAgent());
- statusObject.setLong("timestamp", status.getEpoch());
- });
+ Optional<Endpoint> primaryEndpoint = controller.routing().readDeclaredEndpointsOf(deploymentId.applicationId())
+ .requiresRotation()
+ .primary();
+ if (primaryEndpoint.isPresent()) {
+ DeploymentRoutingContext context = controller.routing().of(deploymentId);
+ RoutingStatus status = context.routingStatus();
+ array.addString(primaryEndpoint.get().upstreamIdOf(deploymentId));
+ Cursor statusObject = array.addObject();
+ statusObject.setString("status", status.value().name());
+ statusObject.setString("reason", "");
+ statusObject.setString("agent", status.agent().name());
+ statusObject.setLong("timestamp", status.changedAt().getEpochSecond());
+ }
return new SlimeJsonResponse(slime);
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiHandler.java
index 45abf7f2946..226a7ca9561 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/routing/RoutingApiHandler.java
@@ -19,26 +19,26 @@ import com.yahoo.slime.Slime;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.Instance;
-import com.yahoo.vespa.hosted.controller.api.application.v4.model.EndpointStatus;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.role.Role;
import com.yahoo.vespa.hosted.controller.api.role.RoleDefinition;
import com.yahoo.vespa.hosted.controller.api.role.SecurityContext;
import com.yahoo.vespa.hosted.controller.application.Endpoint;
+import com.yahoo.vespa.hosted.controller.application.EndpointList;
import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
import com.yahoo.vespa.hosted.controller.auditlog.AuditLoggingRequestHandler;
import com.yahoo.vespa.hosted.controller.routing.RoutingStatus;
+import com.yahoo.vespa.hosted.controller.routing.context.DeploymentRoutingContext;
+import com.yahoo.vespa.hosted.controller.routing.context.RoutingContext;
import com.yahoo.yolean.Exceptions;
import java.net.URI;
-import java.time.Instant;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.logging.Level;
import java.util.stream.Collectors;
-import java.util.stream.Stream;
/**
* This implements the /routing/v1 API, which provides operators and tenants routing control at both zone- (operator
@@ -112,11 +112,8 @@ public class RoutingApiHandler extends AuditLoggingRequestHandler {
var deploymentsStatus = deployments.stream()
.collect(Collectors.toMap(
deploymentId -> deploymentId,
- deploymentId -> Stream.concat(
- directGlobalRoutingStatus(deploymentId).stream(),
- sharedGlobalRoutingStatus(deploymentId).stream()
- ).collect(Collectors.toList())
- ));
+ deploymentId -> controller.routing().of(deploymentId).routingStatus())
+ );
var slime = new Slime();
var root = slime.setObject();
@@ -125,11 +122,11 @@ public class RoutingApiHandler extends AuditLoggingRequestHandler {
var endpointRoot = endpointsRoot.addObject();
endpointToSlime(endpointRoot, endpoint);
var zonesRoot = endpointRoot.setArray("zones");
- endpoint.deployments().stream().sorted(Comparator.comparing(d -> d.zoneId().value())).forEach(deployment -> {
- deploymentsStatus.getOrDefault(deployment, List.of()).forEach(status -> {
- deploymentStatusToSlime(zonesRoot.addObject(), deployment, status, endpoint.routingMethod());
- });
- });
+ endpoint.deployments().stream().sorted(Comparator.comparing(d -> d.zoneId().value()))
+ .forEach(deployment -> {
+ RoutingStatus status = deploymentsStatus.get(deployment);
+ deploymentStatusToSlime(zonesRoot.addObject(), deployment, status, endpoint.routingMethod());
+ });
});
return new SlimeJsonResponse(slime);
@@ -211,13 +208,10 @@ public class RoutingApiHandler extends AuditLoggingRequestHandler {
}
private HttpResponse setZoneStatus(Path path, boolean in) {
- var zone = zoneFrom(path);
- if (exclusiveRoutingIn(zone)) {
- var status = in ? RoutingStatus.Value.in : RoutingStatus.Value.out;
- controller.routing().policies().setRoutingStatus(zone, status);
- } else {
- controller.serviceRegistry().configServer().setGlobalRotationStatus(zone, in);
- }
+ ZoneId zone = zoneFrom(path);
+ RoutingContext context = controller.routing().of(zone);
+ RoutingStatus.Value newStatus = in ? RoutingStatus.Value.in : RoutingStatus.Value.out;
+ context.setRoutingStatus(newStatus, RoutingStatus.Agent.operator);
return new MessageResponse("Set global routing status for deployments in " + zone + " to " +
(in ? "IN" : "OUT"));
}
@@ -231,16 +225,8 @@ public class RoutingApiHandler extends AuditLoggingRequestHandler {
}
private void toSlime(ZoneId zone, Cursor zoneObject) {
- if (exclusiveRoutingIn(zone)) {
- var zonePolicy = controller.routing().policies().get(zone);
- zoneStatusToSlime(zoneObject, zonePolicy.zone(), zonePolicy.routingStatus(), RoutingMethod.exclusive);
- } else {
- // Rotation status per zone only exposes in/out status, no agent or time of change.
- var in = controller.serviceRegistry().configServer().getGlobalRotationStatus(zone);
- var globalRouting = new RoutingStatus(in ? RoutingStatus.Value.in : RoutingStatus.Value.out,
- RoutingStatus.Agent.operator, Instant.EPOCH);
- zoneStatusToSlime(zoneObject, zone, globalRouting, RoutingMethod.shared);
- }
+ RoutingContext context = controller.routing().of(zone);
+ zoneStatusToSlime(zoneObject, zone, context.routingStatus(), context.routingMethod());
}
private HttpResponse setDeploymentStatus(Path path, boolean in, HttpRequest request) {
@@ -249,18 +235,7 @@ public class RoutingApiHandler extends AuditLoggingRequestHandler {
var status = in ? RoutingStatus.Value.in : RoutingStatus.Value.out;
var agent = isOperator(request) ? RoutingStatus.Agent.operator : RoutingStatus.Agent.tenant;
requireDeployment(deployment, instance);
-
- if (sharedRoutingIn(deployment.zoneId())) {
- // Set rotation status
- var endpointStatus = new EndpointStatus(in ? EndpointStatus.Status.in : EndpointStatus.Status.out,
- "",
- agent.name(),
- controller.clock().instant().getEpochSecond());
- controller.routing().setGlobalRotationStatus(deployment, endpointStatus);
- } else {
- // Set policy status
- controller.routing().policies().setRoutingStatus(deployment, status, agent);
- }
+ controller.routing().of(deployment).setRoutingStatus(status, agent);
return new MessageResponse("Set global routing status for " + deployment + " to " + (in ? "IN" : "OUT"));
}
@@ -279,66 +254,24 @@ public class RoutingApiHandler extends AuditLoggingRequestHandler {
var instances = instanceId == null
? application.instances().values()
: List.of(application.instances().get(instanceId.instance()));
+ EndpointList declaredEndpoints = controller.routing().declaredEndpointsOf(application);
for (var instance : instances) {
var zones = zoneId == null
? instance.deployments().keySet().stream().sorted(Comparator.comparing(ZoneId::value))
.collect(Collectors.toList())
: List.of(zoneId);
for (var zone : zones) {
- var deploymentId = requireDeployment(new DeploymentId(instance.id(), zone), instance);
- // Include status from rotation
- sharedGlobalRoutingStatus(deploymentId).ifPresent(status -> {
- deploymentStatusToSlime(deploymentsArray.addObject(), deploymentId, status, RoutingMethod.shared);
- });
-
- // Include status from routing policies
- directGlobalRoutingStatus(deploymentId).forEach(status -> {
- deploymentStatusToSlime(deploymentsArray.addObject(), deploymentId, status, RoutingMethod.exclusive);
- });
- }
- }
- }
-
- }
-
- private Optional<RoutingStatus> sharedGlobalRoutingStatus(DeploymentId deploymentId) {
- if (sharedRoutingIn(deploymentId.zoneId())) {
- var rotationStatus = controller.routing().globalRotationStatus(deploymentId);
- // Status is equal across all global endpoints, as the status is per deployment, not per endpoint.
- var endpointStatus = rotationStatus.values().stream().findFirst();
- if (endpointStatus.isPresent()) {
- var changedAt = Instant.ofEpochSecond(endpointStatus.get().getEpoch());
- RoutingStatus.Agent agent;
- try {
- agent = RoutingStatus.Agent.valueOf(endpointStatus.get().getAgent());
- } catch (IllegalArgumentException e) {
- agent = RoutingStatus.Agent.unknown;
+ DeploymentId deploymentId = requireDeployment(new DeploymentId(instance.id(), zone), instance);
+ DeploymentRoutingContext context = controller.routing().of(deploymentId);
+ if (declaredEndpoints.targets(deploymentId).isEmpty()) continue; // No declared endpoints point to this deployment
+ deploymentStatusToSlime(deploymentsArray.addObject(),
+ deploymentId,
+ context.routingStatus(),
+ context.routingMethod());
}
- var status = endpointStatus.get().getStatus() == EndpointStatus.Status.in
- ? RoutingStatus.Value.in
- : RoutingStatus.Value.out;
- return Optional.of(new RoutingStatus(status, agent, changedAt));
}
}
- return Optional.empty();
- }
-
- private List<RoutingStatus> directGlobalRoutingStatus(DeploymentId deploymentId) {
- return controller.routing().policies().get(deploymentId).values().stream()
- .filter(p -> ! p.instanceEndpoints().isEmpty()) // This policy does not apply to a global endpoint
- .filter(p -> exclusiveRoutingIn(p.id().zone()))
- .map(p -> p.status().routingStatus())
- .collect(Collectors.toList());
- }
-
- /** Returns whether given zone uses exclusive routing */
- private boolean exclusiveRoutingIn(ZoneId zone) {
- return controller.zoneRegistry().routingMethods(zone).contains(RoutingMethod.exclusive);
- }
- /** Returns whether given zone uses shared routing */
- private boolean sharedRoutingIn(ZoneId zone) {
- return controller.zoneRegistry().routingMethods(zone).stream().anyMatch(RoutingMethod::isShared);
}
private static void zoneStatusToSlime(Cursor object, ZoneId zone, RoutingStatus routingStatus, RoutingMethod method) {
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 2a39ed08014..d2dc2771160 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
@@ -215,7 +215,7 @@ public class RoutingPolicies {
Application application = controller.applications().requireApplication(routingTable.keySet().iterator().next().application());
Map<DeploymentId, Map<EndpointId, Integer>> targetWeights = targetWeights(application);
- Map<String, Set<AliasTarget>> targetsByEndpoint = new LinkedHashMap<>();
+ Map<Endpoint, Set<AliasTarget>> targetsByEndpoint = new LinkedHashMap<>();
for (Map.Entry<RoutingId, List<RoutingPolicy>> routeEntry : routingTable.entrySet()) {
RoutingId routingId = routeEntry.getKey();
EndpointList endpoints = controller.routing().declaredEndpointsOf(application)
@@ -230,19 +230,27 @@ public class RoutingPolicies {
for (var policy : routeEntry.getValue()) {
for (var target : endpoint.targets()) {
if (!policy.appliesTo(target.deployment())) continue;
+ if (policy.dnsZone().isEmpty()) continue; // Does not support ALIAS records
int weight = target.weight();
if (isConfiguredOut(policy, inactiveZones) && removableFromApplicationEndpoint(policy, application, targetWeights)) {
weight = 0;
}
WeightedAliasTarget weightedAliasTarget = new WeightedAliasTarget(policy.canonicalName(), policy.dnsZone().get(),
target.deployment().zoneId(), weight);
- targetsByEndpoint.computeIfAbsent(endpoint.dnsName(), (k) -> new LinkedHashSet<>())
+ targetsByEndpoint.computeIfAbsent(endpoint, (k) -> new LinkedHashSet<>())
.add(weightedAliasTarget);
}
}
}
targetsByEndpoint.forEach((applicationEndpoint, targets) -> {
- controller.nameServiceForwarder().createAlias(RecordName.from(applicationEndpoint), targets, Priority.normal);
+ ZoneId targetZone = applicationEndpoint.targets().stream()
+ .map(Endpoint.Target::deployment)
+ .map(DeploymentId::zoneId)
+ .findFirst()
+ .get();
+ nameServiceForwarderIn(targetZone).createAlias(RecordName.from(applicationEndpoint.dnsName()),
+ targets,
+ Priority.normal);
});
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/DeploymentRoutingContext.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/DeploymentRoutingContext.java
new file mode 100644
index 00000000000..28fbeee28f5
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/DeploymentRoutingContext.java
@@ -0,0 +1,154 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.routing.context;
+
+import com.yahoo.config.application.api.DeploymentSpec;
+import com.yahoo.config.provision.ClusterSpec;
+import com.yahoo.config.provision.zone.RoutingMethod;
+import com.yahoo.vespa.hosted.controller.LockedApplication;
+import com.yahoo.vespa.hosted.controller.RoutingController;
+import com.yahoo.vespa.hosted.controller.api.application.v4.model.EndpointStatus;
+import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServer;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.ContainerEndpoint;
+import com.yahoo.vespa.hosted.controller.application.Endpoint;
+import com.yahoo.vespa.hosted.controller.routing.RoutingPolicy;
+import com.yahoo.vespa.hosted.controller.routing.RoutingPolicyId;
+import com.yahoo.vespa.hosted.controller.routing.RoutingStatus;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * A deployment routing context, which extends {@link RoutingContext} to support routing configuration of a deployment.
+ *
+ * @author mpolden
+ */
+public abstract class DeploymentRoutingContext implements RoutingContext {
+
+ final DeploymentId deployment;
+ final RoutingController controller;
+ final RoutingMethod method;
+
+ public DeploymentRoutingContext(DeploymentId deployment, RoutingMethod method, RoutingController controller) {
+ this.deployment = Objects.requireNonNull(deployment);
+ this.controller = Objects.requireNonNull(controller);
+ this.method = Objects.requireNonNull(method);
+ }
+
+ /**
+ * Prepare routing configuration for the deployment in this context
+ *
+ * @return the container endpoints relevant for this deployment, as declared in deployment spec
+ */
+ public final Set<ContainerEndpoint> prepare(LockedApplication application) {
+ return controller.containerEndpointsOf(application, deployment.applicationId().instance(), deployment.zoneId());
+ }
+
+ /** Configure routing for the deployment in this context, using given deployment spec */
+ public final void configure(DeploymentSpec deploymentSpec) {
+ controller.policies().refresh(deployment.applicationId(), deploymentSpec, deployment.zoneId());
+ }
+
+ /** Routing method of this context */
+ public final RoutingMethod routingMethod() {
+ return method;
+ }
+
+ /** Read the routing policy for given cluster in this deployment */
+ public final Optional<RoutingPolicy> routingPolicy(ClusterSpec.Id cluster) {
+ RoutingPolicyId id = new RoutingPolicyId(deployment.applicationId(), cluster, deployment.zoneId());
+ return Optional.ofNullable(controller.policies().get(deployment).get(id));
+ }
+
+ /**
+ * Extension of a {@link DeploymentRoutingContext} for deployments using either {@link RoutingMethod#shared} or
+ * {@link RoutingMethod#sharedLayer4} routing.
+ */
+ public static class SharedDeploymentRoutingContext extends DeploymentRoutingContext {
+
+ private final Clock clock;
+ private final ConfigServer configServer;
+
+ public SharedDeploymentRoutingContext(DeploymentId deployment, RoutingController controller, ConfigServer configServer, Clock clock) {
+ super(deployment, RoutingMethod.shared, controller);
+ this.clock = Objects.requireNonNull(clock);
+ this.configServer = Objects.requireNonNull(configServer);
+ }
+
+ @Override
+ public void setRoutingStatus(RoutingStatus.Value value, RoutingStatus.Agent agent) {
+ EndpointStatus newStatus = new EndpointStatus(value == RoutingStatus.Value.in
+ ? EndpointStatus.Status.in
+ : EndpointStatus.Status.out,
+ "",
+ agent.name(),
+ clock.instant().getEpochSecond());
+ primaryEndpoint().ifPresent(endpoint -> {
+ try {
+ configServer.setGlobalRotationStatus(deployment, endpoint.upstreamIdOf(deployment), newStatus);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to set rotation status of " + endpoint + " in " + deployment, e);
+ }
+ });
+ }
+
+ @Override
+ public RoutingStatus routingStatus() {
+ Optional<EndpointStatus> status = primaryEndpoint().map(endpoint -> {
+ var upstreamName = endpoint.upstreamIdOf(deployment);
+ return configServer.getGlobalRotationStatus(deployment, upstreamName);
+ });
+ if (status.isEmpty()) return RoutingStatus.DEFAULT;
+ RoutingStatus.Agent agent;
+ try {
+ agent = RoutingStatus.Agent.valueOf(status.get().getAgent().toLowerCase());
+ } catch (IllegalArgumentException e) {
+ agent = RoutingStatus.Agent.unknown;
+ }
+ return new RoutingStatus(status.get().getStatus() == EndpointStatus.Status.in
+ ? RoutingStatus.Value.in
+ : RoutingStatus.Value.out,
+ agent,
+ Instant.ofEpochSecond(status.get().getEpoch()));
+ }
+
+ private Optional<Endpoint> primaryEndpoint() {
+ return controller.readDeclaredEndpointsOf(deployment.applicationId())
+ .requiresRotation()
+ .primary();
+ }
+
+ }
+
+ /**
+ * Implementation of a {@link DeploymentRoutingContext} for deployments using {@link RoutingMethod#exclusive}
+ * routing.
+ */
+ public static class ExclusiveDeploymentRoutingContext extends DeploymentRoutingContext {
+
+ public ExclusiveDeploymentRoutingContext(DeploymentId deployment, RoutingController controller) {
+ super(deployment, RoutingMethod.exclusive, controller);
+ }
+
+ @Override
+ public void setRoutingStatus(RoutingStatus.Value value, RoutingStatus.Agent agent) {
+ controller.policies().setRoutingStatus(deployment, value, agent);
+ }
+
+ @Override
+ public RoutingStatus routingStatus() {
+ // Status for a deployment applies to all clusters within the deployment, so we use the status from the
+ // first matching policy here
+ return controller.policies().get(deployment).values().stream()
+ .findFirst()
+ .map(RoutingPolicy::status)
+ .map(RoutingPolicy.Status::routingStatus)
+ .orElse(RoutingStatus.DEFAULT);
+ }
+
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/ExclusiveRoutingContext.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/ExclusiveRoutingContext.java
new file mode 100644
index 00000000000..e949c45f2fd
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/ExclusiveRoutingContext.java
@@ -0,0 +1,41 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.routing.context;
+
+import com.yahoo.config.provision.zone.RoutingMethod;
+import com.yahoo.config.provision.zone.ZoneId;
+import com.yahoo.vespa.hosted.controller.routing.RoutingPolicies;
+import com.yahoo.vespa.hosted.controller.routing.RoutingStatus;
+
+import java.util.Objects;
+
+/**
+ * An implementation of {@link RoutingContext} for a zone using {@link RoutingMethod#exclusive} routing.
+ *
+ * @author mpolden
+ */
+public class ExclusiveRoutingContext implements RoutingContext {
+
+ private final RoutingPolicies policies;
+ private final ZoneId zone;
+
+ public ExclusiveRoutingContext(ZoneId zone, RoutingPolicies policies) {
+ this.policies = Objects.requireNonNull(policies);
+ this.zone = Objects.requireNonNull(zone);
+ }
+
+ @Override
+ public void setRoutingStatus(RoutingStatus.Value value, RoutingStatus.Agent agent) {
+ policies.setRoutingStatus(zone, value);
+ }
+
+ @Override
+ public RoutingStatus routingStatus() {
+ return policies.get(zone).routingStatus();
+ }
+
+ @Override
+ public RoutingMethod routingMethod() {
+ return RoutingMethod.exclusive;
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/RoutingContext.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/RoutingContext.java
new file mode 100644
index 00000000000..6f43416b9b5
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/RoutingContext.java
@@ -0,0 +1,23 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.routing.context;
+
+import com.yahoo.config.provision.zone.RoutingMethod;
+import com.yahoo.vespa.hosted.controller.routing.RoutingStatus;
+
+/**
+ * Top-level interface for a routing context, which provides control of routing status for a deployment or zone.
+ *
+ * @author mpolden
+ */
+public interface RoutingContext {
+
+ /** Change the routing status for the zone or deployment represented by this context */
+ void setRoutingStatus(RoutingStatus.Value value, RoutingStatus.Agent agent);
+
+ /** Get the current routing status for the zone or deployment represented by this context */
+ RoutingStatus routingStatus();
+
+ /** Routing method used in this context */
+ RoutingMethod routingMethod();
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/SharedRoutingContext.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/SharedRoutingContext.java
new file mode 100644
index 00000000000..e38212d7f80
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/context/SharedRoutingContext.java
@@ -0,0 +1,48 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.routing.context;
+
+import com.yahoo.config.provision.zone.RoutingMethod;
+import com.yahoo.config.provision.zone.ZoneId;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServer;
+import com.yahoo.vespa.hosted.controller.routing.RoutingStatus;
+
+import java.time.Instant;
+import java.util.Objects;
+
+/**
+ * An implementation of {@link RoutingContext} for a zone, using either {@link RoutingMethod#shared} or
+ * {@link RoutingMethod#sharedLayer4} routing.
+ *
+ * @author mpolden
+ */
+public class SharedRoutingContext implements RoutingContext {
+
+ private final ConfigServer configServer;
+ private final ZoneId zone;
+
+ public SharedRoutingContext(ZoneId zone, ConfigServer configServer) {
+ this.configServer = Objects.requireNonNull(configServer);
+ this.zone = Objects.requireNonNull(zone);
+ }
+
+ @Override
+ public void setRoutingStatus(RoutingStatus.Value value, RoutingStatus.Agent agent) {
+ boolean in = value == RoutingStatus.Value.in;
+ configServer.setGlobalRotationStatus(zone, in);
+ }
+
+ @Override
+ public RoutingStatus routingStatus() {
+ boolean in = configServer.getGlobalRotationStatus(zone);
+ RoutingStatus.Value newValue = in ? RoutingStatus.Value.in : RoutingStatus.Value.out;
+ return new RoutingStatus(newValue,
+ RoutingStatus.Agent.operator,
+ Instant.EPOCH); // API does not support time of change
+ }
+
+ @Override
+ public RoutingMethod routingMethod() {
+ return RoutingMethod.shared;
+ }
+
+}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/Rotation.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/Rotation.java
index ca5d2d5915f..0cf7101cac0 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/Rotation.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/Rotation.java
@@ -1,5 +1,5 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.rotation;
+package com.yahoo.vespa.hosted.controller.routing.rotation;
import com.yahoo.text.Text;
import java.util.Objects;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationId.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationId.java
index 2b75777fbbd..4d97962a40a 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationId.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationId.java
@@ -1,5 +1,5 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.rotation;
+package com.yahoo.vespa.hosted.controller.routing.rotation;
import java.util.Objects;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationLock.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationLock.java
index fe9280b1193..36a43f80e9a 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationLock.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationLock.java
@@ -1,5 +1,5 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.rotation;
+package com.yahoo.vespa.hosted.controller.routing.rotation;
import com.yahoo.vespa.curator.Lock;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepository.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepository.java
index 5b24f39717b..961fdc6dd9c 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepository.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepository.java
@@ -1,5 +1,5 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.rotation;
+package com.yahoo.vespa.hosted.controller.routing.rotation;
import com.yahoo.config.application.api.DeploymentInstanceSpec;
import com.yahoo.config.application.api.DeploymentSpec;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationState.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationState.java
index 032f01433b3..19e816a0b51 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationState.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationState.java
@@ -1,5 +1,5 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.rotation;
+package com.yahoo.vespa.hosted.controller.routing.rotation;
/**
* The possible states of a global rotation.
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationStatus.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationStatus.java
index 1ddbd640e53..6d95ad9a230 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/rotation/RotationStatus.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationStatus.java
@@ -1,5 +1,5 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.rotation;
+package com.yahoo.vespa.hosted.controller.routing.rotation;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.vespa.hosted.controller.application.Deployment;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
index 9472801ef2c..30cdd1b8466 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java
@@ -19,7 +19,6 @@ import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.zone.RoutingMethod;
import com.yahoo.config.provision.zone.ZoneId;
import com.yahoo.path.Path;
-import com.yahoo.vespa.hosted.controller.api.application.v4.model.EndpointStatus;
import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId;
import com.yahoo.vespa.hosted.controller.api.integration.certificates.EndpointCertificateMetadata;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.ContainerEndpoint;
@@ -38,8 +37,10 @@ import com.yahoo.vespa.hosted.controller.deployment.DeploymentContext;
import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester;
import com.yahoo.vespa.hosted.controller.integration.ZoneApiMock;
import com.yahoo.vespa.hosted.controller.persistence.MockCuratorDb;
-import com.yahoo.vespa.hosted.controller.rotation.RotationId;
-import com.yahoo.vespa.hosted.controller.rotation.RotationLock;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationId;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationLock;
+import com.yahoo.vespa.hosted.controller.routing.RoutingStatus;
+import com.yahoo.vespa.hosted.controller.routing.context.DeploymentRoutingContext;
import com.yahoo.vespa.hosted.rotation.config.RotationsConfig;
import org.junit.Test;
@@ -214,22 +215,18 @@ public class ControllerTest {
// Check initial rotation status
var deployment1 = context.deploymentIdIn(zone1);
- var status1 = tester.controller().routing().globalRotationStatus(deployment1);
- assertEquals(1, status1.size());
- assertTrue("All upstreams are in", status1.values().stream().allMatch(es -> es.getStatus() == EndpointStatus.Status.in));
+ DeploymentRoutingContext routingContext = tester.controller().routing().of(deployment1);
+ RoutingStatus status1 = routingContext.routingStatus();
+ assertEquals(RoutingStatus.Value.in, status1.value());
// Set the deployment out of service in the global rotation
- var newStatus = new EndpointStatus(EndpointStatus.Status.out, "unit-test", ControllerTest.class.getSimpleName(), tester.clock().instant().getEpochSecond());
- tester.controller().routing().setGlobalRotationStatus(deployment1, newStatus);
- status1 = tester.controller().routing().globalRotationStatus(deployment1);
- assertEquals(1, status1.size());
- assertTrue("All upstreams are out", status1.values().stream().allMatch(es -> es.getStatus() == EndpointStatus.Status.out));
- assertTrue("Reason is set", status1.values().stream().allMatch(es -> es.getReason().equals("unit-test")));
+ routingContext.setRoutingStatus(RoutingStatus.Value.out, RoutingStatus.Agent.operator);
+ RoutingStatus status2 = routingContext.routingStatus();
+ assertEquals(RoutingStatus.Value.out, status2.value());
// Other deployment remains in
- var status2 = tester.controller().routing().globalRotationStatus(context.deploymentIdIn(zone2));
- assertEquals(1, status2.size());
- assertTrue("All upstreams are in", status2.values().stream().allMatch(es -> es.getStatus() == EndpointStatus.Status.in));
+ RoutingStatus status3 = tester.controller().routing().of(context.deploymentIdIn(zone2)).routingStatus();
+ assertEquals(RoutingStatus.Value.in, status3.value());
}
@Test
@@ -625,13 +622,13 @@ public class ControllerTest {
.instances("beta,main")
.region("us-west-1")
.region("us-east-3")
- .applicationEndpoint("a", "qrs", "us-west-1",
+ .applicationEndpoint("a", "default", "us-west-1",
Map.of(InstanceName.from("beta"), 2,
InstanceName.from("main"), 8))
- .applicationEndpoint("b", "qrs", "us-west-1",
+ .applicationEndpoint("b", "default", "us-west-1",
Map.of(InstanceName.from("beta"), 1,
InstanceName.from("main"), 1))
- .applicationEndpoint("c", "qrs", "us-east-3",
+ .applicationEndpoint("c", "default", "us-east-3",
Map.of(InstanceName.from("beta"), 4,
InstanceName.from("main"), 6))
.build();
@@ -640,11 +637,11 @@ public class ControllerTest {
// Endpoint names are passed to each deployment
DeploymentId usWest = context.deploymentIdIn(ZoneId.from("prod", "us-west-1"));
DeploymentId usEast = context.deploymentIdIn(ZoneId.from("prod", "us-east-3"));
- Map<DeploymentId, List<String>> deploymentEndpoints = Map.of(usWest, List.of("a--app1--tenant1.us-west-1-r.vespa.oath.cloud", "b--app1--tenant1.us-west-1-r.vespa.oath.cloud"),
- usEast, List.of("c--app1--tenant1.us-east-3-r.vespa.oath.cloud"));
+ Map<DeploymentId, List<String>> deploymentEndpoints = Map.of(usWest, List.of("a.app1.tenant1.us-west-1-r.vespa.oath.cloud", "b.app1.tenant1.us-west-1-r.vespa.oath.cloud"),
+ usEast, List.of("c.app1.tenant1.us-east-3-r.vespa.oath.cloud"));
deploymentEndpoints.forEach((zone, endpointNames) -> {
assertEquals("Endpoint names are passed to config server in " + zone,
- Set.of(new ContainerEndpoint("qrs", "application",
+ Set.of(new ContainerEndpoint("default", "application",
endpointNames)),
tester.configServer().containerEndpoints().get(zone));
});
@@ -653,21 +650,21 @@ public class ControllerTest {
// DNS records are created for each endpoint
Set<Record> records = tester.controllerTester().nameService().records();
assertEquals(Set.of(new Record(Record.Type.CNAME,
- RecordName.from("a--app1--tenant1.us-west-1-r.vespa.oath.cloud"),
+ RecordName.from("a.app1.tenant1.us-west-1-r.vespa.oath.cloud"),
RecordData.from("vip.prod.us-west-1.")),
new Record(Record.Type.CNAME,
- RecordName.from("b--app1--tenant1.us-west-1-r.vespa.oath.cloud"),
+ RecordName.from("b.app1.tenant1.us-west-1-r.vespa.oath.cloud"),
RecordData.from("vip.prod.us-west-1.")),
new Record(Record.Type.CNAME,
- RecordName.from("c--app1--tenant1.us-east-3-r.vespa.oath.cloud"),
+ RecordName.from("c.app1.tenant1.us-east-3-r.vespa.oath.cloud"),
RecordData.from("vip.prod.us-east-3."))),
records);
List<String> endpointDnsNames = tester.controller().routing().declaredEndpointsOf(context.application())
.scope(Endpoint.Scope.application)
.mapToList(Endpoint::dnsName);
- assertEquals(List.of("a--app1--tenant1.us-west-1-r.vespa.oath.cloud",
- "b--app1--tenant1.us-west-1-r.vespa.oath.cloud",
- "c--app1--tenant1.us-east-3-r.vespa.oath.cloud"),
+ assertEquals(List.of("a.app1.tenant1.us-west-1-r.vespa.oath.cloud",
+ "b.app1.tenant1.us-west-1-r.vespa.oath.cloud",
+ "c.app1.tenant1.us-east-3-r.vespa.oath.cloud"),
endpointDnsNames);
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
index 11086ff7663..5cf554f2c01 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/InternalStepRunnerTest.java
@@ -521,22 +521,16 @@ public class InternalStepRunnerTest {
}
@Test
- public void generates_correct_services_xml_using_osgi_based_runtime() {
- generates_correct_services_xml("test_runner_services.xml-cd-osgi", true);
+ public void generates_correct_services_xml() {
+ generates_correct_services_xml("test_runner_services.xml-cd");
}
- @Test
- public void generates_correct_services_xml_using_legacy_runtime() {
- generates_correct_services_xml("test_runner_services.xml-cd-legacy", false);
- }
-
- private void generates_correct_services_xml(String filenameExpectedOutput, boolean useOsgiBasedRuntime) {
+ private void generates_correct_services_xml(String filenameExpectedOutput) {
ControllerConfig.Steprunner.Testerapp config = new ControllerConfig.Steprunner.Testerapp.Builder().build();
assertFile(filenameExpectedOutput,
new String(InternalStepRunner.servicesXml(
true,
false,
- useOsgiBasedRuntime,
new NodeResources(2, 12, 75, 1, NodeResources.DiskSpeed.fast, NodeResources.StorageType.local),
config)));
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
index f1421b5affd..b33f8f6f7e7 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java
@@ -23,9 +23,9 @@ import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics;
import com.yahoo.vespa.hosted.controller.application.QuotaUsage;
import com.yahoo.vespa.hosted.controller.application.TenantAndApplicationId;
import com.yahoo.vespa.hosted.controller.metric.ApplicationMetrics;
-import com.yahoo.vespa.hosted.controller.rotation.RotationId;
-import com.yahoo.vespa.hosted.controller.rotation.RotationState;
-import com.yahoo.vespa.hosted.controller.rotation.RotationStatus;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationId;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationState;
+import com.yahoo.vespa.hosted.controller.routing.rotation.RotationStatus;
import org.junit.Test;
import java.nio.file.Files;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
index ae6232ae419..afd67824ee8 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java
@@ -69,6 +69,7 @@ import com.yahoo.vespa.hosted.controller.notification.NotificationSource;
import com.yahoo.vespa.hosted.controller.restapi.ContainerTester;
import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest;
import com.yahoo.vespa.hosted.controller.routing.RoutingStatus;
+import com.yahoo.vespa.hosted.controller.routing.context.DeploymentRoutingContext;
import com.yahoo.vespa.hosted.controller.security.AthenzCredentials;
import com.yahoo.vespa.hosted.controller.security.AthenzTenantSpec;
import com.yahoo.vespa.hosted.controller.support.access.SupportAccessGrant;
@@ -1868,13 +1869,12 @@ public class ApplicationApiTest extends ControllerContainerTest {
}
private void assertGlobalRouting(DeploymentId deployment, RoutingStatus.Value value, RoutingStatus.Agent agent) {
- var changedAt = tester.controller().clock().instant();
- var westPolicies = tester.controller().routing().policies().get(deployment);
- assertEquals(1, westPolicies.size());
- var westPolicy = westPolicies.values().iterator().next();
- assertEquals(value, westPolicy.status().routingStatus().value());
- assertEquals(agent, westPolicy.status().routingStatus().agent());
- assertEquals(changedAt.truncatedTo(ChronoUnit.MILLIS), westPolicy.status().routingStatus().changedAt());
+ Instant changedAt = tester.controller().clock().instant();
+ DeploymentRoutingContext context = tester.controller().routing().of(deployment);
+ RoutingStatus status = context.routingStatus();
+ assertEquals(value, status.value());
+ assertEquals(agent, status.agent());
+ assertEquals(changedAt.truncatedTo(ChronoUnit.SECONDS), status.changedAt());
}
private static class RequestBuilder implements Supplier<Request> {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json
index fb6088f54b8..ab2a3bf945c 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/deployment.json
@@ -24,9 +24,9 @@
{
"cluster": "foo",
"tls": true,
- "url": "https://a0--application1--tenant1.us-central-1-r.vespa.oath.cloud:4443/",
+ "url": "https://a0.application1.tenant1.us-central-1-r.vespa.oath.cloud/",
"scope": "application",
- "routingMethod": "shared",
+ "routingMethod": "sharedLayer4",
"legacy": false
}
],
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/global-rotation-get.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/global-rotation-get.json
index 934e0cf43b9..de2266fd197 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/global-rotation-get.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/global-rotation-get.json
@@ -4,7 +4,7 @@
{
"status": "in",
"reason": "",
- "agent": "",
+ "agent": "unknown",
"timestamp": 1497618757
}
]
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json
index 409e97b063c..62ad3a2db7e 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/prod-us-central-1.json
@@ -27,9 +27,9 @@
{
"cluster": "foo",
"tls": true,
- "url": "https://a0--application1--tenant1.us-central-1-r.vespa.oath.cloud:4443/",
+ "url": "https://a0.application1.tenant1.us-central-1-r.vespa.oath.cloud/",
"scope": "application",
- "routingMethod": "shared",
+ "routingMethod": "sharedLayer4",
"legacy": false
}
],
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java
index e7c2eacbd02..9a3ac8b547d 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/rotation/RotationRepositoryTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/routing/rotation/RotationRepositoryTest.java
@@ -1,5 +1,5 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.rotation;
+package com.yahoo.vespa.hosted.controller.routing.rotation;
import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.SystemName;
diff --git a/controller-server/src/test/resources/test_runner_services.xml-cd-osgi b/controller-server/src/test/resources/test_runner_services.xml-cd
index 634137e3fb6..634137e3fb6 100644
--- a/controller-server/src/test/resources/test_runner_services.xml-cd-osgi
+++ b/controller-server/src/test/resources/test_runner_services.xml-cd
diff --git a/controller-server/src/test/resources/test_runner_services.xml-cd-legacy b/controller-server/src/test/resources/test_runner_services.xml-cd-legacy
deleted file mode 100644
index c6046479934..00000000000
--- a/controller-server/src/test/resources/test_runner_services.xml-cd-legacy
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<services xmlns:deploy='vespa' version='1.0'>
- <container version='1.0' id='tester'>
-
- <component id="com.yahoo.vespa.hosted.testrunner.TestRunner" bundle="vespa-testrunner-components">
- <config name="com.yahoo.vespa.hosted.testrunner.test-runner">
- <artifactsPath>artifacts</artifactsPath>
- <surefireMemoryMb>5120</surefireMemoryMb>
- <useAthenzCredentials>true</useAthenzCredentials>
- <useTesterCertificate>false</useTesterCertificate>
- </config>
- </component>
-
- <handler id="com.yahoo.vespa.testrunner.TestRunnerHandler" bundle="vespa-osgi-testrunner">
- <binding>http://*/tester/v1/*</binding>
- </handler>
-
- <nodes count="1" allocated-memory="17%">
- <resources vcpu="2.00" memory="12.00Gb" disk="75.00Gb" disk-speed="fast" storage-type="local"/>
- </nodes>
- </container>
-</services>