diff options
8 files changed, 78 insertions, 9 deletions
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 069b992290c..293f4f9a8e3 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 @@ -26,6 +26,7 @@ import com.yahoo.vespa.hosted.controller.deployment.DeploymentSteps; import com.yahoo.vespa.hosted.controller.deployment.JobController; import com.yahoo.vespa.hosted.controller.deployment.Run; import com.yahoo.vespa.hosted.controller.deployment.RunLog; +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 com.yahoo.vespa.hosted.controller.restapi.MessageResponse; @@ -325,7 +326,7 @@ class JobControllerApiHandlerHelper { private static void runToSlime(Cursor runObject, Run run, URI baseUriForJobType) { runObject.setLong("id", run.id().number()); - runObject.setString("status", run.status().name()); + runObject.setString("status", nameOf(run.status())); runObject.setLong("start", run.start().toEpochMilli()); run.end().ifPresent(instant -> runObject.setLong("end", instant.toEpochMilli())); @@ -385,7 +386,10 @@ class JobControllerApiHandlerHelper { Slime slime = new Slime(); Cursor detailsObject = slime.setObject(); - detailsObject.setBool("active", jobController.active(runId).isPresent()); + Run run = jobController.run(runId) + .orElseThrow(() -> new IllegalStateException("Unknown run '" + runId + "'")); + detailsObject.setBool("active", ! run.hasEnded()); + detailsObject.setString("status", nameOf(run.status())); jobController.updateTestLog(runId); RunLog runLog = (after == null ? jobController.details(runId) : jobController.details(runId, Long.parseLong(after))) @@ -455,5 +459,19 @@ class JobControllerApiHandlerHelper { return new SlimeJsonResponse(slime); } + private static String nameOf(RunStatus status) { + switch (status) { + case running: return "running"; + case aborted: return "aborted"; + case error: return "error"; + case testFailure: return "testFailure"; + case outOfCapacity: return "outOfCapacity"; + case installationFailed: return "installationFailed"; + case deploymentFailed: return "deploymentFailed"; + case success: return "success"; + default: throw new IllegalArgumentException("Unexpected status '" + status + "'"); + } + } + } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json index 98d4e2db612..10d7f3260c1 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/system-test-details.json @@ -1,4 +1,5 @@ { "active": true, + "status": "running", "log": {} } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/us-east-3-log-without-first.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/us-east-3-log-without-first.json index 2891ea943f2..6d58392cb25 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/us-east-3-log-without-first.json +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/us-east-3-log-without-first.json @@ -1,5 +1,6 @@ { "active": false, + "status": "deploymentFailed", "log": { "deployTester": [ { diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java b/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java index bcbcb99587d..c6c5c64bd9f 100644 --- a/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java +++ b/hosted-api/src/main/java/ai/vespa/hosted/api/ControllerHttpClient.java @@ -304,6 +304,7 @@ public abstract class ControllerHttpClient { })); return new DeploymentLog(entries, rootObject.field("active").asBool(), + valueOf(rootObject.field("status").asString()), rootObject.field("lastId").valid() ? OptionalLong.of(rootObject.field("lastId").asLong()) : OptionalLong.empty()); } @@ -367,4 +368,18 @@ public abstract class ControllerHttpClient { } + private static DeploymentLog.Status valueOf(String status) { + switch (status) { + case "running": return DeploymentLog.Status.running; + case "aborted": return DeploymentLog.Status.aborted; + case "error": return DeploymentLog.Status.error; + case "testFailure": return DeploymentLog.Status.testFailure; + case "outOfCapacity": return DeploymentLog.Status.outOfCapacity; + case "installationFailed": return DeploymentLog.Status.installationFailed; + case "deploymentFailed": return DeploymentLog.Status.deploymentFailed; + case "success": return DeploymentLog.Status.success; + default: throw new IllegalArgumentException("Unexpected status '" + status + "'"); + } + } + } diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/DeploymentLog.java b/hosted-api/src/main/java/ai/vespa/hosted/api/DeploymentLog.java index f2da64e9403..a42072fffd8 100644 --- a/hosted-api/src/main/java/ai/vespa/hosted/api/DeploymentLog.java +++ b/hosted-api/src/main/java/ai/vespa/hosted/api/DeploymentLog.java @@ -16,11 +16,13 @@ public class DeploymentLog { private final List<Entry> entries; private final boolean active; + private final Status status; private final OptionalLong last; - public DeploymentLog(List<Entry> entries, boolean active, OptionalLong last) { + public DeploymentLog(List<Entry> entries, boolean active, Status status, OptionalLong last) { this.entries = entries.stream().sorted(comparing(Entry::at)).collect(toUnmodifiableList()); this.active = active; + this.status = status; this.last = last; } @@ -32,6 +34,10 @@ public class DeploymentLog { return active; } + public Status status() { + return status; + } + public OptionalLong last() { return last; } @@ -63,4 +69,16 @@ public class DeploymentLog { } + + public enum Status { + running, + aborted, + error, + testFailure, + outOfCapacity, + installationFailed, + deploymentFailed, + success; + } + } diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/DeploymentResult.java b/hosted-api/src/main/java/ai/vespa/hosted/api/DeploymentResult.java index e78a7e926b4..34ce04b95fd 100644 --- a/hosted-api/src/main/java/ai/vespa/hosted/api/DeploymentResult.java +++ b/hosted-api/src/main/java/ai/vespa/hosted/api/DeploymentResult.java @@ -1,8 +1,6 @@ package ai.vespa.hosted.api; -import java.net.URI; - /** * Contains information about the result of a {@link Deployment} against a {@link ControllerHttpClient}. * diff --git a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java index e2bbbb86706..b7c49b289fc 100644 --- a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java +++ b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/AbstractVespaMojo.java @@ -4,6 +4,7 @@ import ai.vespa.hosted.api.ControllerHttpClient; import com.yahoo.config.provision.ApplicationId; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; @@ -44,11 +45,14 @@ public abstract class AbstractVespaMojo extends AbstractMojo { protected ControllerHttpClient controller; @Override - public final void execute() throws MojoExecutionException { + public final void execute() throws MojoExecutionException, MojoFailureException { try { setup(); doExecute(); } + catch (MojoFailureException | MojoExecutionException e) { + throw e; + } catch (Exception e) { throw new MojoExecutionException("Execution failed for application '" + id + "':", e); } diff --git a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java index 9ef31cafb1b..7deffd37a79 100644 --- a/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java +++ b/vespa-maven-plugin/src/main/java/ai/vespa/hosted/plugin/DeployMojo.java @@ -5,6 +5,8 @@ import ai.vespa.hosted.api.DeploymentLog; import ai.vespa.hosted.api.DeploymentResult; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.zone.ZoneId; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; @@ -30,7 +32,7 @@ public class DeployMojo extends AbstractVespaDeploymentMojo { private boolean follow; @Override - protected void doExecute() { + protected void doExecute() throws MojoFailureException, MojoExecutionException { Deployment deployment = Deployment.ofPackage(Paths.get(firstNonBlank(applicationZip, projectPathOf("target", "application.zip")))); if (vespaVersion != null) deployment = deployment.atVersion(vespaVersion); @@ -42,10 +44,11 @@ public class DeployMojo extends AbstractVespaDeploymentMojo { if (follow) tailLogs(id, zone, result.run()); } - private void tailLogs(ApplicationId id, ZoneId zone, long run) { + private void tailLogs(ApplicationId id, ZoneId zone, long run) throws MojoFailureException, MojoExecutionException { long last = -1; + DeploymentLog log; while (true) { - DeploymentLog log = controller.deploymentLog(id, zone, run, last); + log = controller.deploymentLog(id, zone, run, last); for (DeploymentLog.Entry entry : log.entries()) print(entry); last = log.last().orElse(last); @@ -61,6 +64,17 @@ public class DeployMojo extends AbstractVespaDeploymentMojo { break; } } + switch (log.status()) { + case success: return; + case error: throw new MojoExecutionException("Unexpected error during deployment; see log for details"); + case aborted: throw new MojoFailureException("Deployment was aborted, probably by a newer deployment"); + case outOfCapacity: throw new MojoFailureException("No capacity left in zone; please contact the Vespa team"); + case deploymentFailed: throw new MojoFailureException("Deployment failed; see log for details"); + case installationFailed: throw new MojoFailureException("Installation failed; see Vespa log for details"); + case running: throw new MojoFailureException("Deployment not completed"); + case testFailure: throw new IllegalStateException("Unexpected status; tests are not run for manual deployments"); + default: throw new IllegalArgumentException("Unexpected status '" + log.status() + "'"); + } } private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss").withZone(ZoneOffset.UTC); |