diff --git a/src/main/java/io/seqera/tower/cli/commands/computeenvs/DeleteCmd.java b/src/main/java/io/seqera/tower/cli/commands/computeenvs/DeleteCmd.java index e86ecbab..f7e61de1 100644 --- a/src/main/java/io/seqera/tower/cli/commands/computeenvs/DeleteCmd.java +++ b/src/main/java/io/seqera/tower/cli/commands/computeenvs/DeleteCmd.java @@ -17,14 +17,21 @@ package io.seqera.tower.cli.commands.computeenvs; import io.seqera.tower.ApiException; +import io.seqera.tower.cli.commands.enums.OutputType; import io.seqera.tower.cli.commands.global.WorkspaceOptionalOptions; import io.seqera.tower.cli.exceptions.ComputeEnvNotFoundException; import io.seqera.tower.cli.responses.Response; import io.seqera.tower.cli.responses.computeenvs.ComputeEnvDeleted; import io.seqera.tower.model.ComputeEnvResponseDto; +import io.seqera.tower.model.ComputeEnvStatus; import picocli.CommandLine; import picocli.CommandLine.Command; +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +import static io.seqera.tower.cli.utils.ResponseHelper.waitStatus; + @Command( name = "delete", description = "Delete a compute environment." @@ -37,6 +44,9 @@ public class DeleteCmd extends AbstractComputeEnvCmd { @CommandLine.Mixin public WorkspaceOptionalOptions workspace; + @CommandLine.Option(names = {"--wait"}, description = "Wait until the compute environment is fully deleted.") + public boolean wait; + @Override protected Response exec() throws ApiException { Long wspId = workspaceId(workspace.workspace); @@ -51,7 +61,7 @@ protected Response exec() throws ApiException { try { computeEnvsApi().deleteComputeEnv(id, wspId, null); - return new ComputeEnvDeleted(id, workspaceRef(wspId)); + return new ComputeEnvDeleted(id, workspaceRef(wspId), wspId); } catch (ApiException e) { if (e.getCode() == 403) { // Customize the forbidden message @@ -60,4 +70,36 @@ protected Response exec() throws ApiException { throw e; } } -} + + @Override + protected Integer onBeforeExit(int exitCode, Response response) { + if (exitCode != 0 || !wait || response == null) { + return exitCode; + } + + ComputeEnvDeleted computeEnv = (ComputeEnvDeleted) response; + boolean showProgress = app().output != OutputType.json; + + try { + return waitStatus( + app().getOut(), + showProgress, + ComputeEnvStatus.DELETED, + ComputeEnvStatus.values(), + () -> checkComputeEnvStatus(computeEnv.id, computeEnv.workspaceId), + ComputeEnvStatus.ERRORED, ComputeEnvStatus.INVALID + ); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return exitCode; + } + } + + private ComputeEnvStatus checkComputeEnvStatus(String computeEnvId, Long workspaceId) { + try { + return computeEnvsApi().describeComputeEnv(computeEnvId, workspaceId, Collections.emptyList()).getComputeEnv().getStatus(); + } catch (ApiException | NullPointerException e) { + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/io/seqera/tower/cli/responses/computeenvs/ComputeEnvDeleted.java b/src/main/java/io/seqera/tower/cli/responses/computeenvs/ComputeEnvDeleted.java index fe2e8d10..f38c4537 100644 --- a/src/main/java/io/seqera/tower/cli/responses/computeenvs/ComputeEnvDeleted.java +++ b/src/main/java/io/seqera/tower/cli/responses/computeenvs/ComputeEnvDeleted.java @@ -22,9 +22,11 @@ public class ComputeEnvDeleted extends Response { public final String id; public final String workspaceRef; + public final Long workspaceId; - public ComputeEnvDeleted(String id, String workspaceRef) { + public ComputeEnvDeleted(String id, String workspaceRef, Long workspaceId) { this.id = id; + this.workspaceId = workspaceId; this.workspaceRef = workspaceRef; } diff --git a/src/test/java/io/seqera/tower/cli/computeenvs/ComputeEnvsCmdTest.java b/src/test/java/io/seqera/tower/cli/computeenvs/ComputeEnvsCmdTest.java index f83e6022..65772376 100644 --- a/src/test/java/io/seqera/tower/cli/computeenvs/ComputeEnvsCmdTest.java +++ b/src/test/java/io/seqera/tower/cli/computeenvs/ComputeEnvsCmdTest.java @@ -76,7 +76,7 @@ void testDelete(OutputType format, MockServerClient mock) { ); ExecOut out = exec(format, mock, "compute-envs", "delete", "-i", "vYOK4vn7spw7bHHWBDXZ2"); - assertOutput(format, out, new ComputeEnvDeleted("vYOK4vn7spw7bHHWBDXZ2", USER_WORKSPACE_NAME)); + assertOutput(format, out, new ComputeEnvDeleted("vYOK4vn7spw7bHHWBDXZ2", USER_WORKSPACE_NAME, null)); } @Test @@ -712,4 +712,53 @@ void testUpdateInvalidName(OutputType format, MockServerClient mock) { assertEquals("", out.stdOut); assertEquals(1, out.exitCode); } + + @Test + void testDeleteWaitHappyPath(MockServerClient mock) { + // DELETE returns 204 + mock.when( + request().withMethod("DELETE").withPath("/compute-envs/vYOK4vn7spw7bHHWBDXZ2"), exactly(1) + ).respond( + response().withStatusCode(204) + ); + + // First DESCRIBE returns DELETING + mock.when( + request().withMethod("GET").withPath("/compute-envs/vYOK4vn7spw7bHHWBDXZ2"), exactly(1) + ).respond( + response().withStatusCode(200).withBody("{\"computeEnv\":{\"id\":\"vYOK4vn7spw7bHHWBDXZ2\",\"name\":\"demo\",\"platform\":\"aws-batch\",\"status\":\"DELETING\"}}").withContentType(MediaType.APPLICATION_JSON) + ); + + // Second DESCRIBE returns 404 (CE has been deleted) + mock.when( + request().withMethod("GET").withPath("/compute-envs/vYOK4vn7spw7bHHWBDXZ2"), exactly(1) + ).respond( + response().withStatusCode(200).withBody("{\"computeEnv\":{\"id\":\"vYOK4vn7spw7bHHWBDXZ2\",\"name\":\"demo\",\"platform\":\"aws-batch\",\"status\":\"DELETED\"}}").withContentType(MediaType.APPLICATION_JSON) + ); + + ExecOut out = exec(mock, "compute-envs", "delete", "-i", "vYOK4vn7spw7bHHWBDXZ2", "--wait"); + + assertEquals(0, out.exitCode); + } + + @Test + void testDeleteWaitErrored(MockServerClient mock) { + // DELETE returns 204 + mock.when( + request().withMethod("DELETE").withPath("/compute-envs/vYOK4vn7spw7bHHWBDXZ2"), exactly(1) + ).respond( + response().withStatusCode(204) + ); + + // DESCRIBE returns ERRORED + mock.when( + request().withMethod("GET").withPath("/compute-envs/vYOK4vn7spw7bHHWBDXZ2"), exactly(1) + ).respond( + response().withStatusCode(200).withBody("{\"computeEnv\":{\"id\":\"vYOK4vn7spw7bHHWBDXZ2\",\"name\":\"demo\",\"platform\":\"aws-batch\",\"status\":\"ERRORED\"}}").withContentType(MediaType.APPLICATION_JSON) + ); + + ExecOut out = exec(mock, "compute-envs", "delete", "-i", "vYOK4vn7spw7bHHWBDXZ2", "--wait"); + + assertEquals(1, out.exitCode); + } }