diff options
author | Martin Polden <mpolden@mpolden.no> | 2019-12-20 09:25:10 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-12-20 09:25:10 +0100 |
commit | de3ba04b4f74d4deea26b22ca1fdd5cf3bf809c9 (patch) | |
tree | 4604bb7dfd9059343f023af7bbd533786a6abe48 /controller-server | |
parent | d60817bf4723bdf72a5b6e540bf2006f836fa834 (diff) | |
parent | 613fdf95af5ab8282aeeb3d082dfc9ce7687fe47 (diff) |
Merge pull request #11609 from vespa-engine/jvenstad/deplorch-cleanup-1
Jvenstad/deplorch cleanup 1
Diffstat (limited to 'controller-server')
2 files changed, 78 insertions, 43 deletions
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 a441d8670e2..9bfaaacdd05 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 @@ -25,6 +25,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -287,6 +288,22 @@ public class DeploymentStatus { .successOn(versions).isEmpty(); } + + public enum StepType { + + /** An instance — completion marks a change as ready for the jobs contained in it. */ + instance, + + /** A timed delay. */ + delay, + + /** A system, staging or production test. */ + test, + + /** A production deployment. */ + deployment, + } + /** * Used to represent all steps — explicit and implicit — that may run in order to complete deployment of a change. * @@ -300,20 +317,25 @@ public class DeploymentStatus { // TODO jonmv: Make the step status expose _what it is_. public static abstract class StepStatus { + private final StepType type; private final DeploymentSpec.Step step; private final List<StepStatus> dependencies; private final Optional<InstanceName> instance; - protected StepStatus(DeploymentSpec.Step step, List<StepStatus> dependencies) { - this(step, dependencies, null); + protected StepStatus(StepType type, DeploymentSpec.Step step, List<StepStatus> dependencies) { + this(type, step, dependencies, null); } - protected StepStatus(DeploymentSpec.Step step, List<StepStatus> dependencies, InstanceName instance) { + protected StepStatus(StepType type, DeploymentSpec.Step step, List<StepStatus> dependencies, InstanceName instance) { + this.type = requireNonNull(type); this.step = requireNonNull(step); this.dependencies = List.copyOf(dependencies); this.instance = Optional.ofNullable(instance); } + /** The type of step this is. */ + public final StepType type() { return type; } + /** The step defining this. */ public final DeploymentSpec.Step step() { return step; } @@ -323,17 +345,25 @@ public class DeploymentStatus { /** The instance of this, if any. */ public final Optional<InstanceName> instance() { return instance; } + /** The id of the job this corresponds to, if any. */ public Optional<JobId> job() { return Optional.empty(); } - /** The time at which this is complete on the given versions. */ + /** The time at which this is, or was, complete on the given change and / or versions. */ public abstract Optional<Instant> completedAt(Change change, Versions versions); - // TODO jonmv: dependenciesCompletedAt - - // TODO jonmv: pausedUntil and coolingDownUntil - - /** The time at which all dependencies completed on the given version. */ + /** The time at which this step is ready to run the specified change and / or versions. */ public Optional<Instant> readyAt(Change change, Versions versions) { + return dependenciesCompletedAt(change, versions) + .map(ready -> Stream.concat(Stream.of(blockedUntil(change), + pausedUntil(), + coolingDownUntil(versions)) + .flatMap(Optional::stream), + Stream.of(ready)) + .max(naturalOrder()).get()); + } + + /** The time at which all dependencies completed on the given change and / or versions. */ + public Optional<Instant> dependenciesCompletedAt(Change change, Versions versions) { return dependencies.stream().allMatch(step -> step.completedAt(change, versions).isPresent()) ? dependencies.stream().map(step -> step.completedAt(change, versions).get()) .max(naturalOrder()) @@ -341,6 +371,16 @@ public class DeploymentStatus { : Optional.empty(); } + /** The time until which this step is blocked by a change blocker. */ + // TODO jonmv: readyAt for instance can be delayed by block window. Upgrade policy / confidence is something different. + public Optional<Instant> blockedUntil(Change change) { return Optional.empty(); } + + /** The time until which this step is paused by user intervention. */ + public Optional<Instant> pausedUntil() { return Optional.empty(); } + + /** The time until which this step is cooling down, due to consecutive failures. */ + public Optional<Instant> coolingDownUntil(Versions versions) { return Optional.empty(); } + /** Whether this step is currently running, with the given version parameters. */ public abstract boolean isRunning(Versions versions); @@ -353,7 +393,7 @@ public class DeploymentStatus { public static class DelayStatus extends StepStatus { public DelayStatus(DeploymentSpec.Delay step, List<StepStatus> dependencies) { - super(step, dependencies); + super(StepType.delay, step, dependencies); } @Override @@ -374,9 +414,9 @@ public class DeploymentStatus { private final JobStatus job; private final DeploymentStatus status; - protected JobStepStatus(DeploymentSpec.Step step, List<StepStatus> dependencies, JobStatus job, + protected JobStepStatus(StepType type, DeploymentSpec.Step step, List<StepStatus> dependencies, JobStatus job, DeploymentStatus status) { - super(step, dependencies, job.id().application().instance()); + super(type, step, dependencies, job.id().application().instance()); this.job = requireNonNull(job); this.status = requireNonNull(status); } @@ -390,27 +430,21 @@ public class DeploymentStatus { } @Override - // TODO jonmv: Split in readyAt(change, versions), pausedUntil(), and coolingDownUntil(versions) - public Optional<Instant> readyAt(Change change, Versions versions) { - Optional<Instant> readyAt = super.readyAt(change, versions); - if (readyAt.isEmpty()) - return Optional.empty(); - - Optional<Instant> pausedUntil = status.application().require(job.id().application().instance()).jobPause(job.id().type()); - if (pausedUntil.isPresent() && pausedUntil.get().isAfter(readyAt.get())) - return pausedUntil; + public Optional<Instant> pausedUntil() { + return status.application().require(job.id().application().instance()).jobPause(job.id().type()); + } - if (job.lastTriggered().isEmpty()) return readyAt; - if (job.lastCompleted().isEmpty()) return readyAt; - if (job.firstFailing().isEmpty()) return readyAt; - if ( ! versions.targetsMatch(job.lastCompleted().get().versions())) return readyAt; - if (status.application.deploymentSpec().requireInstance(job.id().application().instance()).upgradePolicy() == DeploymentSpec.UpgradePolicy.canary) return readyAt; - if (job.id().type().environment().isTest() && job.isOutOfCapacity()) return readyAt; + @Override + public Optional<Instant> coolingDownUntil(Versions versions) { + if (job.lastTriggered().isEmpty()) return Optional.empty(); + if (job.lastCompleted().isEmpty()) return Optional.empty(); + if (job.firstFailing().isEmpty()) return Optional.empty(); + if ( ! versions.targetsMatch(job.lastCompleted().get().versions())) return Optional.empty(); + if (status.application.deploymentSpec().requireInstance(job.id().application().instance()).upgradePolicy() == DeploymentSpec.UpgradePolicy.canary) return Optional.empty(); + if (job.id().type().environment().isTest() && job.isOutOfCapacity()) return Optional.empty(); Instant firstFailing = job.firstFailing().get().end().get(); Instant lastCompleted = job.lastCompleted().get().end().get(); - if (lastCompleted.isBefore(readyAt.get())) - return readyAt; return firstFailing.equals(lastCompleted) ? Optional.of(lastCompleted) : Optional.of(lastCompleted.plus(Duration.ofMinutes(10)) @@ -425,7 +459,7 @@ public class DeploymentStatus { Optional<Deployment> existingDeployment = Optional.ofNullable(status.application().require(instance) .deployments().get(zone)); - return new JobStepStatus(step, dependencies, job, status) { + return new JobStepStatus(StepType.deployment, step, dependencies, job, status) { @Override public Optional<Instant> readyAt(Change change, Versions versions) { @@ -458,7 +492,7 @@ public class DeploymentStatus { public static JobStepStatus ofProductionTest(DeclaredTest step, List<StepStatus> dependencies, DeploymentStatus status, InstanceName instance, JobType testType, JobType jobType) { JobStatus job = status.instanceJobs(instance).get(testType); - return new JobStepStatus(step, dependencies, job, status) { + return new JobStepStatus(StepType.test, step, dependencies, job, status) { @Override public Optional<Instant> completedAt(Change change, Versions versions) { return job.lastSuccess() @@ -474,7 +508,7 @@ public class DeploymentStatus { DeploymentStatus status, InstanceName instance, JobType jobType, boolean declared) { JobStatus job = status.instanceJobs(instance).get(jobType); - return new JobStepStatus(step, dependencies, job, status) { + return new JobStepStatus(StepType.test, step, dependencies, job, status) { @Override public Optional<Instant> completedAt(Change change, Versions versions) { return RunList.from(job) diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java index 3dcb3b61c5b..5406aeb0c61 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelper.java @@ -201,10 +201,11 @@ class JobControllerApiHandlerHelper { .filter(type -> ! type.isTest()) .filter(type -> controller.applications().deploymentTrigger().isComplete(Change.of(lastPlatform), change, instance, type, status.get(type))) .count(); + long total = productionJobs.stream().filter(type -> ! type.isTest()).count(); if (Optional.of(lastPlatform).equals(change.platform())) - lastPlatformObject.setString("deploying", completed + " of " + productionJobs.stream().filter(type -> ! type.isTest()).count() + " complete"); - else if (completed == productionJobs.size()) - lastPlatformObject.setString("completed", completed + " of " + productionJobs.stream().filter(type -> ! type.isTest()).count() + " complete"); + lastPlatformObject.setString("deploying", completed + " of " + total + " complete"); + else if (completed == total) + lastPlatformObject.setString("completed", completed + " of " + total + " complete"); else if ( ! application.deploymentSpec().instances().stream() .allMatch(spec -> spec.canUpgradeAt(controller.clock().instant()))) { lastPlatformObject.setString("blocked", application.deploymentSpec().instances().stream() @@ -221,18 +222,18 @@ class JobControllerApiHandlerHelper { } private static void lastApplicationToSlime(Cursor lastApplicationObject, Application application, Instance instance, Map<JobType, JobStatus> status, Change change, List<JobType> productionJobs, Controller controller) { - long completed; ApplicationVersion lastApplication = application.latestVersion().get(); applicationVersionToSlime(lastApplicationObject.setObject("application"), lastApplication); lastApplicationObject.setLong("at", lastApplication.buildTime().get().toEpochMilli()); - completed = productionJobs.stream() - .filter(type -> ! type.isTest()) - .filter(type -> controller.applications().deploymentTrigger().isComplete(Change.of(lastApplication), change, instance, type, status.get(type))) - .count(); + long completed = productionJobs.stream() + .filter(type -> ! type.isTest()) + .filter(type -> controller.applications().deploymentTrigger().isComplete(Change.of(lastApplication), change, instance, type, status.get(type))) + .count(); + long total = productionJobs.stream().filter(type -> ! type.isTest()).count(); if (Optional.of(lastApplication).equals(change.application())) - lastApplicationObject.setString("deploying", completed + " of " + productionJobs.stream().filter(type -> ! type.isTest()).count() + " complete"); - else if (completed == productionJobs.size()) - lastApplicationObject.setString("completed", completed + " of " + productionJobs.stream().filter(type -> ! type.isTest()).count() + " complete"); + lastApplicationObject.setString("deploying", completed + " of " + total + " complete"); + else if (completed == total) + lastApplicationObject.setString("completed", completed + " of " + total + " complete"); else if ( ! application.deploymentSpec().instances().stream() .allMatch(spec -> spec.canChangeRevisionAt(controller.clock().instant()))) { lastApplicationObject.setString("blocked", application.deploymentSpec().instances().stream() |