Skip to content

Commit e35705b

Browse files
Smebeddumelendez
andauthored
Read docker credentials stdout and stderr independently (#8007)
Fixes #8006 --------- Co-authored-by: Eddú Meléndez Gonzales <[email protected]>
1 parent 3c7714e commit e35705b

File tree

1 file changed

+85
-57
lines changed

1 file changed

+85
-57
lines changed

core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java

Lines changed: 85 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.testcontainers.DockerClientFactory;
1313
import org.zeroturnaround.exec.InvalidResultException;
1414
import org.zeroturnaround.exec.ProcessExecutor;
15+
import org.zeroturnaround.exec.stream.LogOutputStream;
1516

1617
import java.io.ByteArrayInputStream;
1718
import java.io.File;
@@ -273,7 +274,7 @@ private AuthConfig runCredentialProvider(String hostName, String helperOrStoreNa
273274
}
274275

275276
final String credentialProgramName = getCredentialProgramName(helperOrStoreName);
276-
final String data;
277+
final CredentialOutput data;
277278

278279
log.debug(
279280
"Executing docker credential provider: {} to locate auth config for: {}",
@@ -283,37 +284,38 @@ private AuthConfig runCredentialProvider(String hostName, String helperOrStoreNa
283284

284285
try {
285286
data = runCredentialProgram(hostName, credentialProgramName);
286-
} catch (InvalidResultException e) {
287-
final String responseErrorMsg = extractCredentialProviderErrorMessage(e);
288-
289-
if (!StringUtils.isBlank(responseErrorMsg)) {
290-
String credentialsNotFoundMsg = getGenericCredentialsNotFoundMsg(credentialProgramName);
291-
if (credentialsNotFoundMsg != null && credentialsNotFoundMsg.equals(responseErrorMsg)) {
292-
log.info(
293-
"Credential helper/store ({}) does not have credentials for {}",
287+
if (data.getStderr() != null && !data.getStderr().isEmpty()) {
288+
final String responseErrorMsg = data.getStderr();
289+
290+
if (!StringUtils.isBlank(responseErrorMsg)) {
291+
String credentialsNotFoundMsg = getGenericCredentialsNotFoundMsg(credentialProgramName);
292+
if (credentialsNotFoundMsg != null && credentialsNotFoundMsg.equals(responseErrorMsg)) {
293+
log.info(
294+
"Credential helper/store ({}) does not have credentials for {}",
295+
credentialProgramName,
296+
hostName
297+
);
298+
299+
return null;
300+
}
301+
302+
log.debug(
303+
"Failure running docker credential helper/store ({}) with output '{}'",
294304
credentialProgramName,
295-
hostName
305+
responseErrorMsg
296306
);
297-
298-
return null;
307+
} else {
308+
log.debug("Failure running docker credential helper/store ({})", credentialProgramName);
299309
}
300310

301-
log.debug(
302-
"Failure running docker credential helper/store ({}) with output '{}'",
303-
credentialProgramName,
304-
responseErrorMsg
305-
);
306-
} else {
307-
log.debug("Failure running docker credential helper/store ({})", credentialProgramName);
311+
throw new InvalidResultException(data.getStderr(), null);
308312
}
309-
310-
throw e;
311313
} catch (Exception e) {
312314
log.debug("Failure running docker credential helper/store ({})", credentialProgramName);
313315
throw e;
314316
}
315317

316-
final JsonNode helperResponse = OBJECT_MAPPER.readTree(data);
318+
final JsonNode helperResponse = OBJECT_MAPPER.readTree(data.getStdout());
317319
log.debug("Credential helper/store provided auth config for: {}", hostName);
318320

319321
final String username = helperResponse.at("/Username").asText();
@@ -363,55 +365,81 @@ private String discoverCredentialsHelperNotFoundMessage(String credentialHelperN
363365

364366
String credentialsNotFoundMsg = null;
365367
try {
366-
runCredentialProgram(notExistentFakeHostName, credentialHelperName);
368+
CredentialOutput data = runCredentialProgram(notExistentFakeHostName, credentialHelperName);
367369

368-
// should not reach here
369-
log.warn(
370-
"Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response",
371-
credentialHelperName
372-
);
373-
} catch (Exception e) {
374-
if (e instanceof InvalidResultException) {
375-
credentialsNotFoundMsg = extractCredentialProviderErrorMessage((InvalidResultException) e);
376-
}
370+
if (data.getStderr() != null && !data.getStderr().isEmpty()) {
371+
credentialsNotFoundMsg = data.getStderr();
377372

378-
if (StringUtils.isBlank(credentialsNotFoundMsg)) {
379-
log.warn(
380-
"Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response. Exception message: {}",
381-
credentialHelperName,
382-
e.getMessage()
383-
);
384-
} else {
385373
log.debug(
386374
"Got credentials not found error message from docker credential helper - {}",
387375
credentialsNotFoundMsg
388376
);
389377
}
378+
} catch (Exception e) {
379+
log.warn(
380+
"Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response. Exception message: {}",
381+
credentialHelperName,
382+
e.getMessage()
383+
);
390384
}
391385

392386
return credentialsNotFoundMsg;
393387
}
394388

395-
private String extractCredentialProviderErrorMessage(InvalidResultException invalidResultEx) {
396-
if (invalidResultEx.getResult() != null && invalidResultEx.getResult().hasOutput()) {
397-
return invalidResultEx.getResult().outputString().trim();
398-
}
399-
return null;
400-
}
401-
402-
private String runCredentialProgram(String hostName, String credentialHelperName)
403-
throws InvalidResultException, InterruptedException, TimeoutException, IOException {
389+
private CredentialOutput runCredentialProgram(String hostName, String credentialHelperName)
390+
throws InterruptedException, TimeoutException, IOException {
404391
String[] command = SystemUtils.IS_OS_WINDOWS
405392
? new String[] { "cmd", "/c", credentialHelperName, "get" }
406393
: new String[] { credentialHelperName, "get" };
407-
return new ProcessExecutor()
408-
.command(command)
409-
.redirectInput(new ByteArrayInputStream(hostName.getBytes()))
410-
.readOutput(true)
411-
.exitValueNormal()
412-
.timeout(30, TimeUnit.SECONDS)
413-
.execute()
414-
.outputUTF8()
415-
.trim();
394+
395+
StringBuffer stdout = new StringBuffer();
396+
StringBuffer stderr = new StringBuffer();
397+
398+
try {
399+
new ProcessExecutor()
400+
.command(command)
401+
.redirectInput(new ByteArrayInputStream(hostName.getBytes()))
402+
.redirectOutput(
403+
new LogOutputStream() {
404+
@Override
405+
protected void processLine(String line) {
406+
stdout.append(line).append(System.lineSeparator());
407+
}
408+
}
409+
)
410+
.redirectError(
411+
new LogOutputStream() {
412+
@Override
413+
protected void processLine(String line) {
414+
stderr.append(line).append(System.lineSeparator());
415+
}
416+
}
417+
)
418+
.exitValueNormal()
419+
.timeout(30, TimeUnit.SECONDS)
420+
.execute();
421+
} catch (InvalidResultException e) {}
422+
423+
return new CredentialOutput(stdout.toString(), stderr.toString());
424+
}
425+
426+
static class CredentialOutput {
427+
428+
private final String stdout;
429+
430+
private final String stderr;
431+
432+
public CredentialOutput(String stdout, String stderr) {
433+
this.stdout = stdout.trim();
434+
this.stderr = stderr.trim();
435+
}
436+
437+
public String getStdout() {
438+
return this.stdout;
439+
}
440+
441+
public String getStderr() {
442+
return this.stderr;
443+
}
416444
}
417445
}

0 commit comments

Comments
 (0)