diff --git a/app/src/main/java/io/seqera/wave/cli/App.java b/app/src/main/java/io/seqera/wave/cli/App.java
index 716fbe1..dddc8fb 100644
--- a/app/src/main/java/io/seqera/wave/cli/App.java
+++ b/app/src/main/java/io/seqera/wave/cli/App.java
@@ -30,6 +30,7 @@
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
+import java.time.Duration;
import java.time.OffsetDateTime;
import java.util.Arrays;
import java.util.Base64;
@@ -55,6 +56,7 @@
import io.seqera.wave.cli.model.ContainerSpecEx;
import io.seqera.wave.cli.util.BuildInfo;
import io.seqera.wave.cli.util.CliVersionProvider;
+import io.seqera.wave.cli.util.DurationConverter;
import io.seqera.wave.cli.util.YamlHelper;
import io.seqera.wave.config.CondaOpts;
import io.seqera.wave.config.SpackOpts;
@@ -116,11 +118,8 @@ public class App implements Runnable {
@Option(names = {"--platform"}, paramLabel = "''", description = "Platform to be used for the container build. One of: linux/amd64, linux/arm64.")
private String platform;
- @Option(names = {"--await"}, paramLabel = "false", description = "Await the container build to be available.")
- private boolean await;
-
- @Option(names = {"--await-timeout"}, paramLabel = "false", description = "Timeout in minutes for awaiting the container build to be available, default is 15 minutes")
- private Integer awaitTimeout;
+ @Option(names = {"--await"}, paramLabel = "false", arity = "0..1", description = "Await the container build to be available. you can provide a timeout like --await 10m or 2s, by default its 15 minutes.")
+ private Duration await;
@Option(names = {"--context"}, paramLabel = "''", description = "Directory path where the build context is stored e.g. /some/context/path.")
private String contextDir;
@@ -200,6 +199,9 @@ public static void main(String[] args) {
final App app = new App();
final CommandLine cli = new CommandLine(app);
+ //register duration converter
+ cli.registerConverter(Duration.class, new DurationConverter());
+
// add examples in help
cli
.getCommandSpec()
@@ -371,15 +373,12 @@ protected void validateArgs() {
throw new IllegalCliArgumentException("Context path is not a directory - offending value: " + contextDir);
}
- if( dryRun && await )
+ if( dryRun && await != null )
throw new IllegalCliArgumentException("Options --dry-run and --await conflicts each other");
if( !isEmpty(platform) && !VALID_PLATFORMS.contains(platform) )
throw new IllegalCliArgumentException(String.format("Unsupported container platform: '%s'", platform));
- if( !await && awaitTimeout != null)
- throw new IllegalCliArgumentException("--awaitTimeout option is only allowed when --await option is used");
-
}
protected Client client() {
@@ -435,8 +434,8 @@ public void run() {
// submit it
SubmitContainerTokenResponse resp = client.submit(request);
// await build to be completed
- if( await && resp.buildId!=null && !resp.cached )
- client.awaitCompletion(resp.buildId, awaitTimeout);
+ if( await != null && resp.buildId!=null && !resp.cached )
+ client.awaitCompletion(resp.buildId, await);
// print the wave container name
System.out.println(dumpOutput(resp));
}
diff --git a/app/src/main/java/io/seqera/wave/cli/Client.java b/app/src/main/java/io/seqera/wave/cli/Client.java
index fd3f01c..f7220cb 100644
--- a/app/src/main/java/io/seqera/wave/cli/Client.java
+++ b/app/src/main/java/io/seqera/wave/cli/Client.java
@@ -197,12 +197,10 @@ protected URI imageToManifestUri(String image) {
return URI.create(result);
}
- void awaitCompletion(String buildId, Integer timeout) {
- timeout = timeout == null ? 15 : timeout;
- final long maxAwait = Duration.ofMinutes(timeout).toMillis();
+ void awaitCompletion(String buildId, Duration await) {
final long startTime = Instant.now().toEpochMilli();
while (!isComplete(buildId)) {
- if (System.currentTimeMillis() - startTime > maxAwait) {
+ if (System.currentTimeMillis() - startTime > await.toMillis()) {
break;
}
}
diff --git a/app/src/main/java/io/seqera/wave/cli/util/DurationConverter.java b/app/src/main/java/io/seqera/wave/cli/util/DurationConverter.java
new file mode 100644
index 0000000..79ab74b
--- /dev/null
+++ b/app/src/main/java/io/seqera/wave/cli/util/DurationConverter.java
@@ -0,0 +1,38 @@
+/*
+ * Wave, containers provisioning service
+ * Copyright (c) 2023-2024, Seqera Labs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+
+package io.seqera.wave.cli.util;
+
+import picocli.CommandLine;
+
+import java.time.Duration;
+/**
+ * Converter to convert cli argument to duration
+ *
+ * @author Munish Chouhan
+ */
+public class DurationConverter implements CommandLine.ITypeConverter {
+ @Override
+ public Duration convert(String value) {
+ if (value == null || value.trim().isEmpty()) {
+ return Duration.ofMinutes(15);
+ }
+ return Duration.parse("PT" + value.toUpperCase());
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/groovy/io/seqera/wave/cli/AppTest.groovy b/app/src/test/groovy/io/seqera/wave/cli/AppTest.groovy
index f0f1aa0..eeb5c9f 100644
--- a/app/src/test/groovy/io/seqera/wave/cli/AppTest.groovy
+++ b/app/src/test/groovy/io/seqera/wave/cli/AppTest.groovy
@@ -17,7 +17,10 @@
package io.seqera.wave.cli
+import io.seqera.wave.cli.util.DurationConverter
+
import java.nio.file.Files
+import java.time.Duration
import java.time.Instant
import io.seqera.wave.api.SubmitContainerTokenResponse
@@ -192,7 +195,9 @@ class AppTest extends Specification {
String[] args = ["-i", "ubuntu:latest","--dry-run", '--await']
when:
- new CommandLine(app).parseArgs(args)
+ def cli = new CommandLine(app)
+ cli.registerConverter(Duration.class, new DurationConverter())
+ cli.parseArgs(args)
and:
app.validateArgs()
then:
@@ -273,17 +278,54 @@ class AppTest extends Specification {
app.@towerToken == 'xyz'
}
- def 'should not allow awaitTimeout without await'(){
+ def 'should get the correct await duration in minutes'(){
given:
def app = new App()
- String[] args = ["-i", "ubuntu:latest", '--await-timeout', 10]
+ String[] args = ["-i", "ubuntu:latest", '--await', '10m']
when:
- new CommandLine(app).parseArgs(args)
+ def cli = new CommandLine(app)
+ cli.registerConverter(Duration.class, new DurationConverter())
+ cli.parseArgs(args)
and:
app.validateArgs()
then:
- def e = thrown(IllegalCliArgumentException)
- e.message == '--awaitTimeout option is only allowed when --await option is used'
+ noExceptionThrown()
+ and:
+ app.@await == Duration.ofMinutes(10)
+ }
+
+ def 'should get the correct await duration in seconds'(){
+ given:
+ def app = new App()
+ String[] args = ["-i", "ubuntu:latest", '--await', '10s']
+
+ when:
+ def cli = new CommandLine(app)
+ cli.registerConverter(Duration.class, new DurationConverter())
+ cli.parseArgs(args)
+ and:
+ app.validateArgs()
+ then:
+ noExceptionThrown()
+ and:
+ app.@await == Duration.ofSeconds(10)
+ }
+
+ def 'should get the default await duration'(){
+ given:
+ def app = new App()
+ String[] args = ["-i", "ubuntu:latest", '--await']
+
+ when:
+ def cli = new CommandLine(app)
+ cli.registerConverter(Duration.class, new DurationConverter())
+ cli.parseArgs(args)
+ and:
+ app.validateArgs()
+ then:
+ noExceptionThrown()
+ and:
+ app.@await == Duration.ofMinutes(15)
}
}