Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,19 @@
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;

@Command(
name = "delete",
description = "Delete a compute environment."
Expand All @@ -37,6 +42,12 @@ 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;

private String deletedId;
private Long deletedWspId;

@Override
protected Response exec() throws ApiException {
Long wspId = workspaceId(workspace.workspace);
Expand All @@ -51,6 +62,8 @@ protected Response exec() throws ApiException {

try {
computeEnvsApi().deleteComputeEnv(id, wspId);
deletedId = id;
deletedWspId = wspId;
return new ComputeEnvDeleted(id, workspaceRef(wspId));
} catch (ApiException e) {
if (e.getCode() == 403) {
Expand All @@ -60,4 +73,52 @@ protected Response exec() throws ApiException {
throw e;
}
}

@Override
protected Integer onBeforeExit(int exitCode, Response response) {
if (exitCode != 0 || !wait || response == null) {
return exitCode;
}

boolean showProgress = app().output != OutputType.json;

try {
long sleepMillis = 2000;
while (true) {
ComputeEnvStatus status = checkComputeEnvStatus(deletedId, deletedWspId);

if (status == null) {
// CE is gone (404) - deletion succeeded
if (showProgress) {
app().getOut().println();
}
return CommandLine.ExitCode.OK;
}

if (status == ComputeEnvStatus.ERRORED) {
app().getErr().println("ERROR: Compute environment disposal failed (status: ERRORED). AWS resources may not have been cleaned up.");
return CommandLine.ExitCode.SOFTWARE;
}

if (showProgress) {
app().getOut().print(".");
app().getOut().flush();
}

TimeUnit.MILLISECONDS.sleep(sleepMillis);
sleepMillis = Math.min(sleepMillis + 1000, 120_000);
}
} 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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -675,4 +675,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(404)
);

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);
}
}
Loading