Merge pull request #45291 from nosan

* pr/45291:
  Polish

Closes gh-45291
This commit is contained in:
Stéphane Nicoll 2025-04-29 12:07:13 +02:00
commit 05ee9b387d
6 changed files with 46 additions and 27 deletions

View File

@ -40,7 +40,7 @@ class Credential extends MappedObject {
private final String secret;
private String serverUrl;
private final String serverUrl;
Credential(JsonNode node) {
super(node, MethodHandles.lookup());

View File

@ -39,7 +39,7 @@ class CredentialHelper {
private static final String USR_LOCAL_BIN = "/usr/local/bin/";
Set<String> CREDENTIAL_NOT_FOUND_MESSAGES = Set.of("credentials not found in native keychain",
private static final Set<String> CREDENTIAL_NOT_FOUND_MESSAGES = Set.of("credentials not found in native keychain",
"no credentials server URL", "no credentials username");
private final String executable;
@ -73,12 +73,12 @@ class CredentialHelper {
}
}
private ProcessBuilder processBuilder(String string) {
private ProcessBuilder processBuilder(String action) {
ProcessBuilder processBuilder = new ProcessBuilder().redirectErrorStream(true);
if (Platform.isWindows()) {
processBuilder.command("cmd", "/c");
}
processBuilder.command(this.executable, string);
processBuilder.command(this.executable, action);
return processBuilder;
}
@ -90,14 +90,21 @@ class CredentialHelper {
if (!Platform.isMac()) {
throw ex;
}
try {
List<String> command = new ArrayList<>(processBuilder.command());
command.set(0, USR_LOCAL_BIN + command.get(0));
return processBuilder.command(command).start();
}
catch (Exception suppressed) {
// Suppresses the exception and rethrows the original exception
ex.addSuppressed(suppressed);
throw ex;
}
}
}
private boolean isCredentialsNotFoundError(String message) {
return this.CREDENTIAL_NOT_FOUND_MESSAGES.contains(message.trim());
private static boolean isCredentialsNotFoundError(String message) {
return CREDENTIAL_NOT_FOUND_MESSAGES.contains(message.trim());
}
}

View File

@ -26,6 +26,7 @@ import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.HexFormat;
import java.util.Map;
import java.util.function.Supplier;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
@ -36,6 +37,7 @@ import org.springframework.boot.buildpack.platform.json.SharedObjectMapper;
import org.springframework.boot.buildpack.platform.system.Environment;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.function.SingletonSupplier;
/**
* Docker configuration stored in metadata files managed by the Docker CLI.
@ -63,7 +65,8 @@ final class DockerConfigurationMetadata {
private static final String CONTEXT_FILE_NAME = "meta.json";
private static volatile DockerConfigurationMetadata systemEnvironmentConfigurationMetadata;
private static final Supplier<DockerConfigurationMetadata> systemEnvironmentConfigurationMetadata = SingletonSupplier
.of(() -> DockerConfigurationMetadata.create(Environment.SYSTEM));
private final String configLocation;
@ -90,20 +93,18 @@ final class DockerConfigurationMetadata {
}
static DockerConfigurationMetadata from(Environment environment) {
DockerConfigurationMetadata dockerConfigurationMetadata = (environment == Environment.SYSTEM)
? DockerConfigurationMetadata.systemEnvironmentConfigurationMetadata : null;
if (dockerConfigurationMetadata != null) {
return dockerConfigurationMetadata;
if (environment == Environment.SYSTEM) {
return systemEnvironmentConfigurationMetadata.get();
}
return create(environment);
}
private static DockerConfigurationMetadata create(Environment environment) {
String configLocation = environment.get(DOCKER_CONFIG);
configLocation = (configLocation != null) ? configLocation : getUserHomeConfigLocation();
DockerConfig dockerConfig = createDockerConfig(configLocation);
DockerContext dockerContext = createDockerContext(configLocation, dockerConfig.getCurrentContext());
dockerConfigurationMetadata = new DockerConfigurationMetadata(configLocation, dockerConfig, dockerContext);
if (environment == Environment.SYSTEM) {
DockerConfigurationMetadata.systemEnvironmentConfigurationMetadata = dockerConfigurationMetadata;
}
return dockerConfigurationMetadata;
return new DockerConfigurationMetadata(configLocation, dockerConfig, dockerContext);
}
private static String getUserHomeConfigLocation() {

View File

@ -19,6 +19,7 @@ package org.springframework.boot.buildpack.platform.docker.configuration;
import java.util.function.BiConsumer;
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
import org.springframework.util.Assert;
/**
* Docker registry authentication configuration.
@ -83,7 +84,8 @@ public interface DockerRegistryAuthentication {
* Factory method that returns a new {@link DockerRegistryAuthentication} instance
* that uses the standard docker JSON config (including support for credential
* helpers) to generate auth headers.
* @param fallback the fallback authentication to use if no suitable config is found
* @param fallback the fallback authentication to use if no suitable config is found,
* may be null {@code}
* @return a new {@link DockerRegistryAuthentication} instance
* @since 3.5.0
* @see #configuration(DockerRegistryAuthentication, BiConsumer)
@ -96,15 +98,17 @@ public interface DockerRegistryAuthentication {
* Factory method that returns a new {@link DockerRegistryAuthentication} instance
* that uses the standard docker JSON config (including support for credential
* helpers) to generate auth headers.
* @param fallback the fallback authentication to use if no suitable config is found
* @param fallback the fallback authentication to use if no suitable config is found,
* may be {@code null}
* @param credentialHelperExceptionHandler callback that should handle credential
* helper exceptions
* helper exceptions, never {@code null}
* @return a new {@link DockerRegistryAuthentication} instance
* @since 3.5.0
* @see #configuration(DockerRegistryAuthentication, BiConsumer)
*/
static DockerRegistryAuthentication configuration(DockerRegistryAuthentication fallback,
BiConsumer<String, Exception> credentialHelperExceptionHandler) {
Assert.notNull(credentialHelperExceptionHandler, () -> "'credentialHelperExceptionHandler' must not be null");
return new DockerRegistryConfigAuthentication(fallback, credentialHelperExceptionHandler);
}

View File

@ -89,10 +89,17 @@ class CredentialHelperTests {
}
@Test
void getWhenCommandDoesNotExistErrorThrowsException() {
String name = "docker-credential-%s".formatted(UUID.randomUUID().toString());
assertThatIOException().isThrownBy(() -> new CredentialHelper(name).get("invalid.example.com"))
.withMessageContaining(name);
void getWhenExecutableDoesNotExistErrorThrowsException() {
String executable = "docker-credential-%s".formatted(UUID.randomUUID().toString());
assertThatIOException().isThrownBy(() -> new CredentialHelper(executable).get("invalid.example.com"))
.withMessageContaining(executable)
.satisfies((ex) -> {
if (Platform.isMac()) {
assertThat(ex.getMessage()).doesNotContain("/usr/local/bin/");
assertThat(ex.getSuppressed()).allSatisfy((suppressed) -> assertThat(suppressed)
.hasMessageContaining("/usr/local/bin/" + executable));
}
});
}
}

View File

@ -32,7 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Dmytro Nosan
*/
class CredentialsTests {
class CredentialTests {
@Test
@WithResource(name = "credentials.json", content = """