From bc986626e14653d728da708b84a6738c092db1d7 Mon Sep 17 00:00:00 2001 From: pfurio Date: Thu, 8 May 2025 11:05:53 +0200 Subject: [PATCH] commons: add authentication to DockerUtils class, #TASK-7610 --- .../java/org/opencb/commons/exec/Command.java | 40 +++++++++ .../org/opencb/commons/utils/DockerUtils.java | 82 ++++++++++++++++++- .../opencb/commons/utils/DockerUtilsTest.java | 30 +++++++ 3 files changed, 151 insertions(+), 1 deletion(-) 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 efcbf7f2f..7d6262210 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 @@ -50,6 +50,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()); @@ -92,6 +115,14 @@ public void run() { } 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 is = proc.getInputStream(); // Thread out = readOutputStream(is); Thread readOutputStreamThread = readOutputStream(is); @@ -275,4 +306,13 @@ public Command setPrintOutput(boolean printOutput) { this.printOutput = printOutput; return this; } + + public String getStdinInput() { + return stdinInput; + } + + public Command setStdinInput(String stdinInput) { + this.stdinInput = stdinInput; + return this; + } } 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