summaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorJon Marius Venstad <venstad@gmail.com>2020-01-02 12:49:54 +0100
committerJon Marius Venstad <venstad@gmail.com>2020-01-07 15:28:23 +0100
commitb2d9b9b58d16c618b47b2932ef1fc85b89c8ce51 (patch)
tree93ad38aeaabb3f644f4fc6a0b2f1cdb9ed631823 /controller-server
parent95efcfd4d165b064c7c60284ded1df7d481c6132 (diff)
Compute outstanding change, instead of keeping it in sync
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java12
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java44
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationList.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java25
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatusList.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java65
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployer.java7
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializer.java5
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java10
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java12
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java10
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/ApplicationSerializerTest.java3
12 files changed, 88 insertions, 112 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java
index c75f3102772..b962b260bf9 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Application.java
@@ -46,7 +46,6 @@ public class Application {
private final Optional<ApplicationVersion> latestVersion;
private final OptionalLong projectId;
private final Change change;
- private final Change outstandingChange;
private final Optional<IssueId> deploymentIssueId;
private final Optional<IssueId> ownershipIssueId;
private final Optional<User> owner;
@@ -57,14 +56,14 @@ public class Application {
/** Creates an empty application. */
public Application(TenantAndApplicationId id, Instant now) {
- this(id, now, DeploymentSpec.empty, ValidationOverrides.empty, Change.empty(), Change.empty(),
+ this(id, now, DeploymentSpec.empty, ValidationOverrides.empty, Change.empty(),
Optional.empty(), Optional.empty(), Optional.empty(), OptionalInt.empty(),
new ApplicationMetrics(0, 0), Set.of(), OptionalLong.empty(), Optional.empty(), List.of());
}
// DO NOT USE! For serialization purposes, only.
public Application(TenantAndApplicationId id, Instant createdAt, DeploymentSpec deploymentSpec, ValidationOverrides validationOverrides,
- Change change, Change outstandingChange, Optional<IssueId> deploymentIssueId, Optional<IssueId> ownershipIssueId, Optional<User> owner,
+ Change change, Optional<IssueId> deploymentIssueId, Optional<IssueId> ownershipIssueId, Optional<User> owner,
OptionalInt majorVersion, ApplicationMetrics metrics, Set<PublicKey> deployKeys, OptionalLong projectId,
Optional<ApplicationVersion> latestVersion, Collection<Instance> instances) {
this.id = Objects.requireNonNull(id, "id cannot be null");
@@ -72,7 +71,6 @@ public class Application {
this.deploymentSpec = Objects.requireNonNull(deploymentSpec, "deploymentSpec cannot be null");
this.validationOverrides = Objects.requireNonNull(validationOverrides, "validationOverrides cannot be null");
this.change = Objects.requireNonNull(change, "change cannot be null");
- this.outstandingChange = Objects.requireNonNull(outstandingChange, "outstandingChange cannot be null");
this.deploymentIssueId = Objects.requireNonNull(deploymentIssueId, "deploymentIssueId cannot be null");
this.ownershipIssueId = Objects.requireNonNull(ownershipIssueId, "ownershipIssueId cannot be null");
this.owner = Objects.requireNonNull(owner, "owner cannot be null");
@@ -124,12 +122,6 @@ public class Application {
*/
public Change change() { return change; }
- /**
- * Returns whether this has an outstanding change (in the source repository), which
- * has currently not started deploying (because a deployment is (or was) already in progress
- */
- public Change outstandingChange() { return outstandingChange; }
-
/** Returns ID of any open deployment issue filed for this */
public Optional<IssueId> deploymentIssueId() {
return deploymentIssueId;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java
index fa81a990c70..09666110f70 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/LockedApplication.java
@@ -37,7 +37,6 @@ public class LockedApplication {
private final DeploymentSpec deploymentSpec;
private final ValidationOverrides validationOverrides;
private final Change change;
- private final Change outstandingChange;
private final Optional<IssueId> deploymentIssueId;
private final Optional<IssueId> ownershipIssueId;
private final Optional<User> owner;
@@ -57,13 +56,13 @@ public class LockedApplication {
LockedApplication(Application application, Lock lock) {
this(Objects.requireNonNull(lock, "lock cannot be null"), application.id(), application.createdAt(),
application.deploymentSpec(), application.validationOverrides(), application.change(),
- application.outstandingChange(), application.deploymentIssueId(), application.ownershipIssueId(),
+ application.deploymentIssueId(), application.ownershipIssueId(),
application.owner(), application.majorVersion(), application.metrics(), application.deployKeys(),
application.projectId(), application.latestVersion(), application.instances());
}
private LockedApplication(Lock lock, TenantAndApplicationId id, Instant createdAt, DeploymentSpec deploymentSpec,
- ValidationOverrides validationOverrides, Change change, Change outstandingChange,
+ ValidationOverrides validationOverrides, Change change,
Optional<IssueId> deploymentIssueId, Optional<IssueId> ownershipIssueId, Optional<User> owner,
OptionalInt majorVersion, ApplicationMetrics metrics, Set<PublicKey> deployKeys,
OptionalLong projectId, Optional<ApplicationVersion> latestVersion,
@@ -74,7 +73,6 @@ public class LockedApplication {
this.deploymentSpec = deploymentSpec;
this.validationOverrides = validationOverrides;
this.change = change;
- this.outstandingChange = outstandingChange;
this.deploymentIssueId = deploymentIssueId;
this.ownershipIssueId = ownershipIssueId;
this.owner = owner;
@@ -88,7 +86,7 @@ public class LockedApplication {
/** Returns a read-only copy of this */
public Application get() {
- return new Application(id, createdAt, deploymentSpec, validationOverrides, change, outstandingChange,
+ return new Application(id, createdAt, deploymentSpec, validationOverrides, change,
deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys,
projectId, latestVersion, instances.values());
}
@@ -96,7 +94,7 @@ public class LockedApplication {
public LockedApplication withNewInstance(InstanceName instance) {
var instances = new HashMap<>(this.instances);
instances.put(instance, new Instance(id.instance(instance)));
- return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change, outstandingChange,
+ return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change,
deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys,
projectId, latestVersion, instances);
}
@@ -104,7 +102,7 @@ public class LockedApplication {
public LockedApplication with(InstanceName instance, UnaryOperator<Instance> modification) {
var instances = new HashMap<>(this.instances);
instances.put(instance, modification.apply(instances.get(instance)));
- return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change, outstandingChange,
+ return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change,
deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys,
projectId, latestVersion, instances);
}
@@ -112,75 +110,69 @@ public class LockedApplication {
public LockedApplication without(InstanceName instance) {
var instances = new HashMap<>(this.instances);
instances.remove(instance);
- return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change, outstandingChange,
+ return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change,
deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys,
projectId, latestVersion, instances);
}
public LockedApplication withNewSubmission(ApplicationVersion latestVersion) {
- return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change, outstandingChange,
+ return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change,
deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys,
projectId, Optional.of(latestVersion), instances);
}
public LockedApplication withProjectId(OptionalLong projectId) {
- return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change, outstandingChange,
+ return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change,
deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys,
projectId, latestVersion, instances);
}
public LockedApplication withDeploymentIssueId(IssueId issueId) {
- return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change, outstandingChange,
+ return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change,
Optional.ofNullable(issueId), ownershipIssueId, owner, majorVersion, metrics, deployKeys,
projectId, latestVersion, instances);
}
public LockedApplication with(DeploymentSpec deploymentSpec) {
- return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change, outstandingChange,
+ return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change,
deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys,
projectId, latestVersion, instances);
}
public LockedApplication with(ValidationOverrides validationOverrides) {
- return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change, outstandingChange,
+ return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change,
deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys,
projectId, latestVersion, instances);
}
public LockedApplication withChange(Change change) {
- return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change, outstandingChange,
- deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys,
- projectId, latestVersion, instances);
- }
-
- public LockedApplication withOutstandingChange(Change outstandingChange) {
- return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change, outstandingChange,
+ return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change,
deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys,
projectId, latestVersion, instances);
}
public LockedApplication withOwnershipIssueId(IssueId issueId) {
- return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change, outstandingChange,
+ return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change,
deploymentIssueId, Optional.of(issueId), owner, majorVersion, metrics, deployKeys,
projectId, latestVersion, instances);
}
public LockedApplication withOwner(User owner) {
- return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change, outstandingChange,
+ return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change,
deploymentIssueId, ownershipIssueId, Optional.of(owner), majorVersion, metrics, deployKeys,
projectId, latestVersion, instances);
}
/** Set a major version for this, or set to null to remove any major version override */
public LockedApplication withMajorVersion(Integer majorVersion) {
- return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change, outstandingChange,
+ return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change,
deploymentIssueId, ownershipIssueId, owner,
majorVersion == null ? OptionalInt.empty() : OptionalInt.of(majorVersion),
metrics, deployKeys, projectId, latestVersion, instances);
}
public LockedApplication with(ApplicationMetrics metrics) {
- return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change, outstandingChange,
+ return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change,
deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, deployKeys,
projectId, latestVersion, instances);
}
@@ -188,7 +180,7 @@ public class LockedApplication {
public LockedApplication withDeployKey(PublicKey pemDeployKey) {
Set<PublicKey> keys = new LinkedHashSet<>(deployKeys);
keys.add(pemDeployKey);
- return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change, outstandingChange,
+ return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change,
deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, keys,
projectId, latestVersion, instances);
}
@@ -196,7 +188,7 @@ public class LockedApplication {
public LockedApplication withoutDeployKey(PublicKey pemDeployKey) {
Set<PublicKey> keys = new LinkedHashSet<>(deployKeys);
keys.remove(pemDeployKey);
- return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change, outstandingChange,
+ return new LockedApplication(lock, id, createdAt, deploymentSpec, validationOverrides, change,
deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics, keys,
projectId, latestVersion, instances);
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationList.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationList.java
index 7926531d1fa..e82029e53bf 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationList.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/application/ApplicationList.java
@@ -49,11 +49,6 @@ public class ApplicationList extends AbstractFilteringList<Application, Applicat
// ----------------------------------- Filters
- /** Returns the subset of applications which have changes left to deploy; blocked, or deploying */
- public ApplicationList withChanges() {
- return matching(application -> application.change().hasTargets() || application.outstandingChange().hasTargets());
- }
-
/** Returns the subset of applications which are currently deploying a change */
public ApplicationList deploying() {
return matching(application -> application.change().hasTargets());
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java
index e574b994870..6948e1f1dd0 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatus.java
@@ -120,11 +120,11 @@ public class DeploymentStatus {
/** Returns the set of jobs that need to run for the application's current change to be considered complete. */
public Map<JobId, List<Versions>> jobsToRun() {
Map<JobId, List<Versions>> jobs = jobsToRun(application().change());
- if (application.outstandingChange().isEmpty())
+ if (outstandingChange().isEmpty())
return jobs;
// Add test jobs for any outstanding change.
- var testJobs = jobsToRun(application.outstandingChange().onTopOf(application.change()))
+ var testJobs = jobsToRun(outstandingChange().onTopOf(application.change()))
.entrySet().stream()
.filter(entry -> ! entry.getKey().type().isProduction());
@@ -155,16 +155,27 @@ public class DeploymentStatus {
}
/**
+ * Returns the change of this application's latest submission, if this upgrades any of its production deployments,
+ * and has not yet started rolling out, due to some other change or a block window being present at the time of submission.
+ */
+ public Change outstandingChange() {
+ return application.latestVersion().map(Change::of)
+ .filter(change -> application.change().application().map(change::upgrades).orElse(true))
+ .filter(change -> ! jobsToRun(change).isEmpty())
+ .orElse(Change.empty());
+ }
+
+ /**
* True if the job has already been triggered on the given versions, or if all test types (systemTest, stagingTest),
* restricted to the job's instance if declared in that instance, have successful runs on the given versions.
*/
public boolean isTested(JobId job, Versions versions) {
return allJobs.triggeredOn(versions).get(job).isPresent()
- || Stream.of(systemTest, stagingTest)
- .noneMatch(testType -> declaredTest(job.application(), testType).map(__ -> allJobs.instance(job.application().instance()))
- .orElse(allJobs)
- .type(testType)
- .successOn(versions).isEmpty());
+ || Stream.of(systemTest, stagingTest)
+ .noneMatch(testType -> declaredTest(job.application(), testType).map(__ -> allJobs.instance(job.application().instance()))
+ .orElse(allJobs)
+ .type(testType)
+ .successOn(versions).isEmpty());
}
public Map<JobId, Versions> productionJobs(Change change) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatusList.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatusList.java
index 6f4d947a3c5..c3f6947ee4a 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatusList.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentStatusList.java
@@ -79,7 +79,7 @@ public class DeploymentStatusList extends AbstractFilteringList<DeploymentStatus
/** Returns the subset of applications which have changes left to deploy; blocked, or deploying */
public DeploymentStatusList withChanges() {
- return matching(status -> status.application().change().hasTargets() || status.application().outstandingChange().hasTargets());
+ return matching(status -> status.application().change().hasTargets() || status.outstandingChange().hasTargets());
}
/** Returns the subset of applications which are currently deploying a change */
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java
index 1e02c9751d4..dfd25c57e70 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTrigger.java
@@ -81,14 +81,11 @@ public class DeploymentTrigger {
applications().lockApplicationOrThrow(id, application -> {
if (acceptNewApplicationVersion(application.get())) {
- application = application.withChange(application.get().change().with(version))
- .withOutstandingChange(Change.empty());
+ application = application.withChange(application.get().change().with(version));
for (Run run : jobs.active(id))
if ( ! run.id().type().environment().isManuallyDeployed())
jobs.abort(run.id());
}
- else
- application = application.withOutstandingChange(Change.of(version));
application = application.withProjectId(OptionalLong.of(projectId));
application = application.withNewSubmission(version);
@@ -191,8 +188,6 @@ public class DeploymentTrigger {
/** Overrides the given application's platform and application changes with any contained in the given change. */
public void forceChange(TenantAndApplicationId applicationId, Change change) {
applications().lockApplicationOrThrow(applicationId, application -> {
- if (change.application().isPresent())
- application = application.withOutstandingChange(Change.empty());
applications().store(application.withChange(change.onTopOf(application.get().change())));
});
}
@@ -229,49 +224,45 @@ public class DeploymentTrigger {
/** Returns the set of all jobs which have changes to propagate from the upstream steps. */
private List<Job> computeReadyJobs() {
- return ApplicationList.from(applications().asList())
- .withProjectId() // Need to keep this, as we have applications with deployment spec that shouldn't be orchestrated.
- .withChanges()
- .withDeploymentSpec()
- .idList().stream()
- .map(this::computeReadyJobs)
- .flatMap(Collection::stream)
- .collect(toList());
+ return jobs.deploymentStatuses(ApplicationList.from(applications().asList())
+ .withProjectId() // Need to keep this, as we have applications with deployment spec that shouldn't be orchestrated.
+ .withDeploymentSpec())
+ .withChanges()
+ .asList().stream()
+ .map(this::computeReadyJobs)
+ .flatMap(Collection::stream)
+ .collect(toList());
}
/**
* Finds the next step to trigger for the given application, if any, and returns these as a list.
*/
- private List<Job> computeReadyJobs(TenantAndApplicationId id) {
+ private List<Job> computeReadyJobs(DeploymentStatus status) {
List<Job> jobs = new ArrayList<>();
- applications().getApplication(id).map(controller.jobController()::deploymentStatus).ifPresent(status -> {
- status.jobsToRun().forEach((job, versionsList) -> {
- for (Versions versions : versionsList)
- status.jobSteps().get(job).readyAt(status.application().change(), versions)
- .filter(readyAt -> ! clock.instant().isBefore(readyAt))
- .ifPresent(readyAt -> {
- if ( ! ( isSuspendedInAnotherZone(status.application().require(job.application().instance()),
- job.type().zone(controller.system()))
- && job.type().environment() == Environment.prod)
- && ! status.jobs().get(job).get().isRunning())
- jobs.add(deploymentJob(status.application().require(job.application().instance()),
- versions,
- status.application().change(),
- job.type(),
- status.instanceJobs(job.application().instance()).get(job.type()),
- "unknown reason",
- readyAt));
- });
+ status.jobsToRun().forEach((job, versionsList) -> {
+ for (Versions versions : versionsList)
+ status.jobSteps().get(job).readyAt(status.application().change(), versions)
+ .filter(readyAt -> ! clock.instant().isBefore(readyAt))
+ .filter(__ -> ! status.jobs().get(job).get().isRunning())
+ .filter(__ -> ! (job.type().isProduction() && isSuspendedInAnotherZone(status.application(), job)))
+ .ifPresent(readyAt -> {
+ jobs.add(deploymentJob(status.application().require(job.application().instance()),
+ versions,
+ status.application().change(),
+ job.type(),
+ status.instanceJobs(job.application().instance()).get(job.type()),
+ "unknown reason",
+ readyAt));
});
});
return Collections.unmodifiableList(jobs);
}
/** Returns whether given job should be triggered */
- private boolean isSuspendedInAnotherZone(Instance instance, ZoneId zone) {
- for (Deployment deployment : instance.productionDeployments().values()) {
- if ( ! deployment.zone().equals(zone)
- && controller.applications().isSuspended(new DeploymentId(instance.id(), deployment.zone())))
+ private boolean isSuspendedInAnotherZone(Application application, JobId job) {
+ for (Deployment deployment : application.require(job.application().instance()).productionDeployments().values()) {
+ if ( ! deployment.zone().equals(job.type().zone(controller.system()))
+ && controller.applications().isSuspended(new DeploymentId(job.application(), deployment.zone())))
return true;
}
return false;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployer.java
index b130f7107dd..d398012a71b 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployer.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployer.java
@@ -4,6 +4,7 @@ package com.yahoo.vespa.hosted.controller.maintenance;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.Instance;
import com.yahoo.vespa.hosted.controller.Controller;
+import com.yahoo.vespa.hosted.controller.application.Change;
import java.time.Duration;
@@ -21,11 +22,11 @@ public class OutstandingChangeDeployer extends Maintainer {
@Override
protected void maintain() {
for (Application application : controller().applications().asList()) {
- if ( application.outstandingChange().hasTargets()
+ Change change = controller().jobController().deploymentStatus(application).outstandingChange();
+ if ( change.hasTargets()
&& application.deploymentSpec().instances().stream()
.allMatch(instance -> instance.canChangeRevisionAt(controller().clock().instant()))) {
- controller().applications().deploymentTrigger().triggerChange(application.id(),
- application.outstandingChange());
+ controller().applications().deploymentTrigger().triggerChange(application.id(), change);
}
}
}
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 6c500ff6402..4d6c21fcd1f 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
@@ -83,7 +83,6 @@ public class ApplicationSerializer {
private static final String projectIdField = "projectId";
private static final String latestVersionField = "latestVersion";
private static final String pinnedField = "pinned";
- private static final String outstandingChangeField = "outstandingChangeField";
private static final String deploymentIssueField = "deploymentIssueId";
private static final String ownershipIssueIdField = "ownershipIssueId";
private static final String ownerField = "confirmedOwner";
@@ -175,7 +174,6 @@ public class ApplicationSerializer {
application.deploymentIssueId().ifPresent(jiraIssueId -> root.setString(deploymentIssueField, jiraIssueId.value()));
application.ownershipIssueId().ifPresent(issueId -> root.setString(ownershipIssueIdField, issueId.value()));
toSlime(application.change(), root, deployingField);
- toSlime(application.outstandingChange(), root, outstandingChangeField);
application.owner().ifPresent(owner -> root.setString(ownerField, owner.username()));
application.majorVersion().ifPresent(majorVersion -> root.setLong(majorVersionField, majorVersion));
root.setDouble(queryQualityField, application.metrics().queryServiceQuality());
@@ -339,7 +337,6 @@ public class ApplicationSerializer {
DeploymentSpec deploymentSpec = DeploymentSpec.fromXml(root.field(deploymentSpecField).asString(), false);
ValidationOverrides validationOverrides = ValidationOverrides.fromXml(root.field(validationOverridesField).asString());
Change deploying = changeFromSlime(root.field(deployingField));
- Change outstandingChange = changeFromSlime(root.field(outstandingChangeField));
Optional<IssueId> deploymentIssueId = Serializers.optionalString(root.field(deploymentIssueField)).map(IssueId::from);
Optional<IssueId> ownershipIssueId = Serializers.optionalString(root.field(ownershipIssueIdField)).map(IssueId::from);
Optional<User> owner = Serializers.optionalString(root.field(ownerField)).map(User::from);
@@ -351,7 +348,7 @@ public class ApplicationSerializer {
OptionalLong projectId = Serializers.optionalLong(root.field(projectIdField));
Optional<ApplicationVersion> latestVersion = latestVersionFromSlime(root.field(latestVersionField));
- return new Application(id, createdAt, deploymentSpec, validationOverrides, deploying, outstandingChange,
+ return new Application(id, createdAt, deploymentSpec, validationOverrides, deploying,
deploymentIssueId, ownershipIssueId, owner, majorVersion, metrics,
deployKeys, projectId, latestVersion, instances);
}
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 0f92ecde133..ec5392f1224 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
@@ -699,6 +699,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
"/job/",
request.getUri()).toString());
+ DeploymentStatus status = controller.jobController().deploymentStatus(application);
application.latestVersion().ifPresent(version -> toSlime(version, object.setObject("latestVersion")));
application.projectId().ifPresent(id -> object.setLong("projectId", id));
@@ -708,15 +709,14 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
toSlime(object.setObject("deploying"), application.change());
// Outstanding change
- if ( ! application.outstandingChange().isEmpty())
- toSlime(object.setObject("outstandingChange"), application.outstandingChange());
+ if ( ! status.outstandingChange().isEmpty())
+ toSlime(object.setObject("outstandingChange"), status.outstandingChange());
// Compile version. The version that should be used when building an application
object.setString("compileVersion", compileVersion(application.id()).toFullString());
application.majorVersion().ifPresent(majorVersion -> object.setLong("majorVersion", majorVersion));
- DeploymentStatus status = controller.jobController().deploymentStatus(application);
Cursor instancesArray = object.setArray("instances");
for (Instance instance : application.instances().values())
toSlime(instancesArray.addObject(), status, instance, application.deploymentSpec(), request);
@@ -855,8 +855,8 @@ public class ApplicationApiHandler extends LoggingRequestHandler {
}
// Outstanding change
- if ( ! application.outstandingChange().isEmpty()) {
- toSlime(object.setObject("outstandingChange"), application.outstandingChange());
+ if ( ! status.outstandingChange().isEmpty()) {
+ toSlime(object.setObject("outstandingChange"), status.outstandingChange());
}
if (application.deploymentSpec().instance(instance.name()).isPresent()) {
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java
index c3739f037c9..8cd938dccf6 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/deployment/DeploymentTriggerTest.java
@@ -292,11 +292,11 @@ public class DeploymentTriggerTest {
assertEquals(0, tester.jobs().active().size());
app.submit(applicationPackage);
- assertTrue(app.application().outstandingChange().hasTargets());
+ assertTrue(app.deploymentStatus().outstandingChange().hasTargets());
app.runJob(systemTest).runJob(stagingTest);
tester.outstandingChangeDeployer().run();
- assertTrue(app.application().outstandingChange().hasTargets());
+ assertTrue(app.deploymentStatus().outstandingChange().hasTargets());
tester.triggerJobs();
assertEquals(emptyList(), tester.jobs().active());
@@ -304,7 +304,7 @@ public class DeploymentTriggerTest {
tester.clock().advance(Duration.ofHours(2)); // ---------------- Exit block window: 20:30
tester.outstandingChangeDeployer().run();
- assertFalse(app.application().outstandingChange().hasTargets());
+ assertFalse(app.deploymentStatus().outstandingChange().hasTargets());
tester.triggerJobs(); // Tests already run for the blocked production job.
app.assertRunning(productionUsWest1);
@@ -337,7 +337,7 @@ public class DeploymentTriggerTest {
tester.outstandingChangeDeployer().run();
app.runJob(productionUsWest1);
assertEquals(1, app.instanceJobs().get(productionUsWest1).lastSuccess().get().versions().targetApplication().buildNumber().getAsLong());
- assertEquals(2, app.application().outstandingChange().application().get().buildNumber().getAsLong());
+ assertEquals(2, app.deploymentStatus().outstandingChange().application().get().buildNumber().getAsLong());
tester.triggerJobs();
// Platform upgrade keeps rolling, since it began before block window, and tests for the new revision have also started.
@@ -347,13 +347,13 @@ public class DeploymentTriggerTest {
// Upgrade is done, and outstanding change rolls out when block window ends.
assertEquals(Change.empty(), app.application().change());
- assertTrue(app.application().outstandingChange().hasTargets());
+ assertTrue(app.deploymentStatus().outstandingChange().hasTargets());
app.runJob(stagingTest).runJob(systemTest);
tester.clock().advance(Duration.ofHours(1));
tester.outstandingChangeDeployer().run();
assertTrue(app.application().change().hasTargets());
- assertFalse(app.application().outstandingChange().hasTargets());
+ assertFalse(app.deploymentStatus().outstandingChange().hasTargets());
app.runJob(productionUsWest1).runJob(productionUsEast3);
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java
index 11c77304dd0..a822e4fca9f 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/OutstandingChangeDeployerTest.java
@@ -44,13 +44,13 @@ public class OutstandingChangeDeployerTest {
tester.deploymentTrigger().triggerReadyJobs();
assertEquals(Change.of(version), app1.application().change());
- assertFalse(app1.application().outstandingChange().hasTargets());
+ assertFalse(app1.deploymentStatus().outstandingChange().hasTargets());
assertEquals(1, app1.application().latestVersion().get().buildNumber().getAsLong());
app1.submit(applicationPackage, new SourceRevision("repository1", "master", "cafed00d"));
- assertTrue(app1.application().outstandingChange().hasTargets());
- assertEquals("1.0.2-cafed00d", app1.application().outstandingChange().application().get().id());
+ assertTrue(app1.deploymentStatus().outstandingChange().hasTargets());
+ assertEquals("1.0.2-cafed00d", app1.deploymentStatus().outstandingChange().application().get().id());
app1.assertRunning(JobType.systemTest);
app1.assertRunning(JobType.stagingTest);
assertEquals(2, tester.jobs().active().size());
@@ -58,7 +58,7 @@ public class OutstandingChangeDeployerTest {
deployer.maintain();
tester.triggerJobs();
assertEquals("No effect as job is in progress", 2, tester.jobs().active().size());
- assertEquals("1.0.2-cafed00d", app1.application().outstandingChange().application().get().id());
+ assertEquals("1.0.2-cafed00d", app1.deploymentStatus().outstandingChange().application().get().id());
app1.runJob(JobType.systemTest).runJob(JobType.stagingTest).runJob(JobType.productionUsWest1)
.runJob(JobType.stagingTest).runJob(JobType.systemTest);
@@ -70,7 +70,7 @@ public class OutstandingChangeDeployerTest {
List<Run> runs = tester.jobs().active();
assertEquals(1, runs.size());
app1.assertRunning(JobType.productionUsWest1);
- assertFalse(app1.application().outstandingChange().hasTargets());
+ assertFalse(app1.deploymentStatus().outstandingChange().hasTargets());
}
}
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 9d0438ba49f..14002807b96 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
@@ -124,7 +124,6 @@ public class ApplicationSerializerTest {
deploymentSpec,
validationOverrides,
Change.of(Version.fromString("6.7")).withPin(),
- Change.of(ApplicationVersion.from(new SourceRevision("repo", "master", "deadcafe"), 42)),
Optional.of(IssueId.from("4321")),
Optional.of(IssueId.from("1234")),
Optional.of(User.from("by-username")),
@@ -166,8 +165,6 @@ public class ApplicationSerializerTest {
assertEquals(original.require(id1.instance()).jobPause(JobType.stagingTest),
serialized.require(id1.instance()).jobPause(JobType.stagingTest));
- assertEquals(original.outstandingChange(), serialized.outstandingChange());
-
assertEquals(original.ownershipIssueId(), serialized.ownershipIssueId());
assertEquals(original.owner(), serialized.owner());
assertEquals(original.majorVersion(), serialized.majorVersion());