diff options
author | Jon Bratseth <bratseth@oath.com> | 2018-08-08 17:09:47 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-08 17:09:47 +0200 |
commit | bc0dda19eb7f2d2eee21629cedc6e041b0c6be2b (patch) | |
tree | c36f120a31afc85569fd871118dc1850280508af | |
parent | ad4e3a8ca7dd8d6a90555de93c37b04107f10df0 (diff) | |
parent | 67d8c4a95b13798c3d721a4c8fec6ee289665379 (diff) |
Merge pull request #6521 from vespa-engine/jvenstad/deployments
Store version information in run status
8 files changed, 177 insertions, 70 deletions
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 3bfce2725a2..72bc1c61eae 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 @@ -170,7 +170,10 @@ public class DeploymentTrigger { try { applications().lockOrThrow(job.applicationId(), application -> { if (application.get().deploymentJobs().builtInternally()) - jobs.start(job.applicationId(), job.jobType); + jobs.start(job.applicationId(), job.jobType, new Versions(job.triggering.platform(), + job.triggering.application(), + job.triggering.sourcePlatform(), + job.triggering.sourceApplication())); else buildService.trigger(job); 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 ba79364fa34..06af36a2e26 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 @@ -1,6 +1,7 @@ package com.yahoo.vespa.hosted.controller.deployment; import com.google.common.collect.ImmutableMap; +import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; import com.yahoo.vespa.curator.Lock; import com.yahoo.vespa.hosted.controller.Application; @@ -195,7 +196,7 @@ public class JobController { } /** Orders a run of the given type, or throws an IllegalStateException if that job type is already running. */ - public void start(ApplicationId id, JobType type) { + public void start(ApplicationId id, JobType type, Versions versions) { controller.applications().lockIfPresent(id, application -> { if ( ! application.get().deploymentJobs().builtInternally()) throw new IllegalArgumentException(id + " is not built here!"); @@ -206,7 +207,7 @@ public class JobController { throw new IllegalStateException("Can not start " + type + " for " + id + "; it is already running!"); RunId newId = new RunId(id, type, last.map(run -> run.id().number()).orElse(0L) + 1); - curator.writeLastRun(RunStatus.initial(newId, controller.clock().instant())); + curator.writeLastRun(RunStatus.initial(newId, versions, controller.clock().instant())); }); }); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunStatus.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunStatus.java index c6724255a58..c30b1aee7f1 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunStatus.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/RunStatus.java @@ -1,7 +1,9 @@ package com.yahoo.vespa.hosted.controller.deployment; import com.google.common.collect.ImmutableList; +import com.yahoo.component.Version; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; +import com.yahoo.vespa.hosted.controller.application.ApplicationVersion; import java.time.Instant; import java.util.Collections; @@ -24,24 +26,26 @@ public class RunStatus { private final RunId id; private final Map<Step, Step.Status> steps; + private final Versions versions; private final Instant start; private final Optional<Instant> end; private final boolean aborted; - // TODO jvenstad: Add a Versions object and a reason String. Requires shortcutting of triggering of these runs. // For deserialisation only -- do not use! - public RunStatus(RunId id, Map<Step, Step.Status> steps, Instant start, Optional<Instant> end, boolean aborted) { + public RunStatus(RunId id, Map<Step, Step.Status> steps, Versions versions, + Instant start, Optional<Instant> end, boolean aborted) { this.id = id; this.steps = Collections.unmodifiableMap(new EnumMap<>(steps)); + this.versions = versions; this.start = start; this.end = end; this.aborted = aborted; } - public static RunStatus initial(RunId id, Instant now) { + public static RunStatus initial(RunId id, Versions versions, Instant now) { EnumMap<Step, Step.Status> steps = new EnumMap<>(Step.class); JobProfile.of(id.type()).steps().forEach(step -> steps.put(step, unfinished)); - return new RunStatus(id, steps, requireNonNull(now), Optional.empty(), false); + return new RunStatus(id, steps, requireNonNull(versions), requireNonNull(now), Optional.empty(), false); } public RunStatus with(Step.Status status, LockedStep step) { @@ -50,21 +54,21 @@ public class RunStatus { EnumMap<Step, Step.Status> steps = new EnumMap<>(this.steps); steps.put(step.get(), requireNonNull(status)); - return new RunStatus(id, steps, start, end, aborted); + return new RunStatus(id, steps, versions, start, end, aborted); } public RunStatus finished(Instant now) { if (hasEnded()) throw new AssertionError("This step ended at " + end.get() + " -- it can't be ended again!"); - return new RunStatus(id, new EnumMap<>(steps), start, Optional.of(now), aborted); + return new RunStatus(id, new EnumMap<>(steps), versions, start, Optional.of(now), aborted); } public RunStatus aborted() { if (hasEnded()) throw new AssertionError("This step ended at " + end.get() + " -- it can't be aborted now!"); - return new RunStatus(id, new EnumMap<>(steps), start, end, true); + return new RunStatus(id, new EnumMap<>(steps), versions, start, end, true); } /** Returns the id of this run. */ @@ -116,6 +120,11 @@ public class RunStatus { return end.isPresent(); } + /** Returns the target, and possibly source, versions for this run. */ + public Versions versions() { + return versions; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Versions.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Versions.java index bf58bac177c..e0f7a955a80 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Versions.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/deployment/Versions.java @@ -9,8 +9,11 @@ import com.yahoo.vespa.hosted.controller.application.Change; import com.yahoo.vespa.hosted.controller.application.Deployment; import com.yahoo.vespa.hosted.controller.application.JobStatus; +import java.util.Objects; import java.util.Optional; +import static java.util.Objects.requireNonNull; + /** * Source and target versions for an application. * @@ -26,10 +29,13 @@ public class Versions { public Versions(Version targetPlatform, ApplicationVersion targetApplication, Optional<Version> sourcePlatform, Optional<ApplicationVersion> sourceApplication) { - this.targetPlatform = targetPlatform; - this.targetApplication = targetApplication; - this.sourcePlatform = sourcePlatform; - this.sourceApplication = sourceApplication; + if (sourcePlatform.isPresent() ^ sourceApplication.isPresent()) + throw new IllegalArgumentException("Sources must both be present or absent."); + + this.targetPlatform = requireNonNull(targetPlatform); + this.targetApplication = requireNonNull(targetApplication); + this.sourcePlatform = requireNonNull(sourcePlatform); + this.sourceApplication = requireNonNull(sourceApplication); } /** Target platform version for this */ diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java index 7df60278390..153f4e327a0 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/persistence/RunSerializer.java @@ -1,5 +1,6 @@ package com.yahoo.vespa.hosted.controller.persistence; +import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; import com.yahoo.slime.ArrayTraverser; import com.yahoo.slime.Cursor; @@ -8,9 +9,12 @@ import com.yahoo.slime.ObjectTraverser; import com.yahoo.slime.Slime; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; +import com.yahoo.vespa.hosted.controller.application.ApplicationVersion; +import com.yahoo.vespa.hosted.controller.application.SourceRevision; import com.yahoo.vespa.hosted.controller.deployment.RunStatus; import com.yahoo.vespa.hosted.controller.deployment.Step; import com.yahoo.vespa.hosted.controller.deployment.Step.Status; +import com.yahoo.vespa.hosted.controller.deployment.Versions; import java.time.Instant; import java.util.EnumMap; @@ -47,6 +51,13 @@ public class RunSerializer { private static final String startField = "start"; private static final String endField = "end"; private static final String abortedField = "aborted"; + private static final String versionsField = "versions"; + private static final String platformVersionField = "platform"; + private static final String repositoryField = "repository"; + private static final String branchField = "branch"; + private static final String commitField = "commit"; + private static final String buildField = "build"; + private static final String sourceField = "source"; RunStatus runFromSlime(Slime slime) { return runFromSlime(slime.get()); @@ -71,6 +82,7 @@ public class RunSerializer { JobType.fromJobName(runObject.field(jobTypeField).asString()), runObject.field(numberField).asLong()), steps, + versionsFromSlime(runObject.field(versionsField)), Instant.ofEpochMilli(runObject.field(startField).asLong()), Optional.of(runObject.field(endField)) .filter(Inspector::valid) @@ -78,6 +90,25 @@ public class RunSerializer { runObject.field(abortedField).asBool()); } + private Versions versionsFromSlime(Inspector versionsObject) { + Version targetPlatformVersion = Version.fromString(versionsObject.field(platformVersionField).asString()); + ApplicationVersion targetApplicationVersion = ApplicationVersion.from(new SourceRevision(versionsObject.field(repositoryField).asString(), + versionsObject.field(branchField).asString(), + versionsObject.field(commitField).asString()), + versionsObject.field(buildField).asLong()); + Optional<Version> sourcePlatformVersion = versionsObject.field(sourceField).valid() + ? Optional.of(Version.fromString(versionsObject.field(sourceField).field(platformVersionField).asString())) + : Optional.empty(); + Optional<ApplicationVersion> sourceApplicationVersion = versionsObject.field(sourceField).valid() + ? Optional.of(ApplicationVersion.from(new SourceRevision(versionsObject.field(repositoryField).asString(), + versionsObject.field(branchField).asString(), + versionsObject.field(commitField).asString()), + versionsObject.field(buildField).asLong())) + : Optional.empty(); + + return new Versions(targetPlatformVersion, targetApplicationVersion, sourcePlatformVersion, sourceApplicationVersion); + } + Slime toSlime(Iterable<RunStatus> runs) { Slime slime = new Slime(); Cursor runArray = slime.setArray(); @@ -98,59 +129,84 @@ public class RunSerializer { runObject.setLong(startField, run.start().toEpochMilli()); run.end().ifPresent(end -> runObject.setLong(endField, end.toEpochMilli())); if (run.isAborted()) runObject.setBool(abortedField, true); + Cursor stepsObject = runObject.setObject(stepsField); run.steps().forEach((step, status) -> stepsObject.setString(valueOf(step), valueOf(status))); + + Cursor versionsObject = runObject.setObject(versionsField); + toSlime(run.versions().targetPlatform(), run.versions().targetApplication(), versionsObject); + run.versions().sourcePlatform().ifPresent(sourcePlatformVersion -> { + toSlime(sourcePlatformVersion, + run.versions().sourceApplication() + .orElseThrow(() -> new IllegalArgumentException("Source versions must be both present or absent.")), + versionsObject.setObject(sourceField)); + }); + } + + private void toSlime(Version platformVersion, ApplicationVersion applicationVersion, Cursor versionsObject) { + versionsObject.setString(platformVersionField, platformVersion.toString()); + SourceRevision targetSourceRevision = applicationVersion.source() + .orElseThrow(() -> new IllegalArgumentException("Source revision must be present in target application version.")); + versionsObject.setString(repositoryField, targetSourceRevision.repository()); + versionsObject.setString(branchField, targetSourceRevision.branch()); + versionsObject.setString(commitField, targetSourceRevision.commit()); + versionsObject.setLong(buildField, applicationVersion.buildNumber() + .orElseThrow(() -> new IllegalArgumentException("Build number must be present in target application version."))); } static String valueOf(Step step) { switch (step) { - case deployInitialReal : return "DIR"; - case installInitialReal : return "IIR"; - case deployReal : return "DR" ; - case installReal : return "IR" ; - case deactivateReal : return "DAR"; - case deployTester : return "DT" ; - case installTester : return "IT" ; - case deactivateTester : return "DAT"; - case startTests : return "ST" ; - case endTests : return "ET" ; - case report : return "R" ; + case deployInitialReal : return "deployInitialReal"; + case installInitialReal : return "installInitialReal"; + case deployReal : return "deployReal"; + case installReal : return "installReal"; + case deactivateReal : return "deactivateReal"; + case deployTester : return "deployTester"; + case installTester : return "installTester"; + case deactivateTester : return "deactivateTester"; + case startTests : return "startTests"; + case endTests : return "endTests"; + case report : return "report"; + default : throw new AssertionError("No value defined for '" + step + "'!"); } } static Step stepOf(String step) { switch (step) { - case "DIR" : return deployInitialReal ; - case "IIR" : return installInitialReal; - case "DR" : return deployReal ; - case "IR" : return installReal ; - case "DAR" : return deactivateReal ; - case "DT" : return deployTester ; - case "IT" : return installTester ; - case "DAT" : return deactivateTester ; - case "ST" : return startTests ; - case "ET" : return endTests ; - case "R" : return report ; - default : throw new IllegalArgumentException("No step defined by '" + step + "'!"); + case "deployInitialReal" : return deployInitialReal; + case "installInitialReal" : return installInitialReal; + case "deployReal" : return deployReal; + case "installReal" : return installReal; + case "deactivateReal" : return deactivateReal; + case "deployTester" : return deployTester; + case "installTester" : return installTester; + case "deactivateTester" : return deactivateTester; + case "startTests" : return startTests; + case "endTests" : return endTests; + case "report" : return report; + + default : throw new IllegalArgumentException("No step defined by '" + step + "'!"); } } static String valueOf(Status status) { switch (status) { - case unfinished : return "U"; - case failed : return "F"; - case succeeded : return "S"; - default : throw new AssertionError("No value defined for '" + status + "'!"); + case unfinished : return "unfinished"; + case failed : return "failed"; + case succeeded : return "succeeded"; + + default : throw new AssertionError("No value defined for '" + status + "'!"); } } static Status statusOf(String status) { switch (status) { - case "U" : return unfinished; - case "F" : return failed ; - case "S" : return succeeded ; - default : throw new IllegalArgumentException("No status defined by '" + status + "'!"); + case "unfinished" : return unfinished; + case "failed" : return failed; + case "succeeded" : return succeeded; + + default : throw new IllegalArgumentException("No status defined by '" + status + "'!"); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java index e084e9aa46d..e5416672bb7 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java @@ -1,8 +1,10 @@ package com.yahoo.vespa.hosted.controller.maintenance; +import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; import com.yahoo.vespa.hosted.controller.TestIdentities; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; +import com.yahoo.vespa.hosted.controller.application.ApplicationVersion; import com.yahoo.vespa.hosted.controller.application.SourceRevision; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTester; import com.yahoo.vespa.hosted.controller.deployment.JobController; @@ -10,6 +12,7 @@ import com.yahoo.vespa.hosted.controller.deployment.RunStatus; import com.yahoo.vespa.hosted.controller.deployment.Step; import com.yahoo.vespa.hosted.controller.deployment.Step.Status; import com.yahoo.vespa.hosted.controller.deployment.StepRunner; +import com.yahoo.vespa.hosted.controller.deployment.Versions; import org.junit.Test; import java.time.Duration; @@ -40,7 +43,6 @@ import static com.yahoo.vespa.hosted.controller.deployment.Step.deactivateReal; import static com.yahoo.vespa.hosted.controller.deployment.Step.deactivateTester; import static com.yahoo.vespa.hosted.controller.deployment.Step.deployReal; import static com.yahoo.vespa.hosted.controller.deployment.Step.deployTester; -import static com.yahoo.vespa.hosted.controller.deployment.Step.installInitialReal; import static com.yahoo.vespa.hosted.controller.deployment.Step.installReal; import static com.yahoo.vespa.hosted.controller.deployment.Step.installTester; import static com.yahoo.vespa.hosted.controller.deployment.Step.report; @@ -56,6 +58,14 @@ import static org.junit.Assert.fail; */ public class JobRunnerTest { + static final Versions versions = new Versions(Version.fromString("1.2.3"), + ApplicationVersion.from(new SourceRevision("repo", + "branch", + "bada55"), + 321), + Optional.empty(), + Optional.empty()); + @Test public void multiThreadedExecutionFinishes() throws InterruptedException { DeploymentTester tester = new DeploymentTester(); @@ -67,15 +77,15 @@ public class JobRunnerTest { Executors.newFixedThreadPool(32), notifying(stepRunner, latch)); ApplicationId id = tester.createApplication("real", "tenant", 1, 1L).id(); - jobs.submit(id, new SourceRevision("repo", "branch", "bada55"), new byte[0], new byte[0]); + jobs.submit(id, versions.targetApplication().source().get(), new byte[0], new byte[0]); - jobs.start(id, systemTest); + jobs.start(id, systemTest, versions); try { - jobs.start(id, systemTest); + jobs.start(id, systemTest, versions); fail("Job is already running, so this should not be allowed!"); } catch (IllegalStateException e) { } - jobs.start(id, stagingTest); + jobs.start(id, stagingTest, versions); assertTrue(jobs.last(id, systemTest).get().steps().values().stream().allMatch(unfinished::equals)); runner.maintain(); @@ -100,10 +110,10 @@ public class JobRunnerTest { inThreadExecutor(), mappedRunner(outcomes)); ApplicationId id = tester.createApplication("real", "tenant", 1, 1L).id(); - jobs.submit(id, new SourceRevision("repo", "branch", "bada55"), new byte[0], new byte[0]); + jobs.submit(id, versions.targetApplication().source().get(), new byte[0], new byte[0]); Supplier<RunStatus> run = () -> jobs.last(id, systemTest).get(); - jobs.start(id, systemTest); + jobs.start(id, systemTest, versions); RunId first = run.get().id(); Map<Step, Status> steps = run.get().steps(); @@ -155,7 +165,7 @@ public class JobRunnerTest { assertTrue(run.get().isAborted()); // A new run is attempted. - jobs.start(id, systemTest); + jobs.start(id, systemTest, versions); assertEquals(first.number() + 1, run.get().id().number()); // Run fails on tester deployment -- remaining run-always steps succeed, and the run finishes. @@ -171,7 +181,7 @@ public class JobRunnerTest { assertEquals(2, jobs.runs(id, systemTest).size()); // Start a third run, then unregister and wait for data to be deleted. - jobs.start(id, systemTest); + jobs.start(id, systemTest, versions); jobs.unregister(id); runner.maintain(); assertFalse(jobs.last(id, systemTest).isPresent()); @@ -188,10 +198,10 @@ public class JobRunnerTest { Executors.newFixedThreadPool(32), waitingRunner(barrier)); ApplicationId id = tester.createApplication("real", "tenant", 1, 1L).id(); - jobs.submit(id, new SourceRevision("repo", "branch", "bada55"), new byte[0], new byte[0]); + jobs.submit(id, versions.targetApplication().source().get(), new byte[0], new byte[0]); RunId runId = new RunId(id, systemTest, 1); - jobs.start(id, systemTest); + jobs.start(id, systemTest, versions); runner.maintain(); barrier.await(); try { diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json index d659bd9fff0..9b3de20acc4 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/persistence/testdata/run-status.json @@ -5,17 +5,31 @@ "number": 112358, "start": 1196676930000, "steps": { - "DIR": "U", - "IIR": "F", - "DR": "S", - "IR": "U", - "DAR": "F", - "DT": "S", - "IT": "U", - "DAT": "F", - "ST": "S", - "ET": "U", - "R": "F" + "deployInitialReal": "unfinished", + "installInitialReal": "failed", + "deployReal": "succeeded", + "installReal": "unfinished", + "deactivateReal": "failed", + "deployTester": "succeeded", + "installTester": "unfinished", + "deactivateTester": "failed", + "startTests": "succeeded", + "endTests": "unfinished", + "report": "failed" + }, + "versions": { + "platform": "1.2.3", + "repository": "git@github.com:user/repo.git", + "branch": "master", + "commit": "f00bad", + "build": 123, + "source": { + "platform": "1.2.3", + "repository": "git@github.com:user/repo.git", + "branch": "master", + "commit": "badb17", + "build": 122 + } } } ]
\ No newline at end of file diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java index 856b91b912e..f350cc8627b 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/JobControllerApiHandlerHelperTest.java @@ -1,5 +1,6 @@ package com.yahoo.vespa.hosted.controller.restapi.application; +import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.TenantName; import com.yahoo.container.jdisc.HttpResponse; @@ -7,10 +8,12 @@ import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockLogStore; +import com.yahoo.vespa.hosted.controller.application.ApplicationVersion; import com.yahoo.vespa.hosted.controller.application.SourceRevision; import com.yahoo.vespa.hosted.controller.deployment.JobController; import com.yahoo.vespa.hosted.controller.deployment.RunStatus; import com.yahoo.vespa.hosted.controller.deployment.Step; +import com.yahoo.vespa.hosted.controller.deployment.Versions; import org.json.JSONException; import org.json.JSONObject; import org.junit.Assert; @@ -24,9 +27,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.time.Instant; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -38,6 +39,13 @@ public class JobControllerApiHandlerHelperTest { private final ApplicationId appId = ApplicationId.from("vespa", "music", "default"); private final Instant start = Instant.parse("2018-06-27T10:12:35Z"); + static final Versions versions = new Versions(Version.fromString("1.2.3"), + ApplicationVersion.from(new SourceRevision("repo", + "branch", + "bada55"), + 321), + Optional.empty(), + Optional.empty()); private static Step lastStep = Step.values()[Step.values().length - 1]; @@ -131,7 +139,7 @@ public class JobControllerApiHandlerHelperTest { end = Optional.of(start.plusSeconds(duration)); } - return new RunStatus(runId, stepStatusMap, start, end, false); + return new RunStatus(runId, stepStatusMap, versions, start, end, false); } private void compare(HttpResponse response, String expected) { |