diff --git a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java index 600a800c0df..00dd8184f51 100644 --- a/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java +++ b/core/src/main/java/org/testcontainers/utility/RegistryAuthLocator.java @@ -12,6 +12,7 @@ import org.testcontainers.DockerClientFactory; import org.zeroturnaround.exec.InvalidResultException; import org.zeroturnaround.exec.ProcessExecutor; +import org.zeroturnaround.exec.stream.LogOutputStream; import java.io.ByteArrayInputStream; import java.io.File; @@ -273,7 +274,7 @@ private AuthConfig runCredentialProvider(String hostName, String helperOrStoreNa } final String credentialProgramName = getCredentialProgramName(helperOrStoreName); - final String data; + final CredentialOutput data; log.debug( "Executing docker credential provider: {} to locate auth config for: {}", @@ -283,37 +284,38 @@ private AuthConfig runCredentialProvider(String hostName, String helperOrStoreNa try { data = runCredentialProgram(hostName, credentialProgramName); - } catch (InvalidResultException e) { - final String responseErrorMsg = extractCredentialProviderErrorMessage(e); - - if (!StringUtils.isBlank(responseErrorMsg)) { - String credentialsNotFoundMsg = getGenericCredentialsNotFoundMsg(credentialProgramName); - if (credentialsNotFoundMsg != null && credentialsNotFoundMsg.equals(responseErrorMsg)) { - log.info( - "Credential helper/store ({}) does not have credentials for {}", + if (data.getStderr() != null && !data.getStderr().isEmpty()) { + final String responseErrorMsg = data.getStderr(); + + if (!StringUtils.isBlank(responseErrorMsg)) { + String credentialsNotFoundMsg = getGenericCredentialsNotFoundMsg(credentialProgramName); + if (credentialsNotFoundMsg != null && credentialsNotFoundMsg.equals(responseErrorMsg)) { + log.info( + "Credential helper/store ({}) does not have credentials for {}", + credentialProgramName, + hostName + ); + + return null; + } + + log.debug( + "Failure running docker credential helper/store ({}) with output '{}'", credentialProgramName, - hostName + responseErrorMsg ); - - return null; + } else { + log.debug("Failure running docker credential helper/store ({})", credentialProgramName); } - log.debug( - "Failure running docker credential helper/store ({}) with output '{}'", - credentialProgramName, - responseErrorMsg - ); - } else { - log.debug("Failure running docker credential helper/store ({})", credentialProgramName); + throw new InvalidResultException(data.getStderr(), null); } - - throw e; } catch (Exception e) { log.debug("Failure running docker credential helper/store ({})", credentialProgramName); throw e; } - final JsonNode helperResponse = OBJECT_MAPPER.readTree(data); + final JsonNode helperResponse = OBJECT_MAPPER.readTree(data.getStdout()); log.debug("Credential helper/store provided auth config for: {}", hostName); final String username = helperResponse.at("/Username").asText(); @@ -363,55 +365,81 @@ private String discoverCredentialsHelperNotFoundMessage(String credentialHelperN String credentialsNotFoundMsg = null; try { - runCredentialProgram(notExistentFakeHostName, credentialHelperName); + CredentialOutput data = runCredentialProgram(notExistentFakeHostName, credentialHelperName); - // should not reach here - log.warn( - "Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response", - credentialHelperName - ); - } catch (Exception e) { - if (e instanceof InvalidResultException) { - credentialsNotFoundMsg = extractCredentialProviderErrorMessage((InvalidResultException) e); - } + if (data.getStderr() != null && !data.getStderr().isEmpty()) { + credentialsNotFoundMsg = data.getStderr(); - if (StringUtils.isBlank(credentialsNotFoundMsg)) { - log.warn( - "Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response. Exception message: {}", - credentialHelperName, - e.getMessage() - ); - } else { log.debug( "Got credentials not found error message from docker credential helper - {}", credentialsNotFoundMsg ); } + } catch (Exception e) { + log.warn( + "Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response. Exception message: {}", + credentialHelperName, + e.getMessage() + ); } return credentialsNotFoundMsg; } - private String extractCredentialProviderErrorMessage(InvalidResultException invalidResultEx) { - if (invalidResultEx.getResult() != null && invalidResultEx.getResult().hasOutput()) { - return invalidResultEx.getResult().outputString().trim(); - } - return null; - } - - private String runCredentialProgram(String hostName, String credentialHelperName) - throws InvalidResultException, InterruptedException, TimeoutException, IOException { + private CredentialOutput runCredentialProgram(String hostName, String credentialHelperName) + throws InterruptedException, TimeoutException, IOException { String[] command = SystemUtils.IS_OS_WINDOWS ? new String[] { "cmd", "/c", credentialHelperName, "get" } : new String[] { credentialHelperName, "get" }; - return new ProcessExecutor() - .command(command) - .redirectInput(new ByteArrayInputStream(hostName.getBytes())) - .readOutput(true) - .exitValueNormal() - .timeout(30, TimeUnit.SECONDS) - .execute() - .outputUTF8() - .trim(); + + StringBuffer stdout = new StringBuffer(); + StringBuffer stderr = new StringBuffer(); + + try { + new ProcessExecutor() + .command(command) + .redirectInput(new ByteArrayInputStream(hostName.getBytes())) + .redirectOutput( + new LogOutputStream() { + @Override + protected void processLine(String line) { + stdout.append(line).append(System.lineSeparator()); + } + } + ) + .redirectError( + new LogOutputStream() { + @Override + protected void processLine(String line) { + stderr.append(line).append(System.lineSeparator()); + } + } + ) + .exitValueNormal() + .timeout(30, TimeUnit.SECONDS) + .execute(); + } catch (InvalidResultException e) {} + + return new CredentialOutput(stdout.toString(), stderr.toString()); + } + + static class CredentialOutput { + + private final String stdout; + + private final String stderr; + + public CredentialOutput(String stdout, String stderr) { + this.stdout = stdout.trim(); + this.stderr = stderr.trim(); + } + + public String getStdout() { + return this.stdout; + } + + public String getStderr() { + return this.stderr; + } } }