Skip to content

Commit bc98662

Browse files
committed
commons: add authentication to DockerUtils class, #TASK-7610
1 parent 5b0c4e6 commit bc98662

3 files changed

Lines changed: 151 additions & 1 deletion

File tree

commons-lib/src/main/java/org/opencb/commons/exec/Command.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,29 @@ public class Command extends RunnableProcess {
5050
private final String[] cmdArray;
5151
private boolean printOutput = true;
5252

53+
private String stdinInput;
54+
55+
// Add a constructor that takes stdin input
56+
public Command(String commandLine, String stdinInput) {
57+
this(commandLine);
58+
this.stdinInput = stdinInput;
59+
}
60+
61+
public Command(String commandLine, List<String> environment, String stdinInput) {
62+
this(commandLine, environment);
63+
this.stdinInput = stdinInput;
64+
}
65+
66+
public Command(String[] cmdArray, List<String> environment, String stdinInput) {
67+
this(cmdArray, environment);
68+
this.stdinInput = stdinInput;
69+
}
70+
71+
public Command(String[] cmdArray, Map<String, String> environment, String stdinInput) {
72+
this(cmdArray, environment);
73+
this.stdinInput = stdinInput;
74+
}
75+
5376
public Command(String commandLine) {
5477
this.commandLine = commandLine;
5578
cmdArray = Commandline.translateCommandline(getCommandLine());
@@ -92,6 +115,14 @@ public void run() {
92115
}
93116
setStatus(Status.RUNNING);
94117

118+
// Write to stdin if input is provided
119+
if (stdinInput != null) {
120+
try (OutputStream stdin = proc.getOutputStream()) {
121+
stdin.write(stdinInput.getBytes());
122+
stdin.flush();
123+
}
124+
}
125+
95126
InputStream is = proc.getInputStream();
96127
// Thread out = readOutputStream(is);
97128
Thread readOutputStreamThread = readOutputStream(is);
@@ -275,4 +306,13 @@ public Command setPrintOutput(boolean printOutput) {
275306
this.printOutput = printOutput;
276307
return this;
277308
}
309+
310+
public String getStdinInput() {
311+
return stdinInput;
312+
}
313+
314+
public Command setStdinInput(String stdinInput) {
315+
this.stdinInput = stdinInput;
316+
return this;
317+
}
278318
}

commons-lib/src/main/java/org/opencb/commons/utils/DockerUtils.java

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,58 @@ private static List<String> getPaths(String entryPoint) {
159159
return new ArrayList<>(res);
160160
}
161161

162+
/**
163+
* Login into dockerhub with the given credentials.
164+
*
165+
* @param registry Docker registry URL
166+
* @param username Docker registry username
167+
* @param password Docker registry password
168+
* @return True if login was successful, false otherwise
169+
* @throws IOException IO exception
170+
*/
171+
public static boolean login(String registry, String username, String password) throws IOException {
172+
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
173+
throw new IllegalArgumentException("Username and password are required");
174+
}
175+
if (registry == null) {
176+
// Leave it empty (defaults to dockerhub)
177+
registry = "";
178+
}
179+
180+
String commandLine = "docker login " + registry + " --username " + username + " --password-stdin";
181+
182+
LOGGER.info("Running docker login command");
183+
Command cmd = new Command(commandLine, password);
184+
cmd.run();
185+
186+
if (cmd.getExitValue() != 0) {
187+
LOGGER.error("Docker login failed: {}", cmd.getError());
188+
return false;
189+
}
190+
191+
return true;
192+
}
193+
194+
/**
195+
* Create and run the command line to execute the docker image with optional registry authentication.
196+
*
197+
* @param image Docker image name
198+
* @param inputBindings Array of bind mounts for docker input volumes (source-target)
199+
* @param outputBinding Bind mount for docker output volume (source-target)
200+
* @param cmdParams Image command parameters
201+
* @param dockerParams Docker parameters
202+
* @param registry Docker registry URL
203+
* @param username Optional Docker registry username
204+
* @param password Optional Docker registry password
205+
* @return The command line
206+
* @throws IOException IO exception
207+
*/
208+
public static String run(String image, List<AbstractMap.SimpleEntry<String, String>> inputBindings,
209+
AbstractMap.SimpleEntry<String, String> outputBinding, String cmdParams,
210+
Map<String, String> dockerParams, String registry, String username, String password) throws IOException {
211+
return run(image, inputBindings, outputBinding != null ? Collections.singletonList(outputBinding) : null,
212+
cmdParams, dockerParams, registry, username, password);
213+
}
162214

