diff --git a/commons-lib/src/main/java/org/opencb/commons/exec/Command.java b/commons-lib/src/main/java/org/opencb/commons/exec/Command.java index dd9e7182e..0d7ca9614 100755 --- a/commons-lib/src/main/java/org/opencb/commons/exec/Command.java +++ b/commons-lib/src/main/java/org/opencb/commons/exec/Command.java @@ -59,6 +59,29 @@ public class Command extends RunnableProcess { private final String[] cmdArray; private boolean printOutput = true; + private String stdinInput; + + // Add a constructor that takes stdin input + public Command(String commandLine, String stdinInput) { + this(commandLine); + this.stdinInput = stdinInput; + } + + public Command(String commandLine, List environment, String stdinInput) { + this(commandLine, environment); + this.stdinInput = stdinInput; + } + + public Command(String[] cmdArray, List environment, String stdinInput) { + this(cmdArray, environment); + this.stdinInput = stdinInput; + } + + public Command(String[] cmdArray, Map environment, String stdinInput) { + this(cmdArray, environment); + this.stdinInput = stdinInput; + } + public Command(String commandLine) { this.commandLine = commandLine; cmdArray = Commandline.translateCommandline(getCommandLine()); @@ -109,6 +132,14 @@ public Future run(boolean background) { } setStatus(Status.RUNNING); + // Write to stdin if input is provided + if (stdinInput != null) { + try (OutputStream stdin = proc.getOutputStream()) { + stdin.write(stdinInput.getBytes()); + stdin.flush(); + } + } + InputStream stdout = proc.getInputStream(); Future readOutputStreamThread = readOutputStream(stdout); InputStream stderr = proc.getErrorStream(); @@ -310,6 +341,15 @@ public Command setPrintOutput(boolean printOutput) { return this; } + public String getStdinInput() { + return stdinInput; + } + + public Command setStdinInput(String stdinInput) { + this.stdinInput = stdinInput; + return this; + } + public Process getProc() { return proc; } diff --git a/commons-lib/src/main/java/org/opencb/commons/utils/DockerUtils.java b/commons-lib/src/main/java/org/opencb/commons/utils/DockerUtils.java index 6d1c9574c..892fe7554 100644 --- a/commons-lib/src/main/java/org/opencb/commons/utils/DockerUtils.java +++ b/commons-lib/src/main/java/org/opencb/commons/utils/DockerUtils.java @@ -159,6 +159,58 @@ private static List getPaths(String entryPoint) { return new ArrayList<>(res); } + /** + * Login into dockerhub with the given credentials. + * + * @param registry Docker registry URL + * @param username Docker registry username + * @param password Docker registry password + * @return True if login was successful, false otherwise + * @throws IOException IO exception + */ + public static boolean login(String registry, String username, String password) throws IOException { + if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { + throw new IllegalArgumentException("Username and password are required"); + } + if (registry == null) { + // Leave it empty (defaults to dockerhub) + registry = ""; + } + + String commandLine = "docker login " + registry + " --username " + username + " --password-stdin"; + + LOGGER.info("Running docker login command"); + Command cmd = new Command(commandLine, password); + cmd.run(); + + if (cmd.getExitValue() != 0) { + LOGGER.error("Docker login failed: {}", cmd.getError()); + return false; + } + + return true; + } + + /** + * Create and run the command line to execute the docker image with optional registry authentication. + * + * @param image Docker image name + * @param inputBindings Array of bind mounts for docker input volumes (source-target) + * @param outputBinding Bind mount for docker output volume (source-target) + * @param cmdParams Image command parameters + * @param dockerParams Docker parameters + * @param registry Docker registry URL + * @param username Optional Docker registry username + * @param password Optional Docker registry password + * @return The command line + * @throws IOException IO exception + */ + public static String run(String image, List> inputBindings, + AbstractMap.SimpleEntry outputBinding, String cmdParams, + Map dockerParams, String registry, String username, String password) throws IOException { + return run(image, inputBindings, outputBinding != null ? Collections.singletonList(outputBinding) : null, + cmdParams, dockerParams, registry, username, password); + } /** * Create and run the command line to execute the docker image. @@ -191,8 +243,37 @@ public static String run(String image, List> inputBindings, List> outputBindings, String cmdParams, Map dockerParams) throws IOException { + return run(image, inputBindings, outputBindings, cmdParams, dockerParams, null, null, null); + } + + /** + * Create and run the command line to execute the docker image with optional registry authentication. + * + * @param image Docker image name + * @param inputBindings Array of bind mounts for docker input volumes (source-target) + * @param outputBindings Array of bind mount for docker output volume (source-target) + * @param cmdParams Image command parameters + * @param dockerParams Docker parameters + * @param registry Optional Docker registry URL (null if not using private registry) + * @param username Optional Docker registry username + * @param password Optional Docker registry password + * @return The command line + * @throws IOException IO exception + */ + public static String run(String image, List> inputBindings, + List> outputBindings, String cmdParams, + Map dockerParams, String registry, String username, String password) throws IOException { + checkDockerDaemonAlive(); + // Login to registry if credentials provided + if (StringUtils.isNotEmpty(username) && StringUtils.isNotEmpty(password)) { + boolean loginSuccess = login(registry, username, password); + if (!loginSuccess) { + throw new IOException("Failed to authenticate to Dockerhub"); + } + } + String commandLine = buildCommandLine(image, inputBindings, outputBindings, cmdParams, dockerParams); LOGGER.info("Run docker command line"); @@ -222,7 +303,6 @@ public static String run(String image, List