163215
/**
164216
* Create and run the command line to execute the docker image.
@@ -191,8 +243,37 @@ public static String run(String image, List<AbstractMap.SimpleEntry<String, Stri
191243
public static String run(String image, List<AbstractMap.SimpleEntry<String, String>> inputBindings,
192244
List<AbstractMap.SimpleEntry<String, String>> outputBindings, String cmdParams,
193245
Map<String, String> dockerParams) throws IOException {
246+
return run(image, inputBindings, outputBindings, cmdParams, dockerParams, null, null, null);
247+
}
248+
249+
/**
250+
* Create and run the command line to execute the docker image with optional registry authentication.
251+
*
252+
* @param image Docker image name
253+
* @param inputBindings Array of bind mounts for docker input volumes (source-target)
254+
* @param outputBindings Array of bind mount for docker output volume (source-target)
255+
* @param cmdParams Image command parameters
256+
* @param dockerParams Docker parameters
257+
* @param registry Optional Docker registry URL (null if not using private registry)
258+
* @param username Optional Docker registry username
259+
* @param password Optional Docker registry password
260+
* @return The command line
261+
* @throws IOException IO exception
262+
*/
263+
public static String run(String image, List<AbstractMap.SimpleEntry<String, String>> inputBindings,
264+
List<AbstractMap.SimpleEntry<String, String>> outputBindings, String cmdParams,
265+
Map<String, String> dockerParams, String registry, String username, String password) throws IOException {
266+
194267
checkDockerDaemonAlive();
195268

269+
// Login to registry if credentials provided
270+
if (StringUtils.isNotEmpty(username) && StringUtils.isNotEmpty(password)) {
271+
boolean loginSuccess = login(registry, username, password);
272+
if (!loginSuccess) {
273+
throw new IOException("Failed to authenticate to Dockerhub");
274+
}
275+
}
276+
196277
String commandLine = buildCommandLine(image, inputBindings, outputBindings, cmdParams, dockerParams);
197278

198279
LOGGER.info("Run docker command line");
@@ -222,7 +303,6 @@ public static String run(String image, List<AbstractMap.SimpleEntry<String, Stri
222303
return commandLine;
223304
}
224305

225-
226306
public static void checkDockerDaemonAlive() throws IOException {
227307
int maxAttempts = 12;
228308
for (int i = 0; i < maxAttempts; i++) {

commons-lib/src/test/java/org/opencb/commons/utils/DockerUtilsTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
import org.junit.Test;
44

55
import java.io.IOException;
6+
import java.util.AbstractMap;
67
import java.util.ArrayList;
78
import java.util.Collections;
89
import java.util.List;
910

11+
import static org.junit.Assert.assertTrue;
12+
1013
public class DockerUtilsTest {
1114

1215
@Test
@@ -24,4 +27,31 @@ public void buildMountPathsCommandLine() throws IOException {
2427

2528
}
2629

30+
@Test
31+
public void testDockerLogin() throws IOException {
32+
// Skip test if environment variables are not set
33+
String registry = System.getenv("DOCKER_REGISTRY_URL");
34+
String username = System.getenv("DOCKER_USERNAME");
35+
String password = System.getenv("DOCKER_PASSWORD");
36+
37+
// Skip test if credentials are not provided
38+
org.junit.Assume.assumeTrue("Skipping test: Docker credentials not provided in environment variables",
39+
org.apache.commons.lang3.StringUtils.isNotEmpty(username) && org.apache.commons.lang3.StringUtils.isNotEmpty(password));
40+
41+
try {
42+
// First, check if docker is available
43+
DockerUtils.checkDockerDaemonAlive();
44+
45+
// Execute private docker image with authentication
46+
assertTrue(DockerUtils.login(registry, username, password));
47+
48+
System.out.println("Successfully logged in");
49+
50+
} catch (IOException e) {
51+
System.err.println("Test failed: " + e.getMessage());
52+
throw e;
53+
}
54+
}
55+
56+
2757
}

0 commit comments

Comments
 (0)