mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-17 12:09:16 +00:00
Add nullability annotations to buildpack/spring-boot-buildpack-platform
See gh-46587
This commit is contained in:
+4
-2
@@ -19,6 +19,8 @@ package org.springframework.boot.buildpack.platform.build;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
@@ -45,7 +47,7 @@ public abstract class AbstractBuildLog implements BuildLog {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Consumer<TotalProgressEvent> pullingImage(ImageReference imageReference, ImagePlatform platform,
|
||||
public Consumer<TotalProgressEvent> pullingImage(ImageReference imageReference, @Nullable ImagePlatform platform,
|
||||
ImageType imageType) {
|
||||
return (platform != null)
|
||||
? getProgressConsumer(" > Pulling %s '%s' for platform '%s'".formatted(imageType.getDescription(),
|
||||
@@ -109,7 +111,7 @@ public abstract class AbstractBuildLog implements BuildLog {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failedCleaningWorkDir(Cache cache, Exception exception) {
|
||||
public void failedCleaningWorkDir(Cache cache, @Nullable Exception exception) {
|
||||
StringBuilder message = new StringBuilder("Warning: Working location " + cache + " could not be cleaned");
|
||||
if (exception != null) {
|
||||
message.append(": ").append(exception.getMessage());
|
||||
|
||||
+3
-1
@@ -19,6 +19,8 @@ package org.springframework.boot.buildpack.platform.build;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ApiVersion;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -62,7 +64,7 @@ final class ApiVersions {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+4
-2
@@ -19,6 +19,8 @@ package org.springframework.boot.buildpack.platform.build;
|
||||
import java.io.PrintStream;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
@@ -52,7 +54,7 @@ public interface BuildLog {
|
||||
* @param imageType the image type
|
||||
* @return a consumer for progress update events
|
||||
*/
|
||||
Consumer<TotalProgressEvent> pullingImage(ImageReference imageReference, ImagePlatform platform,
|
||||
Consumer<TotalProgressEvent> pullingImage(ImageReference imageReference, @Nullable ImagePlatform platform,
|
||||
ImageType imageType);
|
||||
|
||||
/**
|
||||
@@ -124,7 +126,7 @@ public interface BuildLog {
|
||||
* @param exception any exception that caused the failure
|
||||
* @since 3.2.6
|
||||
*/
|
||||
void failedCleaningWorkDir(Cache cache, Exception exception);
|
||||
void failedCleaningWorkDir(Cache cache, @Nullable Exception exception);
|
||||
|
||||
/**
|
||||
* Log that a binding with a sensitive target has been detected.
|
||||
|
||||
+27
-24
@@ -26,6 +26,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
@@ -69,9 +71,9 @@ public class BuildRequest {
|
||||
|
||||
private final ImageReference builder;
|
||||
|
||||
private final Boolean trustBuilder;
|
||||
private final @Nullable Boolean trustBuilder;
|
||||
|
||||
private final ImageReference runImage;
|
||||
private final @Nullable ImageReference runImage;
|
||||
|
||||
private final Creator creator;
|
||||
|
||||
@@ -89,23 +91,23 @@ public class BuildRequest {
|
||||
|
||||
private final List<Binding> bindings;
|
||||
|
||||
private final String network;
|
||||
private final @Nullable String network;
|
||||
|
||||
private final List<ImageReference> tags;
|
||||
|
||||
private final Cache buildWorkspace;
|
||||
private final @Nullable Cache buildWorkspace;
|
||||
|
||||
private final Cache buildCache;
|
||||
private final @Nullable Cache buildCache;
|
||||
|
||||
private final Cache launchCache;
|
||||
private final @Nullable Cache launchCache;
|
||||
|
||||
private final Instant createdDate;
|
||||
private final @Nullable Instant createdDate;
|
||||
|
||||
private final String applicationDirectory;
|
||||
private final @Nullable String applicationDirectory;
|
||||
|
||||
private final List<String> securityOptions;
|
||||
private final @Nullable List<String> securityOptions;
|
||||
|
||||
private final ImagePlatform platform;
|
||||
private final @Nullable ImagePlatform platform;
|
||||
|
||||
BuildRequest(ImageReference name, Function<Owner, TarArchive> applicationContent) {
|
||||
Assert.notNull(name, "'name' must not be null");
|
||||
@@ -135,11 +137,12 @@ public class BuildRequest {
|
||||
}
|
||||
|
||||
BuildRequest(ImageReference name, Function<Owner, TarArchive> applicationContent, ImageReference builder,
|
||||
Boolean trustBuilder, ImageReference runImage, Creator creator, Map<String, String> env, boolean cleanCache,
|
||||
boolean verboseLogging, PullPolicy pullPolicy, boolean publish, List<BuildpackReference> buildpacks,
|
||||
List<Binding> bindings, String network, List<ImageReference> tags, Cache buildWorkspace, Cache buildCache,
|
||||
Cache launchCache, Instant createdDate, String applicationDirectory, List<String> securityOptions,
|
||||
ImagePlatform platform) {
|
||||
@Nullable Boolean trustBuilder, @Nullable ImageReference runImage, Creator creator, Map<String, String> env,
|
||||
boolean cleanCache, boolean verboseLogging, PullPolicy pullPolicy, boolean publish,
|
||||
List<BuildpackReference> buildpacks, List<Binding> bindings, @Nullable String network,
|
||||
List<ImageReference> tags, @Nullable Cache buildWorkspace, @Nullable Cache buildCache,
|
||||
@Nullable Cache launchCache, @Nullable Instant createdDate, @Nullable String applicationDirectory,
|
||||
@Nullable List<String> securityOptions, @Nullable ImagePlatform platform) {
|
||||
this.name = name;
|
||||
this.applicationContent = applicationContent;
|
||||
this.builder = builder;
|
||||
@@ -538,7 +541,7 @@ public class BuildRequest {
|
||||
* Return the run image that should be used, if provided.
|
||||
* @return the run image
|
||||
*/
|
||||
public ImageReference getRunImage() {
|
||||
public @Nullable ImageReference getRunImage() {
|
||||
return this.runImage;
|
||||
}
|
||||
|
||||
@@ -612,7 +615,7 @@ public class BuildRequest {
|
||||
* @return the network
|
||||
* @since 2.6.0
|
||||
*/
|
||||
public String getNetwork() {
|
||||
public @Nullable String getNetwork() {
|
||||
return this.network;
|
||||
}
|
||||
|
||||
@@ -629,7 +632,7 @@ public class BuildRequest {
|
||||
* @return the build workspace or {@code null}
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public Cache getBuildWorkspace() {
|
||||
public @Nullable Cache getBuildWorkspace() {
|
||||
return this.buildWorkspace;
|
||||
}
|
||||
|
||||
@@ -637,7 +640,7 @@ public class BuildRequest {
|
||||
* Return the custom build cache that should be used by the lifecycle.
|
||||
* @return the build cache
|
||||
*/
|
||||
public Cache getBuildCache() {
|
||||
public @Nullable Cache getBuildCache() {
|
||||
return this.buildCache;
|
||||
}
|
||||
|
||||
@@ -645,7 +648,7 @@ public class BuildRequest {
|
||||
* Return the custom launch cache that should be used by the lifecycle.
|
||||
* @return the launch cache
|
||||
*/
|
||||
public Cache getLaunchCache() {
|
||||
public @Nullable Cache getLaunchCache() {
|
||||
return this.launchCache;
|
||||
}
|
||||
|
||||
@@ -653,7 +656,7 @@ public class BuildRequest {
|
||||
* Return the custom created date that should be used by the lifecycle.
|
||||
* @return the created date
|
||||
*/
|
||||
public Instant getCreatedDate() {
|
||||
public @Nullable Instant getCreatedDate() {
|
||||
return this.createdDate;
|
||||
}
|
||||
|
||||
@@ -661,7 +664,7 @@ public class BuildRequest {
|
||||
* Return the application directory that should be used by the lifecycle.
|
||||
* @return the application directory
|
||||
*/
|
||||
public String getApplicationDirectory() {
|
||||
public @Nullable String getApplicationDirectory() {
|
||||
return this.applicationDirectory;
|
||||
}
|
||||
|
||||
@@ -670,7 +673,7 @@ public class BuildRequest {
|
||||
* @return the security options or {@code null}
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public List<String> getSecurityOptions() {
|
||||
public @Nullable List<String> getSecurityOptions() {
|
||||
return this.securityOptions;
|
||||
}
|
||||
|
||||
@@ -679,7 +682,7 @@ public class BuildRequest {
|
||||
* @return the platform or {@code null}
|
||||
* @since 3.4.0
|
||||
*/
|
||||
public ImagePlatform getImagePlatform() {
|
||||
public @Nullable ImagePlatform getImagePlatform() {
|
||||
return this.platform;
|
||||
}
|
||||
|
||||
|
||||
+12
-8
@@ -20,6 +20,8 @@ import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerLog;
|
||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
||||
@@ -86,12 +88,12 @@ public class Builder {
|
||||
* @param dockerConfiguration the docker configuration
|
||||
* @since 3.5.0
|
||||
*/
|
||||
public Builder(BuildLog log, BuilderDockerConfiguration dockerConfiguration) {
|
||||
public Builder(BuildLog log, @Nullable BuilderDockerConfiguration dockerConfiguration) {
|
||||
this(log, new DockerApi((dockerConfiguration != null) ? dockerConfiguration.connection() : null,
|
||||
BuildLogAdapter.get(log)), dockerConfiguration);
|
||||
}
|
||||
|
||||
Builder(BuildLog log, DockerApi docker, BuilderDockerConfiguration dockerConfiguration) {
|
||||
Builder(BuildLog log, DockerApi docker, @Nullable BuilderDockerConfiguration dockerConfiguration) {
|
||||
Assert.notNull(log, "'log' must not be null");
|
||||
this.log = log;
|
||||
this.docker = docker;
|
||||
@@ -109,6 +111,7 @@ public class Builder {
|
||||
Image builderImage = imageFetcher.fetchImage(ImageType.BUILDER, request.getBuilder());
|
||||
BuilderMetadata builderMetadata = BuilderMetadata.fromImage(builderImage);
|
||||
request = withRunImageIfNeeded(request, builderMetadata);
|
||||
Assert.state(request.getRunImage() != null, "'request.getRunImage()' must not be null");
|
||||
Image runImage = imageFetcher.fetchImage(ImageType.RUNNER, request.getRunImage());
|
||||
assertStackIdsMatch(runImage, builderImage);
|
||||
BuildOwner buildOwner = BuildOwner.fromEnv(builderImage.getConfig().getEnv());
|
||||
@@ -181,7 +184,7 @@ public class Builder {
|
||||
}
|
||||
}
|
||||
|
||||
private ResolvedDockerHost getDockerHost() {
|
||||
private @Nullable ResolvedDockerHost getDockerHost() {
|
||||
boolean bindToBuilder = this.dockerConfiguration.bindHostToBuilder();
|
||||
return (bindToBuilder) ? ResolvedDockerHost.from(this.dockerConfiguration.connection()) : null;
|
||||
}
|
||||
@@ -208,7 +211,8 @@ public class Builder {
|
||||
this.log.pushedImage(reference);
|
||||
}
|
||||
|
||||
private static String authHeader(DockerRegistryAuthentication authentication, ImageReference reference) {
|
||||
private static @Nullable String authHeader(@Nullable DockerRegistryAuthentication authentication,
|
||||
ImageReference reference) {
|
||||
return (authentication != null) ? authentication.getAuthHeader(reference) : null;
|
||||
}
|
||||
|
||||
@@ -217,14 +221,14 @@ public class Builder {
|
||||
*/
|
||||
private class ImageFetcher {
|
||||
|
||||
private final DockerRegistryAuthentication registryAuthentication;
|
||||
private final @Nullable DockerRegistryAuthentication registryAuthentication;
|
||||
|
||||
private final PullPolicy pullPolicy;
|
||||
|
||||
private ImagePlatform defaultPlatform;
|
||||
private @Nullable ImagePlatform defaultPlatform;
|
||||
|
||||
ImageFetcher(DockerRegistryAuthentication registryAuthentication, PullPolicy pullPolicy,
|
||||
ImagePlatform platform) {
|
||||
ImageFetcher(@Nullable DockerRegistryAuthentication registryAuthentication, PullPolicy pullPolicy,
|
||||
@Nullable ImagePlatform platform) {
|
||||
this.registryAuthentication = registryAuthentication;
|
||||
this.pullPolicy = pullPolicy;
|
||||
this.defaultPlatform = platform;
|
||||
|
||||
+13
-5
@@ -18,6 +18,8 @@ package org.springframework.boot.buildpack.platform.build;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Layer;
|
||||
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -59,10 +61,10 @@ class BuilderBuildpack implements Buildpack {
|
||||
* @param reference the buildpack reference
|
||||
* @return the resolved {@link Buildpack} or {@code null}
|
||||
*/
|
||||
static Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference) {
|
||||
static @Nullable Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference) {
|
||||
boolean unambiguous = reference.hasPrefix(PREFIX);
|
||||
BuilderReference builderReference = BuilderReference
|
||||
.of(unambiguous ? reference.getSubReference(PREFIX) : reference.toString());
|
||||
.of(unambiguous ? getSubReference(reference) : reference.toString());
|
||||
BuildpackMetadata buildpackMetadata = findBuildpackMetadata(context, builderReference);
|
||||
if (unambiguous) {
|
||||
Assert.state(buildpackMetadata != null, () -> "Buildpack '" + reference + "' not found in builder");
|
||||
@@ -70,7 +72,13 @@ class BuilderBuildpack implements Buildpack {
|
||||
return (buildpackMetadata != null) ? new BuilderBuildpack(buildpackMetadata) : null;
|
||||
}
|
||||
|
||||
private static BuildpackMetadata findBuildpackMetadata(BuildpackResolverContext context,
|
||||
private static String getSubReference(BuildpackReference reference) {
|
||||
String result = reference.getSubReference(PREFIX);
|
||||
Assert.state(result != null, "'result' must not be null");
|
||||
return result;
|
||||
}
|
||||
|
||||
private static @Nullable BuildpackMetadata findBuildpackMetadata(BuildpackResolverContext context,
|
||||
BuilderReference builderReference) {
|
||||
for (BuildpackMetadata candidate : context.getBuildpackMetadata()) {
|
||||
if (builderReference.matches(candidate)) {
|
||||
@@ -87,9 +95,9 @@ class BuilderBuildpack implements Buildpack {
|
||||
|
||||
private final String id;
|
||||
|
||||
private final String version;
|
||||
private final @Nullable String version;
|
||||
|
||||
BuilderReference(String id, String version) {
|
||||
BuilderReference(String id, @Nullable String version) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
+5
-3
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.boot.buildpack.platform.build;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConnectionConfiguration;
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryAuthentication;
|
||||
|
||||
@@ -32,9 +34,9 @@ import org.springframework.boot.buildpack.platform.docker.configuration.DockerRe
|
||||
* @author Scott Frederick
|
||||
* @since 3.5.0
|
||||
*/
|
||||
public record BuilderDockerConfiguration(DockerConnectionConfiguration connection, boolean bindHostToBuilder,
|
||||
DockerRegistryAuthentication builderRegistryAuthentication,
|
||||
DockerRegistryAuthentication publishRegistryAuthentication) {
|
||||
public record BuilderDockerConfiguration(@Nullable DockerConnectionConfiguration connection, boolean bindHostToBuilder,
|
||||
@Nullable DockerRegistryAuthentication builderRegistryAuthentication,
|
||||
@Nullable DockerRegistryAuthentication publishRegistryAuthentication) {
|
||||
|
||||
public BuilderDockerConfiguration() {
|
||||
this(null, false, null, null);
|
||||
|
||||
+31
-6
@@ -26,6 +26,7 @@ import java.util.function.Consumer;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageConfig;
|
||||
@@ -59,13 +60,31 @@ class BuilderMetadata extends MappedObject {
|
||||
|
||||
BuilderMetadata(JsonNode node) {
|
||||
super(node, MethodHandles.lookup());
|
||||
this.stack = valueAt("/stack", Stack.class);
|
||||
this.stack = extractStack();
|
||||
this.runImages = childrenAt("/images", RunImage::new);
|
||||
this.lifecycle = valueAt("/lifecycle", Lifecycle.class);
|
||||
this.createdBy = valueAt("/createdBy", CreatedBy.class);
|
||||
this.lifecycle = extractLifecycle();
|
||||
this.createdBy = extractCreatedBy();
|
||||
this.buildpacks = extractBuildpacks(getNode().at("/buildpacks"));
|
||||
}
|
||||
|
||||
private CreatedBy extractCreatedBy() {
|
||||
CreatedBy result = valueAt("/createdBy", CreatedBy.class);
|
||||
Assert.state(result != null, "'result' must not be null");
|
||||
return result;
|
||||
}
|
||||
|
||||
private Lifecycle extractLifecycle() {
|
||||
Lifecycle result = valueAt("/lifecycle", Lifecycle.class);
|
||||
Assert.state(result != null, "'result' must not be null");
|
||||
return result;
|
||||
}
|
||||
|
||||
private Stack extractStack() {
|
||||
Stack result = valueAt("/stack", Stack.class);
|
||||
Assert.state(result != null, "'result' must not be null");
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<BuildpackMetadata> extractBuildpacks(JsonNode node) {
|
||||
if (node.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
@@ -219,10 +238,16 @@ class BuilderMetadata extends MappedObject {
|
||||
*/
|
||||
RunImage(JsonNode node) {
|
||||
super(node, MethodHandles.lookup());
|
||||
this.image = valueAt("/image", String.class);
|
||||
this.image = extractImage();
|
||||
this.mirrors = childrenAt("/mirrors", JsonNode::asText);
|
||||
}
|
||||
|
||||
private String extractImage() {
|
||||
String result = valueAt("/image", String.class);
|
||||
Assert.state(result != null, "'result' must not be null");
|
||||
return result;
|
||||
}
|
||||
|
||||
String getImage() {
|
||||
return this.image;
|
||||
}
|
||||
@@ -284,7 +309,7 @@ class BuilderMetadata extends MappedObject {
|
||||
* Return the supported buildpack API versions.
|
||||
* @return the buildpack versions
|
||||
*/
|
||||
default String[] getBuildpack() {
|
||||
default String @Nullable [] getBuildpack() {
|
||||
return valueAt(this, "/buildpack/supported", String[].class);
|
||||
}
|
||||
|
||||
@@ -292,7 +317,7 @@ class BuilderMetadata extends MappedObject {
|
||||
* Return the supported platform API versions.
|
||||
* @return the platform versions
|
||||
*/
|
||||
default String[] getPlatform() {
|
||||
default String @Nullable [] getPlatform() {
|
||||
return valueAt(this, "/platform/supported", String[].class);
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -20,6 +20,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.tomlj.Toml;
|
||||
import org.tomlj.TomlParseResult;
|
||||
|
||||
@@ -39,9 +40,9 @@ final class BuildpackCoordinates {
|
||||
|
||||
private final String id;
|
||||
|
||||
private final String version;
|
||||
private final @Nullable String version;
|
||||
|
||||
private BuildpackCoordinates(String id, String version) {
|
||||
private BuildpackCoordinates(String id, @Nullable String version) {
|
||||
Assert.hasText(id, "'id' must not be empty");
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
@@ -59,12 +60,12 @@ final class BuildpackCoordinates {
|
||||
return this.id.replace("/", "_");
|
||||
}
|
||||
|
||||
String getVersion() {
|
||||
@Nullable String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+10
-9
@@ -22,6 +22,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageConfig;
|
||||
@@ -53,7 +54,7 @@ final class BuildpackLayersMetadata extends MappedObject {
|
||||
* @return the buildpack details or {@code null} if a buildpack with the given ID and
|
||||
* version does not exist in the metadata
|
||||
*/
|
||||
BuildpackLayerDetails getBuildpack(String id, String version) {
|
||||
@Nullable BuildpackLayerDetails getBuildpack(String id, @Nullable String version) {
|
||||
return this.buildpacks.getBuildpack(id, version);
|
||||
}
|
||||
|
||||
@@ -105,7 +106,7 @@ final class BuildpackLayersMetadata extends MappedObject {
|
||||
|
||||
private final Map<String, BuildpackVersions> buildpacks = new HashMap<>();
|
||||
|
||||
private BuildpackLayerDetails getBuildpack(String id, String version) {
|
||||
private @Nullable BuildpackLayerDetails getBuildpack(String id, @Nullable String version) {
|
||||
if (this.buildpacks.containsKey(id)) {
|
||||
return this.buildpacks.get(id).getBuildpack(version);
|
||||
}
|
||||
@@ -130,7 +131,7 @@ final class BuildpackLayersMetadata extends MappedObject {
|
||||
|
||||
private final Map<String, BuildpackLayerDetails> versions = new HashMap<>();
|
||||
|
||||
private BuildpackLayerDetails getBuildpack(String version) {
|
||||
private @Nullable BuildpackLayerDetails getBuildpack(@Nullable String version) {
|
||||
return this.versions.get(version);
|
||||
}
|
||||
|
||||
@@ -150,11 +151,11 @@ final class BuildpackLayersMetadata extends MappedObject {
|
||||
|
||||
static final class BuildpackLayerDetails extends MappedObject {
|
||||
|
||||
private final String name;
|
||||
private final @Nullable String name;
|
||||
|
||||
private final String homepage;
|
||||
private final @Nullable String homepage;
|
||||
|
||||
private final String layerDiffId;
|
||||
private final @Nullable String layerDiffId;
|
||||
|
||||
private BuildpackLayerDetails(JsonNode node) {
|
||||
super(node, MethodHandles.lookup());
|
||||
@@ -167,7 +168,7 @@ final class BuildpackLayersMetadata extends MappedObject {
|
||||
* Return the buildpack name.
|
||||
* @return the name
|
||||
*/
|
||||
String getName() {
|
||||
@Nullable String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@@ -175,7 +176,7 @@ final class BuildpackLayersMetadata extends MappedObject {
|
||||
* Return the buildpack homepage address.
|
||||
* @return the homepage address
|
||||
*/
|
||||
String getHomepage() {
|
||||
@Nullable String getHomepage() {
|
||||
return this.homepage;
|
||||
}
|
||||
|
||||
@@ -183,7 +184,7 @@ final class BuildpackLayersMetadata extends MappedObject {
|
||||
* Return the buildpack layer {@code diffID}.
|
||||
* @return the layer {@code diffID}
|
||||
*/
|
||||
String getLayerDiffId() {
|
||||
@Nullable String getLayerDiffId() {
|
||||
return this.layerDiffId;
|
||||
}
|
||||
|
||||
|
||||
+12
-5
@@ -20,6 +20,7 @@ import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageConfig;
|
||||
@@ -39,17 +40,23 @@ final class BuildpackMetadata extends MappedObject {
|
||||
|
||||
private final String id;
|
||||
|
||||
private final String version;
|
||||
private final @Nullable String version;
|
||||
|
||||
private final String homepage;
|
||||
private final @Nullable String homepage;
|
||||
|
||||
private BuildpackMetadata(JsonNode node) {
|
||||
super(node, MethodHandles.lookup());
|
||||
this.id = valueAt("/id", String.class);
|
||||
this.id = extractId();
|
||||
this.version = valueAt("/version", String.class);
|
||||
this.homepage = valueAt("/homepage", String.class);
|
||||
}
|
||||
|
||||
private String extractId() {
|
||||
String result = valueAt("/id", String.class);
|
||||
Assert.state(result != null, "'result' must not be null");
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the buildpack ID.
|
||||
* @return the ID
|
||||
@@ -62,7 +69,7 @@ final class BuildpackMetadata extends MappedObject {
|
||||
* Return the buildpack version.
|
||||
* @return the version
|
||||
*/
|
||||
String getVersion() {
|
||||
@Nullable String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
@@ -70,7 +77,7 @@ final class BuildpackMetadata extends MappedObject {
|
||||
* Return the buildpack homepage address.
|
||||
* @return the homepage
|
||||
*/
|
||||
String getHomepage() {
|
||||
@Nullable String getHomepage() {
|
||||
return this.homepage;
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -22,6 +22,8 @@ import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -44,11 +46,11 @@ public final class BuildpackReference {
|
||||
return this.value.startsWith(prefix);
|
||||
}
|
||||
|
||||
String getSubReference(String prefix) {
|
||||
@Nullable String getSubReference(String prefix) {
|
||||
return this.value.startsWith(prefix) ? this.value.substring(prefix.length()) : null;
|
||||
}
|
||||
|
||||
Path asPath() {
|
||||
@Nullable Path asPath() {
|
||||
try {
|
||||
URL url = new URL(this.value);
|
||||
if (url.getProtocol().equals("file")) {
|
||||
@@ -68,7 +70,7 @@ public final class BuildpackReference {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+3
-1
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.boot.buildpack.platform.build;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Strategy interface used to resolve a {@link BuildpackReference} to a {@link Buildpack}.
|
||||
*
|
||||
@@ -31,6 +33,6 @@ interface BuildpackResolver {
|
||||
* @param reference the reference to resolve
|
||||
* @return a resolved {@link Buildpack} instance or {@code null}
|
||||
*/
|
||||
Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference);
|
||||
@Nullable Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference);
|
||||
|
||||
}
|
||||
|
||||
+7
-5
@@ -18,6 +18,8 @@ package org.springframework.boot.buildpack.platform.build;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -67,7 +69,7 @@ public class Cache {
|
||||
* Return the details of the cache if it is a volume cache.
|
||||
* @return the cache, or {@code null} if it is not a volume cache
|
||||
*/
|
||||
public Volume getVolume() {
|
||||
public @Nullable Volume getVolume() {
|
||||
return (this.format.equals(Format.VOLUME)) ? (Volume) this : null;
|
||||
}
|
||||
|
||||
@@ -75,7 +77,7 @@ public class Cache {
|
||||
* Return the details of the cache if it is a bind cache.
|
||||
* @return the cache, or {@code null} if it is not a bind cache
|
||||
*/
|
||||
public Bind getBind() {
|
||||
public @Nullable Bind getBind() {
|
||||
return (this.format.equals(Format.BIND)) ? (Bind) this : null;
|
||||
}
|
||||
|
||||
@@ -110,7 +112,7 @@ public class Cache {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
@@ -147,7 +149,7 @@ public class Cache {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
@@ -192,7 +194,7 @@ public class Cache {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+3
-1
@@ -25,6 +25,8 @@ import java.nio.file.Paths;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Layer;
|
||||
import org.springframework.boot.buildpack.platform.io.Content;
|
||||
import org.springframework.boot.buildpack.platform.io.FilePermissions;
|
||||
@@ -99,7 +101,7 @@ final class DirectoryBuildpack implements Buildpack {
|
||||
* @param reference the buildpack reference
|
||||
* @return the resolved {@link Buildpack} or {@code null}
|
||||
*/
|
||||
static Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference) {
|
||||
static @Nullable Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference) {
|
||||
Path path = reference.asPath();
|
||||
if (path != null && Files.exists(path) && Files.isDirectory(path)) {
|
||||
return new DirectoryBuildpack(path);
|
||||
|
||||
+4
-1
@@ -19,6 +19,8 @@ package org.springframework.boot.buildpack.platform.build;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive.Update;
|
||||
@@ -63,7 +65,8 @@ class EphemeralBuilder {
|
||||
* @param buildpacks an optional set of buildpacks to apply
|
||||
*/
|
||||
EphemeralBuilder(BuildOwner buildOwner, Image builderImage, ImageReference targetImage,
|
||||
BuilderMetadata builderMetadata, Creator creator, Map<String, String> env, Buildpacks buildpacks) {
|
||||
BuilderMetadata builderMetadata, Creator creator, Map<String, String> env,
|
||||
@Nullable Buildpacks buildpacks) {
|
||||
this.name = ImageReference.random("pack.local/builder/").inTaggedForm();
|
||||
this.buildOwner = buildOwner;
|
||||
this.creator = creator;
|
||||
|
||||
+13
-4
@@ -28,6 +28,7 @@ import java.util.List;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.build.BuildpackLayersMetadata.BuildpackLayerDetails;
|
||||
import org.springframework.boot.buildpack.platform.docker.transport.DockerEngineException;
|
||||
@@ -37,6 +38,7 @@ import org.springframework.boot.buildpack.platform.docker.type.Layer;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.LayerId;
|
||||
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
@@ -54,7 +56,7 @@ final class ImageBuildpack implements Buildpack {
|
||||
|
||||
private final BuildpackCoordinates coordinates;
|
||||
|
||||
private final ExportedLayers exportedLayers;
|
||||
private final @Nullable ExportedLayers exportedLayers;
|
||||
|
||||
private ImageBuildpack(BuildpackResolverContext context, ImageReference imageReference) {
|
||||
ImageReference reference = imageReference.inTaggedOrDigestForm();
|
||||
@@ -95,11 +97,18 @@ final class ImageBuildpack implements Buildpack {
|
||||
* @param reference the buildpack reference
|
||||
* @return the resolved {@link Buildpack} or {@code null}
|
||||
*/
|
||||
static Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference) {
|
||||
static @Nullable Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference) {
|
||||
boolean unambiguous = reference.hasPrefix(PREFIX);
|
||||
try {
|
||||
ImageReference imageReference = ImageReference
|
||||
.of((unambiguous) ? reference.getSubReference(PREFIX) : reference.toString());
|
||||
ImageReference imageReference;
|
||||
if (unambiguous) {
|
||||
String subReference = reference.getSubReference(PREFIX);
|
||||
Assert.state(subReference != null, "'subReference' must not be null");
|
||||
imageReference = ImageReference.of(subReference);
|
||||
}
|
||||
else {
|
||||
imageReference = ImageReference.of(reference.toString());
|
||||
}
|
||||
return new ImageBuildpack(context, imageReference);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
|
||||
+26
-8
@@ -25,7 +25,9 @@ import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.sun.jna.Platform;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.build.Cache.Bind;
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost;
|
||||
@@ -66,7 +68,7 @@ class Lifecycle implements Closeable {
|
||||
|
||||
private final DockerApi docker;
|
||||
|
||||
private final ResolvedDockerHost dockerHost;
|
||||
private final @Nullable ResolvedDockerHost dockerHost;
|
||||
|
||||
private final BuildRequest request;
|
||||
|
||||
@@ -100,7 +102,7 @@ class Lifecycle implements Closeable {
|
||||
* @param request the request to process
|
||||
* @param builder the ephemeral builder used to run the phases
|
||||
*/
|
||||
Lifecycle(BuildLog log, DockerApi docker, ResolvedDockerHost dockerHost, BuildRequest request,
|
||||
Lifecycle(BuildLog log, DockerApi docker, @Nullable ResolvedDockerHost dockerHost, BuildRequest request,
|
||||
EphemeralBuilder builder) {
|
||||
this.log = log;
|
||||
this.docker = docker;
|
||||
@@ -189,7 +191,9 @@ class Lifecycle implements Closeable {
|
||||
phase.withApp(this.applicationDirectory,
|
||||
Binding.from(getCacheBindingSource(this.application), this.applicationDirectory));
|
||||
phase.withPlatform(Directory.PLATFORM);
|
||||
phase.withRunImage(this.request.getRunImage());
|
||||
ImageReference runImage = this.request.getRunImage();
|
||||
Assert.state(runImage != null, "'runImage' must not be null");
|
||||
phase.withRunImage(runImage);
|
||||
phase.withLayers(Directory.LAYERS, Binding.from(getCacheBindingSource(this.layers), Directory.LAYERS));
|
||||
phase.withBuildCache(Directory.CACHE, Binding.from(getCacheBindingSource(this.buildCache), Directory.CACHE));
|
||||
phase.withLaunchCache(Directory.LAUNCH_CACHE,
|
||||
@@ -214,7 +218,9 @@ class Lifecycle implements Closeable {
|
||||
phase.withLaunchCache(Directory.LAUNCH_CACHE,
|
||||
Binding.from(getCacheBindingSource(this.launchCache), Directory.LAUNCH_CACHE));
|
||||
phase.withLayers(Directory.LAYERS, Binding.from(getCacheBindingSource(this.layers), Directory.LAYERS));
|
||||
phase.withRunImage(this.request.getRunImage());
|
||||
ImageReference runImage = this.request.getRunImage();
|
||||
Assert.state(runImage != null, "'runImage' must not be null");
|
||||
phase.withRunImage(runImage);
|
||||
phase.withImageName(this.request.getName());
|
||||
configureOptions(phase);
|
||||
return phase;
|
||||
@@ -282,12 +288,22 @@ class Lifecycle implements Closeable {
|
||||
}
|
||||
|
||||
private Cache getBuildWorkspaceBindingSource(Cache buildWorkspace, String suffix) {
|
||||
return (buildWorkspace.getVolume() != null) ? Cache.volume(buildWorkspace.getVolume().getName() + "-" + suffix)
|
||||
: Cache.bind(buildWorkspace.getBind().getSource() + "-" + suffix);
|
||||
if (buildWorkspace.getVolume() != null) {
|
||||
return Cache.volume(buildWorkspace.getVolume().getName() + "-" + suffix);
|
||||
}
|
||||
|
||||
Bind bind = buildWorkspace.getBind();
|
||||
Assert.state(bind != null, "'bind' must not be null");
|
||||
return Cache.bind(bind.getSource() + "-" + suffix);
|
||||
}
|
||||
|
||||
private String getCacheBindingSource(Cache cache) {
|
||||
return (cache.getVolume() != null) ? cache.getVolume().getName() : cache.getBind().getSource();
|
||||
if (cache.getVolume() != null) {
|
||||
return cache.getVolume().getName();
|
||||
}
|
||||
Bind bind = cache.getBind();
|
||||
Assert.state(bind != null, "'bind' must not be null");
|
||||
return bind.getSource();
|
||||
}
|
||||
|
||||
private Cache createVolumeCache(String prefix) {
|
||||
@@ -310,7 +326,9 @@ class Lifecycle implements Closeable {
|
||||
phase.withEnv("DOCKER_HOST", this.dockerHost.getAddress());
|
||||
if (this.dockerHost.isSecure()) {
|
||||
phase.withEnv("DOCKER_TLS_VERIFY", "1");
|
||||
phase.withEnv("DOCKER_CERT_PATH", this.dockerHost.getCertificatePath());
|
||||
if (this.dockerHost.getCertificatePath() != null) {
|
||||
phase.withEnv("DOCKER_CERT_PATH", this.dockerHost.getCertificatePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
+3
-1
@@ -18,6 +18,8 @@ package org.springframework.boot.buildpack.platform.build;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -44,7 +46,7 @@ class LifecycleVersion implements Comparable<LifecycleVersion> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+4
-2
@@ -22,6 +22,8 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
@@ -48,7 +50,7 @@ class Phase {
|
||||
|
||||
private final List<String> securityOptions = new ArrayList<>();
|
||||
|
||||
private String networkMode;
|
||||
private @Nullable String networkMode;
|
||||
|
||||
private boolean requiresApp;
|
||||
|
||||
@@ -152,7 +154,7 @@ class Phase {
|
||||
* Update this phase with the network the build container will connect to.
|
||||
* @param networkMode the network
|
||||
*/
|
||||
void withNetworkMode(String networkMode) {
|
||||
void withNetworkMode(@Nullable String networkMode) {
|
||||
this.networkMode = networkMode;
|
||||
}
|
||||
|
||||
|
||||
+10
-6
@@ -16,6 +16,10 @@
|
||||
|
||||
package org.springframework.boot.buildpack.platform.build;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageConfig;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -30,21 +34,21 @@ class StackId {
|
||||
|
||||
private static final String LABEL_NAME = "io.buildpacks.stack.id";
|
||||
|
||||
private final String value;
|
||||
private final @Nullable String value;
|
||||
|
||||
StackId(String value) {
|
||||
StackId(@Nullable String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return this.value.equals(((StackId) obj).value);
|
||||
return Objects.equals(this.value, ((StackId) obj).value);
|
||||
}
|
||||
|
||||
boolean hasId() {
|
||||
@@ -53,12 +57,12 @@ class StackId {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.value.hashCode();
|
||||
return Objects.hashCode(this.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.value;
|
||||
return (this.value != null) ? this.value : "<null>";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-1
@@ -27,6 +27,7 @@ import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
||||
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Layer;
|
||||
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
||||
@@ -118,7 +119,7 @@ final class TarGzipBuildpack implements Buildpack {
|
||||
* @param reference the buildpack reference
|
||||
* @return the resolved {@link Buildpack} or {@code null}
|
||||
*/
|
||||
static Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference) {
|
||||
static @Nullable Buildpack resolve(BuildpackResolverContext context, BuildpackReference reference) {
|
||||
Path path = reference.asPath();
|
||||
if (path != null && Files.exists(path) && Files.isRegularFile(path)) {
|
||||
return new TarGzipBuildpack(path);
|
||||
|
||||
+3
@@ -17,4 +17,7 @@
|
||||
/**
|
||||
* Central API for performing a buildpack build.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.buildpack.platform.build;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
+23
-19
@@ -27,7 +27,9 @@ import java.util.Objects;
|
||||
|
||||
import org.apache.hc.core5.http.Header;
|
||||
import org.apache.hc.core5.net.URIBuilder;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.PushImageUpdateEvent.ErrorDetail;
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConnectionConfiguration;
|
||||
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport;
|
||||
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport.Response;
|
||||
@@ -81,7 +83,7 @@ public class DockerApi {
|
||||
|
||||
private final SystemApi system;
|
||||
|
||||
private volatile ApiVersion apiVersion = null;
|
||||
private volatile @Nullable ApiVersion apiVersion;
|
||||
|
||||
/**
|
||||
* Create a new {@link DockerApi} instance.
|
||||
@@ -96,7 +98,7 @@ public class DockerApi {
|
||||
* @param log a logger used to record output
|
||||
* @since 3.5.0
|
||||
*/
|
||||
public DockerApi(DockerConnectionConfiguration connectionConfiguration, DockerLog log) {
|
||||
public DockerApi(@Nullable DockerConnectionConfiguration connectionConfiguration, DockerLog log) {
|
||||
this(HttpTransport.create(connectionConfiguration), log);
|
||||
}
|
||||
|
||||
@@ -125,7 +127,7 @@ public class DockerApi {
|
||||
return this.jsonStream;
|
||||
}
|
||||
|
||||
private URI buildUrl(String path, Collection<?> params) {
|
||||
private URI buildUrl(String path, @Nullable Collection<?> params) {
|
||||
return buildUrl(API_VERSION, path, (params != null) ? params.toArray() : null);
|
||||
}
|
||||
|
||||
@@ -133,7 +135,7 @@ public class DockerApi {
|
||||
return buildUrl(API_VERSION, path, params);
|
||||
}
|
||||
|
||||
private URI buildUrl(ApiVersion apiVersion, String path, Object... params) {
|
||||
private URI buildUrl(ApiVersion apiVersion, String path, Object @Nullable ... params) {
|
||||
verifyApiVersion(apiVersion);
|
||||
try {
|
||||
URIBuilder builder = new URIBuilder("/v" + apiVersion + path);
|
||||
@@ -158,12 +160,10 @@ public class DockerApi {
|
||||
}
|
||||
|
||||
private ApiVersion getApiVersion() {
|
||||
ApiVersion apiVersion = this.apiVersion;
|
||||
if (this.apiVersion == null) {
|
||||
apiVersion = this.system.getApiVersion();
|
||||
this.apiVersion = apiVersion;
|
||||
this.apiVersion = this.system.getApiVersion();
|
||||
}
|
||||
return apiVersion;
|
||||
return this.apiVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,8 +220,8 @@ public class DockerApi {
|
||||
* @return the {@link ImageApi pulled image} instance
|
||||
* @throws IOException on IO error
|
||||
*/
|
||||
public Image pull(ImageReference reference, ImagePlatform platform,
|
||||
UpdateListener<PullImageUpdateEvent> listener, String registryAuth) throws IOException {
|
||||
public Image pull(ImageReference reference, @Nullable ImagePlatform platform,
|
||||
UpdateListener<PullImageUpdateEvent> listener, @Nullable String registryAuth) throws IOException {
|
||||
Assert.notNull(reference, "'reference' must not be null");
|
||||
Assert.notNull(listener, "'listener' must not be null");
|
||||
URI createUri = (platform != null)
|
||||
@@ -250,8 +250,8 @@ public class DockerApi {
|
||||
* @param registryAuth registry authentication credentials
|
||||
* @throws IOException on IO error
|
||||
*/
|
||||
public void push(ImageReference reference, UpdateListener<PushImageUpdateEvent> listener, String registryAuth)
|
||||
throws IOException {
|
||||
public void push(ImageReference reference, UpdateListener<PushImageUpdateEvent> listener,
|
||||
@Nullable String registryAuth) throws IOException {
|
||||
Assert.notNull(reference, "'reference' must not be null");
|
||||
Assert.notNull(listener, "'listener' must not be null");
|
||||
URI pushUri = buildUrl("/images/" + reference + "/push");
|
||||
@@ -375,8 +375,8 @@ public class DockerApi {
|
||||
* @return a {@link ContainerReference} for the newly created container
|
||||
* @throws IOException on IO error
|
||||
*/
|
||||
public ContainerReference create(ContainerConfig config, ImagePlatform platform, ContainerContent... contents)
|
||||
throws IOException {
|
||||
public ContainerReference create(ContainerConfig config, @Nullable ImagePlatform platform,
|
||||
ContainerContent... contents) throws IOException {
|
||||
Assert.notNull(config, "'config' must not be null");
|
||||
Assert.noNullElements(contents, "'contents' must not contain null elements");
|
||||
ContainerReference containerReference = createContainer(config, platform);
|
||||
@@ -386,7 +386,8 @@ public class DockerApi {
|
||||
return containerReference;
|
||||
}
|
||||
|
||||
private ContainerReference createContainer(ContainerConfig config, ImagePlatform platform) throws IOException {
|
||||
private ContainerReference createContainer(ContainerConfig config, @Nullable ImagePlatform platform)
|
||||
throws IOException {
|
||||
URI createUri = (platform != null)
|
||||
? buildUrl(PLATFORM_API_VERSION, "/containers/create", "platform", platform)
|
||||
: buildUrl("/containers/create");
|
||||
@@ -530,7 +531,7 @@ public class DockerApi {
|
||||
|
||||
private static final String PREFIX = "Digest:";
|
||||
|
||||
private String digest;
|
||||
private @Nullable String digest;
|
||||
|
||||
@Override
|
||||
public void onUpdate(ProgressUpdateEvent event) {
|
||||
@@ -551,7 +552,7 @@ public class DockerApi {
|
||||
|
||||
private final ImageArchive archive;
|
||||
|
||||
private String stream;
|
||||
private @Nullable String stream;
|
||||
|
||||
private LoadImageUpdateListener(ImageArchive archive) {
|
||||
this.archive = archive;
|
||||
@@ -584,8 +585,11 @@ public class DockerApi {
|
||||
|
||||
@Override
|
||||
public void onUpdate(PushImageUpdateEvent event) {
|
||||
Assert.state(event.getErrorDetail() == null,
|
||||
() -> "Error response received when pushing image: " + event.getErrorDetail().getMessage());
|
||||
ErrorDetail errorDetail = event.getErrorDetail();
|
||||
if (errorDetail != null) {
|
||||
throw new IllegalStateException(
|
||||
"Error response received when pushing image: " + errorDetail.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+10
-6
@@ -34,6 +34,7 @@ import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.BlobReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchiveIndex;
|
||||
@@ -101,7 +102,7 @@ class ExportedImageTar implements Closeable {
|
||||
* @return a new {@link TarArchive} instance or {@code null} if this entry is not
|
||||
* a layer.
|
||||
*/
|
||||
abstract TarArchive getLayerArchive(TarArchiveInputStream tar, TarArchiveEntry entry);
|
||||
abstract @Nullable TarArchive getLayerArchive(TarArchiveInputStream tar, TarArchiveEntry entry);
|
||||
|
||||
/**
|
||||
* Create a new {@link LayerArchiveFactory} for the given tar file using either
|
||||
@@ -129,8 +130,11 @@ class ExportedImageTar implements Closeable {
|
||||
Assert.state(index != null || manifest != null,
|
||||
() -> "Exported image '%s' does not contain 'index.json' or 'manifest.json'"
|
||||
.formatted(reference));
|
||||
return (index != null) ? new IndexLayerArchiveFactory(tarFile, index)
|
||||
: new ManifestLayerArchiveFactory(tarFile, manifest);
|
||||
if (index != null) {
|
||||
return new IndexLayerArchiveFactory(tarFile, index);
|
||||
}
|
||||
Assert.state(manifest != null, "'manifest' must not be null");
|
||||
return new ManifestLayerArchiveFactory(tarFile, manifest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +243,7 @@ class ExportedImageTar implements Closeable {
|
||||
}
|
||||
|
||||
@Override
|
||||
TarArchive getLayerArchive(TarArchiveInputStream tar, TarArchiveEntry entry) {
|
||||
@Nullable TarArchive getLayerArchive(TarArchiveInputStream tar, TarArchiveEntry entry) {
|
||||
String mediaType = this.layerMediaTypes.get(entry.getName());
|
||||
if (mediaType == null) {
|
||||
return null;
|
||||
@@ -264,7 +268,7 @@ class ExportedImageTar implements Closeable {
|
||||
*/
|
||||
private static class ManifestLayerArchiveFactory extends LayerArchiveFactory {
|
||||
|
||||
private Set<String> layers;
|
||||
private final Set<String> layers;
|
||||
|
||||
ManifestLayerArchiveFactory(Path tarFile, ImageArchiveManifest manifest) {
|
||||
this.layers = manifest.getEntries()
|
||||
@@ -274,7 +278,7 @@ class ExportedImageTar implements Closeable {
|
||||
}
|
||||
|
||||
@Override
|
||||
TarArchive getLayerArchive(TarArchiveInputStream tar, TarArchiveEntry entry) {
|
||||
@Nullable TarArchive getLayerArchive(TarArchiveInputStream tar, TarArchiveEntry entry) {
|
||||
if (!this.layers.contains(entry.getName())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
+6
-3
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.boot.buildpack.platform.docker;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link ProgressUpdateEvent} fired for image events.
|
||||
*
|
||||
@@ -25,9 +27,10 @@ package org.springframework.boot.buildpack.platform.docker;
|
||||
*/
|
||||
public class ImageProgressUpdateEvent extends ProgressUpdateEvent {
|
||||
|
||||
private final String id;
|
||||
private final @Nullable String id;
|
||||
|
||||
protected ImageProgressUpdateEvent(String id, String status, ProgressDetail progressDetail, String progress) {
|
||||
protected ImageProgressUpdateEvent(@Nullable String id, String status, @Nullable ProgressDetail progressDetail,
|
||||
@Nullable String progress) {
|
||||
super(status, progressDetail, progress);
|
||||
this.id = id;
|
||||
}
|
||||
@@ -36,7 +39,7 @@ public class ImageProgressUpdateEvent extends ProgressUpdateEvent {
|
||||
* Returns the ID of the image layer being updated if available.
|
||||
* @return the ID of the updated layer or {@code null}
|
||||
*/
|
||||
public String getId() {
|
||||
public @Nullable String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
|
||||
+7
-6
@@ -18,6 +18,7 @@ package org.springframework.boot.buildpack.platform.docker;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link ProgressUpdateEvent} fired as an image is loaded.
|
||||
@@ -27,13 +28,13 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
*/
|
||||
public class LoadImageUpdateEvent extends ProgressUpdateEvent {
|
||||
|
||||
private final String stream;
|
||||
private final @Nullable String stream;
|
||||
|
||||
private final ErrorDetail errorDetail;
|
||||
private final @Nullable ErrorDetail errorDetail;
|
||||
|
||||
@JsonCreator
|
||||
public LoadImageUpdateEvent(String stream, String status, ProgressDetail progressDetail, String progress,
|
||||
ErrorDetail errorDetail) {
|
||||
public LoadImageUpdateEvent(@Nullable String stream, String status, ProgressDetail progressDetail, String progress,
|
||||
@Nullable ErrorDetail errorDetail) {
|
||||
super(status, progressDetail, progress);
|
||||
this.stream = stream;
|
||||
this.errorDetail = errorDetail;
|
||||
@@ -43,7 +44,7 @@ public class LoadImageUpdateEvent extends ProgressUpdateEvent {
|
||||
* Return the stream response or {@code null} if no response is available.
|
||||
* @return the stream response.
|
||||
*/
|
||||
public String getStream() {
|
||||
public @Nullable String getStream() {
|
||||
return this.stream;
|
||||
}
|
||||
|
||||
@@ -52,7 +53,7 @@ public class LoadImageUpdateEvent extends ProgressUpdateEvent {
|
||||
* @return the error detail, if any
|
||||
* @since 3.2.12
|
||||
*/
|
||||
public ErrorDetail getErrorDetail() {
|
||||
public @Nullable ErrorDetail getErrorDetail() {
|
||||
return this.errorDetail;
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -22,6 +22,8 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
@@ -80,7 +82,7 @@ public class LogUpdateEvent extends UpdateEvent {
|
||||
}
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
byte[] message = ex.getMessage().getBytes(StandardCharsets.UTF_8);
|
||||
byte[] message = (ex.getMessage() == null) ? new byte[0] : ex.getMessage().getBytes(StandardCharsets.UTF_8);
|
||||
consumer.accept(new LogUpdateEvent(StreamType.STD_ERR, message));
|
||||
StreamUtils.drain(inputStream);
|
||||
}
|
||||
@@ -89,7 +91,7 @@ public class LogUpdateEvent extends UpdateEvent {
|
||||
}
|
||||
}
|
||||
|
||||
private static LogUpdateEvent read(InputStream inputStream) throws IOException {
|
||||
private static @Nullable LogUpdateEvent read(InputStream inputStream) throws IOException {
|
||||
byte[] header = read(inputStream, 8);
|
||||
if (header == null) {
|
||||
return null;
|
||||
@@ -100,10 +102,11 @@ public class LogUpdateEvent extends UpdateEvent {
|
||||
size = (size << 8) + (header[i + 4] & 0xff);
|
||||
}
|
||||
byte[] payload = read(inputStream, size);
|
||||
Assert.state(payload != null, "'payload' must not be null");
|
||||
return new LogUpdateEvent(streamType, payload);
|
||||
}
|
||||
|
||||
private static byte[] read(InputStream inputStream, long size) throws IOException {
|
||||
private static byte @Nullable [] read(InputStream inputStream, long size) throws IOException {
|
||||
byte[] data = new byte[(int) size];
|
||||
int offset = 0;
|
||||
do {
|
||||
|
||||
+16
-11
@@ -17,6 +17,7 @@
|
||||
package org.springframework.boot.buildpack.platform.docker;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* An {@link UpdateEvent} that includes progress information.
|
||||
@@ -27,13 +28,14 @@ import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
*/
|
||||
public abstract class ProgressUpdateEvent extends UpdateEvent {
|
||||
|
||||
private final String status;
|
||||
private final @Nullable String status;
|
||||
|
||||
private final ProgressDetail progressDetail;
|
||||
private final @Nullable ProgressDetail progressDetail;
|
||||
|
||||
private final String progress;
|
||||
private final @Nullable String progress;
|
||||
|
||||
protected ProgressUpdateEvent(String status, ProgressDetail progressDetail, String progress) {
|
||||
protected ProgressUpdateEvent(@Nullable String status, @Nullable ProgressDetail progressDetail,
|
||||
@Nullable String progress) {
|
||||
this.status = status;
|
||||
this.progressDetail = (ProgressDetail.isEmpty(progressDetail)) ? null : progressDetail;
|
||||
this.progress = progress;
|
||||
@@ -43,7 +45,7 @@ public abstract class ProgressUpdateEvent extends UpdateEvent {
|
||||
* Return the status for the update. For example, "Extracting" or "Downloading".
|
||||
* @return the status of the update.
|
||||
*/
|
||||
public String getStatus() {
|
||||
public @Nullable String getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
@@ -51,7 +53,7 @@ public abstract class ProgressUpdateEvent extends UpdateEvent {
|
||||
* Return progress details if available.
|
||||
* @return progress details or {@code null}
|
||||
*/
|
||||
public ProgressDetail getProgressDetail() {
|
||||
public @Nullable ProgressDetail getProgressDetail() {
|
||||
return this.progressDetail;
|
||||
}
|
||||
|
||||
@@ -59,7 +61,7 @@ public abstract class ProgressUpdateEvent extends UpdateEvent {
|
||||
* Return a text based progress bar if progress information is available.
|
||||
* @return the progress bar or {@code null}
|
||||
*/
|
||||
public String getProgress() {
|
||||
public @Nullable String getProgress() {
|
||||
return this.progress;
|
||||
}
|
||||
|
||||
@@ -68,12 +70,12 @@ public abstract class ProgressUpdateEvent extends UpdateEvent {
|
||||
*/
|
||||
public static class ProgressDetail {
|
||||
|
||||
private final Long current;
|
||||
private final @Nullable Long current;
|
||||
|
||||
private final Long total;
|
||||
private final @Nullable Long total;
|
||||
|
||||
@JsonCreator
|
||||
public ProgressDetail(Long current, Long total) {
|
||||
public ProgressDetail(@Nullable Long current, @Nullable Long total) {
|
||||
this.current = current;
|
||||
this.total = total;
|
||||
}
|
||||
@@ -84,11 +86,14 @@ public abstract class ProgressUpdateEvent extends UpdateEvent {
|
||||
* @since 3.3.7
|
||||
*/
|
||||
public int asPercentage() {
|
||||
if (this.total == null || this.current == null) {
|
||||
return 0;
|
||||
}
|
||||
int percentage = (int) ((100.0 / this.total) * this.current);
|
||||
return (percentage < 0) ? 0 : Math.min(percentage, 100);
|
||||
}
|
||||
|
||||
private static boolean isEmpty(ProgressDetail progressDetail) {
|
||||
private static boolean isEmpty(@Nullable ProgressDetail progressDetail) {
|
||||
return progressDetail == null || progressDetail.current == null || progressDetail.total == null;
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -18,6 +18,7 @@ package org.springframework.boot.buildpack.platform.docker;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A {@link ProgressUpdateEvent} fired as an image is pushed to a registry.
|
||||
@@ -27,11 +28,11 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
*/
|
||||
public class PushImageUpdateEvent extends ImageProgressUpdateEvent {
|
||||
|
||||
private final ErrorDetail errorDetail;
|
||||
private final @Nullable ErrorDetail errorDetail;
|
||||
|
||||
@JsonCreator
|
||||
public PushImageUpdateEvent(String id, String status, ProgressDetail progressDetail, String progress,
|
||||
ErrorDetail errorDetail) {
|
||||
@Nullable ErrorDetail errorDetail) {
|
||||
super(id, status, progressDetail, progress);
|
||||
this.errorDetail = errorDetail;
|
||||
}
|
||||
@@ -40,7 +41,7 @@ public class PushImageUpdateEvent extends ImageProgressUpdateEvent {
|
||||
* Returns the details of any error encountered during processing.
|
||||
* @return the error
|
||||
*/
|
||||
public ErrorDetail getErrorDetail() {
|
||||
public @Nullable ErrorDetail getErrorDetail() {
|
||||
return this.errorDetail;
|
||||
}
|
||||
|
||||
|
||||
+18
-4
@@ -19,8 +19,10 @@ package org.springframework.boot.buildpack.platform.docker.configuration;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.json.MappedObject;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A class that represents credentials for a server as returned from a
|
||||
@@ -40,15 +42,27 @@ class Credential extends MappedObject {
|
||||
|
||||
private final String secret;
|
||||
|
||||
private final String serverUrl;
|
||||
private final @Nullable String serverUrl;
|
||||
|
||||
Credential(JsonNode node) {
|
||||
super(node, MethodHandles.lookup());
|
||||
this.username = valueAt("/Username", String.class);
|
||||
this.secret = valueAt("/Secret", String.class);
|
||||
this.username = extractUsername();
|
||||
this.secret = extractSecret();
|
||||
this.serverUrl = valueAt("/ServerURL", String.class);
|
||||
}
|
||||
|
||||
private String extractSecret() {
|
||||
String result = valueAt("/Secret", String.class);
|
||||
Assert.state(result != null, "'result' must not be null");
|
||||
return result;
|
||||
}
|
||||
|
||||
private String extractUsername() {
|
||||
String result = valueAt("/Username", String.class);
|
||||
Assert.state(result != null, "'result' must not be null");
|
||||
return result;
|
||||
}
|
||||
|
||||
String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
@@ -57,7 +71,7 @@ class Credential extends MappedObject {
|
||||
return this.secret;
|
||||
}
|
||||
|
||||
String getServerUrl() {
|
||||
@Nullable String getServerUrl() {
|
||||
return this.serverUrl;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -25,6 +25,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sun.jna.Platform;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.json.SharedObjectMapper;
|
||||
|
||||
@@ -48,7 +49,7 @@ class CredentialHelper {
|
||||
this.executable = executable;
|
||||
}
|
||||
|
||||
Credential get(String serverUrl) throws IOException {
|
||||
@Nullable Credential get(String serverUrl) throws IOException {
|
||||
ProcessBuilder processBuilder = processBuilder("get");
|
||||
Process process = start(processBuilder);
|
||||
try (OutputStream request = process.getOutputStream()) {
|
||||
|
||||
+19
-18
@@ -31,6 +31,7 @@ import java.util.function.Supplier;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.NullNode;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.json.MappedObject;
|
||||
import org.springframework.boot.buildpack.platform.json.SharedObjectMapper;
|
||||
@@ -124,7 +125,7 @@ final class DockerConfigurationMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
private static DockerContext createDockerContext(String configLocation, String currentContext) {
|
||||
private static DockerContext createDockerContext(String configLocation, @Nullable String currentContext) {
|
||||
if (currentContext == null || DEFAULT_CONTEXT.equals(currentContext)) {
|
||||
return DockerContext.empty();
|
||||
}
|
||||
@@ -145,7 +146,7 @@ final class DockerConfigurationMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
private static String asHash(String currentContext) {
|
||||
private static @Nullable String asHash(String currentContext) {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
byte[] hash = digest.digest(currentContext.getBytes(StandardCharsets.UTF_8));
|
||||
@@ -167,9 +168,9 @@ final class DockerConfigurationMetadata {
|
||||
|
||||
static final class DockerConfig extends MappedObject {
|
||||
|
||||
private final String currentContext;
|
||||
private final @Nullable String currentContext;
|
||||
|
||||
private final String credsStore;
|
||||
private final @Nullable String credsStore;
|
||||
|
||||
private final Map<String, String> credHelpers;
|
||||
|
||||
@@ -183,11 +184,11 @@ final class DockerConfigurationMetadata {
|
||||
this.auths = mapAt("/auths", Auth::new);
|
||||
}
|
||||
|
||||
String getCurrentContext() {
|
||||
@Nullable String getCurrentContext() {
|
||||
return this.currentContext;
|
||||
}
|
||||
|
||||
String getCredsStore() {
|
||||
@Nullable String getCredsStore() {
|
||||
return this.credsStore;
|
||||
}
|
||||
|
||||
@@ -211,11 +212,11 @@ final class DockerConfigurationMetadata {
|
||||
|
||||
static final class Auth extends MappedObject {
|
||||
|
||||
private final String username;
|
||||
private final @Nullable String username;
|
||||
|
||||
private final String password;
|
||||
private final @Nullable String password;
|
||||
|
||||
private final String email;
|
||||
private final @Nullable String email;
|
||||
|
||||
Auth(JsonNode node) {
|
||||
super(node, MethodHandles.lookup());
|
||||
@@ -233,15 +234,15 @@ final class DockerConfigurationMetadata {
|
||||
this.email = valueAt("/email", String.class);
|
||||
}
|
||||
|
||||
String getUsername() {
|
||||
@Nullable String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
String getPassword() {
|
||||
@Nullable String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
String getEmail() {
|
||||
@Nullable String getEmail() {
|
||||
return this.email;
|
||||
}
|
||||
|
||||
@@ -254,20 +255,20 @@ final class DockerConfigurationMetadata {
|
||||
|
||||
static final class DockerContext extends MappedObject {
|
||||
|
||||
private final String dockerHost;
|
||||
private final @Nullable String dockerHost;
|
||||
|
||||
private final Boolean skipTlsVerify;
|
||||
private final @Nullable Boolean skipTlsVerify;
|
||||
|
||||
private final String tlsPath;
|
||||
private final @Nullable String tlsPath;
|
||||
|
||||
private DockerContext(JsonNode node, String tlsPath) {
|
||||
private DockerContext(JsonNode node, @Nullable String tlsPath) {
|
||||
super(node, MethodHandles.lookup());
|
||||
this.dockerHost = valueAt("/Endpoints/" + DOCKER_ENDPOINT + "/Host", String.class);
|
||||
this.skipTlsVerify = valueAt("/Endpoints/" + DOCKER_ENDPOINT + "/SkipTLSVerify", Boolean.class);
|
||||
this.tlsPath = tlsPath;
|
||||
}
|
||||
|
||||
String getDockerHost() {
|
||||
@Nullable String getDockerHost() {
|
||||
return this.dockerHost;
|
||||
}
|
||||
|
||||
@@ -275,7 +276,7 @@ final class DockerConfigurationMetadata {
|
||||
return this.skipTlsVerify != null && !this.skipTlsVerify;
|
||||
}
|
||||
|
||||
String getTlsPath() {
|
||||
@Nullable String getTlsPath() {
|
||||
return this.tlsPath;
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.boot.buildpack.platform.docker.configuration;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -33,7 +35,8 @@ public sealed interface DockerConnectionConfiguration {
|
||||
* @param secure if connection is secure
|
||||
* @param certificatePath a path to the certificate used for secure connections
|
||||
*/
|
||||
record Host(String address, boolean secure, String certificatePath) implements DockerConnectionConfiguration {
|
||||
record Host(String address, boolean secure,
|
||||
@Nullable String certificatePath) implements DockerConnectionConfiguration {
|
||||
|
||||
public Host(String address) {
|
||||
this(address, false, null);
|
||||
|
||||
+8
-6
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.boot.buildpack.platform.docker.configuration;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Docker host connection options.
|
||||
*
|
||||
@@ -24,23 +26,23 @@ package org.springframework.boot.buildpack.platform.docker.configuration;
|
||||
*/
|
||||
public class DockerHost {
|
||||
|
||||
private final String address;
|
||||
private final @Nullable String address;
|
||||
|
||||
private final boolean secure;
|
||||
|
||||
private final String certificatePath;
|
||||
private final @Nullable String certificatePath;
|
||||
|
||||
public DockerHost(String address) {
|
||||
public DockerHost(@Nullable String address) {
|
||||
this(address, false, null);
|
||||
}
|
||||
|
||||
public DockerHost(String address, boolean secure, String certificatePath) {
|
||||
public DockerHost(@Nullable String address, boolean secure, @Nullable String certificatePath) {
|
||||
this.address = address;
|
||||
this.secure = secure;
|
||||
this.certificatePath = certificatePath;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
public @Nullable String getAddress() {
|
||||
return this.address;
|
||||
}
|
||||
|
||||
@@ -48,7 +50,7 @@ public class DockerHost {
|
||||
return this.secure;
|
||||
}
|
||||
|
||||
public String getCertificatePath() {
|
||||
public @Nullable String getCertificatePath() {
|
||||
return this.certificatePath;
|
||||
}
|
||||
|
||||
|
||||
+8
-5
@@ -18,6 +18,8 @@ package org.springframework.boot.buildpack.platform.docker.configuration;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -43,7 +45,7 @@ public interface DockerRegistryAuthentication {
|
||||
* @return the auth header
|
||||
* @since 3.5.0
|
||||
*/
|
||||
default String getAuthHeader(ImageReference imageReference) {
|
||||
default @Nullable String getAuthHeader(@Nullable ImageReference imageReference) {
|
||||
return getAuthHeader();
|
||||
}
|
||||
|
||||
@@ -51,7 +53,7 @@ public interface DockerRegistryAuthentication {
|
||||
* Returns the auth header that should be used for docker authentication.
|
||||
* @return the auth header
|
||||
*/
|
||||
String getAuthHeader();
|
||||
@Nullable String getAuthHeader();
|
||||
|
||||
/**
|
||||
* Factory method to that returns a new {@link DockerRegistryAuthentication} instance
|
||||
@@ -76,7 +78,8 @@ public interface DockerRegistryAuthentication {
|
||||
* @return a new {@link DockerRegistryAuthentication} instance
|
||||
* @since 3.5.0
|
||||
*/
|
||||
static DockerRegistryAuthentication user(String username, String password, String serverAddress, String email) {
|
||||
static DockerRegistryAuthentication user(String username, String password, @Nullable String serverAddress,
|
||||
@Nullable String email) {
|
||||
return new DockerRegistryUserAuthentication(username, password, serverAddress, email);
|
||||
}
|
||||
|
||||
@@ -90,7 +93,7 @@ public interface DockerRegistryAuthentication {
|
||||
* @since 3.5.0
|
||||
* @see #configuration(DockerRegistryAuthentication, BiConsumer)
|
||||
*/
|
||||
static DockerRegistryAuthentication configuration(DockerRegistryAuthentication fallback) {
|
||||
static DockerRegistryAuthentication configuration(@Nullable DockerRegistryAuthentication fallback) {
|
||||
return configuration(fallback, (message, ex) -> System.out.println(message));
|
||||
}
|
||||
|
||||
@@ -106,7 +109,7 @@ public interface DockerRegistryAuthentication {
|
||||
* @since 3.5.0
|
||||
* @see #configuration(DockerRegistryAuthentication, BiConsumer)
|
||||
*/
|
||||
static DockerRegistryAuthentication configuration(DockerRegistryAuthentication fallback,
|
||||
static DockerRegistryAuthentication configuration(@Nullable DockerRegistryAuthentication fallback,
|
||||
BiConsumer<String, Exception> credentialHelperExceptionHandler) {
|
||||
Assert.notNull(credentialHelperExceptionHandler, () -> "'credentialHelperExceptionHandler' must not be null");
|
||||
return new DockerRegistryConfigAuthentication(fallback, credentialHelperExceptionHandler);
|
||||
|
||||
+26
-15
@@ -21,10 +21,13 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfigurationMetadata.Auth;
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfigurationMetadata.DockerConfig;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.system.Environment;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -42,7 +45,7 @@ class DockerRegistryConfigAuthentication implements DockerRegistryAuthentication
|
||||
|
||||
static Map<String, Credential> credentialFromHelperCache = new ConcurrentHashMap<>();
|
||||
|
||||
private final DockerRegistryAuthentication fallback;
|
||||
private final @Nullable DockerRegistryAuthentication fallback;
|
||||
|
||||
private final BiConsumer<String, Exception> credentialHelperExceptionHandler;
|
||||
|
||||
@@ -50,13 +53,13 @@ class DockerRegistryConfigAuthentication implements DockerRegistryAuthentication
|
||||
|
||||
private final DockerConfig dockerConfig;
|
||||
|
||||
DockerRegistryConfigAuthentication(DockerRegistryAuthentication fallback,
|
||||
DockerRegistryConfigAuthentication(@Nullable DockerRegistryAuthentication fallback,
|
||||
BiConsumer<String, Exception> credentialHelperExceptionHandler) {
|
||||
this(fallback, credentialHelperExceptionHandler, Environment.SYSTEM,
|
||||
(helper) -> new CredentialHelper("docker-credential-" + helper));
|
||||
}
|
||||
|
||||
DockerRegistryConfigAuthentication(DockerRegistryAuthentication fallback,
|
||||
DockerRegistryConfigAuthentication(@Nullable DockerRegistryAuthentication fallback,
|
||||
BiConsumer<String, Exception> credentialHelperExceptionHandler, Environment environment,
|
||||
Function<String, CredentialHelper> credentialHelperFactory) {
|
||||
this.fallback = fallback;
|
||||
@@ -66,23 +69,23 @@ class DockerRegistryConfigAuthentication implements DockerRegistryAuthentication
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthHeader() {
|
||||
public @Nullable String getAuthHeader() {
|
||||
return getAuthHeader(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthHeader(ImageReference imageReference) {
|
||||
public @Nullable String getAuthHeader(@Nullable ImageReference imageReference) {
|
||||
String serverUrl = getServerUrl(imageReference);
|
||||
DockerRegistryAuthentication authentication = getAuthentication(serverUrl);
|
||||
return (authentication != null) ? authentication.getAuthHeader(imageReference) : null;
|
||||
}
|
||||
|
||||
private String getServerUrl(ImageReference imageReference) {
|
||||
private @Nullable String getServerUrl(@Nullable ImageReference imageReference) {
|
||||
String domain = (imageReference != null) ? imageReference.getDomain() : null;
|
||||
return (!DEFAULT_DOMAIN.equals(domain)) ? domain : INDEX_URL;
|
||||
}
|
||||
|
||||
private DockerRegistryAuthentication getAuthentication(String serverUrl) {
|
||||
private @Nullable DockerRegistryAuthentication getAuthentication(@Nullable String serverUrl) {
|
||||
Credential credentialsFromHelper = getCredentialsFromHelper(serverUrl);
|
||||
Map.Entry<String, Auth> authConfigEntry = getAuthConfigEntry(serverUrl);
|
||||
Auth authConfig = (authConfigEntry != null) ? authConfigEntry.getValue() : null;
|
||||
@@ -90,14 +93,19 @@ class DockerRegistryConfigAuthentication implements DockerRegistryAuthentication
|
||||
return getAuthentication(credentialsFromHelper, authConfig, serverUrl);
|
||||
}
|
||||
if (authConfig != null) {
|
||||
return DockerRegistryAuthentication.user(authConfig.getUsername(), authConfig.getPassword(),
|
||||
authConfigEntry.getKey(), authConfig.getEmail());
|
||||
Assert.state(authConfigEntry != null, "'authConfigEntry' must not be null");
|
||||
String username = authConfig.getUsername();
|
||||
String password = authConfig.getPassword();
|
||||
Assert.state(username != null, "'username' must not be null");
|
||||
Assert.state(password != null, "'password' must not be null");
|
||||
return DockerRegistryAuthentication.user(username, password, authConfigEntry.getKey(),
|
||||
authConfig.getEmail());
|
||||
}
|
||||
return this.fallback;
|
||||
}
|
||||
|
||||
private DockerRegistryAuthentication getAuthentication(Credential credentialsFromHelper, Auth authConfig,
|
||||
String serverUrl) {
|
||||
private DockerRegistryAuthentication getAuthentication(Credential credentialsFromHelper, @Nullable Auth authConfig,
|
||||
@Nullable String serverUrl) {
|
||||
if (credentialsFromHelper.isIdentityToken()) {
|
||||
return DockerRegistryAuthentication.token(credentialsFromHelper.getSecret());
|
||||
}
|
||||
@@ -109,12 +117,12 @@ class DockerRegistryConfigAuthentication implements DockerRegistryAuthentication
|
||||
return DockerRegistryAuthentication.user(username, password, serverAddress, email);
|
||||
}
|
||||
|
||||
private Credential getCredentialsFromHelper(String serverUrl) {
|
||||
private @Nullable Credential getCredentialsFromHelper(@Nullable String serverUrl) {
|
||||
return StringUtils.hasLength(serverUrl)
|
||||
? credentialFromHelperCache.computeIfAbsent(serverUrl, this::computeCredentialsFromHelper) : null;
|
||||
}
|
||||
|
||||
private Credential computeCredentialsFromHelper(String serverUrl) {
|
||||
private @Nullable Credential computeCredentialsFromHelper(String serverUrl) {
|
||||
CredentialHelper credentialHelper = getCredentialHelper(serverUrl);
|
||||
if (credentialHelper != null) {
|
||||
try {
|
||||
@@ -129,12 +137,15 @@ class DockerRegistryConfigAuthentication implements DockerRegistryAuthentication
|
||||
return null;
|
||||
}
|
||||
|
||||
private CredentialHelper getCredentialHelper(String serverUrl) {
|
||||
private @Nullable CredentialHelper getCredentialHelper(String serverUrl) {
|
||||
String name = this.dockerConfig.getCredHelpers().getOrDefault(serverUrl, this.dockerConfig.getCredsStore());
|
||||
return (StringUtils.hasLength(name)) ? this.credentialHelperFactory.apply(name) : null;
|
||||
}
|
||||
|
||||
private Map.Entry<String, Auth> getAuthConfigEntry(String serverUrl) {
|
||||
private Map.@Nullable Entry<String, Auth> getAuthConfigEntry(@Nullable String serverUrl) {
|
||||
if (serverUrl == null) {
|
||||
return null;
|
||||
}
|
||||
for (Map.Entry<String, Auth> candidate : this.dockerConfig.getAuths().entrySet()) {
|
||||
if (candidate.getKey().equals(serverUrl) || candidate.getKey().endsWith("://" + serverUrl)) {
|
||||
return candidate;
|
||||
|
||||
+6
-5
@@ -17,6 +17,7 @@
|
||||
package org.springframework.boot.buildpack.platform.docker.configuration;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* {@link DockerRegistryAuthentication} for
|
||||
@@ -33,12 +34,12 @@ class DockerRegistryUserAuthentication extends JsonEncodedDockerRegistryAuthenti
|
||||
private final String password;
|
||||
|
||||
@JsonProperty("serveraddress")
|
||||
private final String url;
|
||||
private final @Nullable String url;
|
||||
|
||||
@JsonProperty
|
||||
private final String email;
|
||||
private final @Nullable String email;
|
||||
|
||||
DockerRegistryUserAuthentication(String username, String password, String url, String email) {
|
||||
DockerRegistryUserAuthentication(String username, String password, @Nullable String url, @Nullable String email) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.url = url;
|
||||
@@ -54,11 +55,11 @@ class DockerRegistryUserAuthentication extends JsonEncodedDockerRegistryAuthenti
|
||||
return this.password;
|
||||
}
|
||||
|
||||
String getUrl() {
|
||||
@Nullable String getUrl() {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
String getEmail() {
|
||||
@Nullable String getEmail() {
|
||||
return this.email;
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -20,6 +20,7 @@ import java.util.Base64;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.json.SharedObjectMapper;
|
||||
|
||||
@@ -32,10 +33,10 @@ import org.springframework.boot.buildpack.platform.json.SharedObjectMapper;
|
||||
class JsonEncodedDockerRegistryAuthentication implements DockerRegistryAuthentication {
|
||||
|
||||
@JsonIgnore
|
||||
private String authHeader;
|
||||
private @Nullable String authHeader;
|
||||
|
||||
@Override
|
||||
public String getAuthHeader() {
|
||||
public @Nullable String getAuthHeader() {
|
||||
return this.authHeader;
|
||||
}
|
||||
|
||||
|
||||
+7
-5
@@ -20,6 +20,7 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import com.sun.jna.Platform;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConfigurationMetadata.DockerContext;
|
||||
import org.springframework.boot.buildpack.platform.system.Environment;
|
||||
@@ -46,11 +47,11 @@ public class ResolvedDockerHost extends DockerHost {
|
||||
|
||||
private static final String DOCKER_CONTEXT = "DOCKER_CONTEXT";
|
||||
|
||||
ResolvedDockerHost(String address) {
|
||||
ResolvedDockerHost(@Nullable String address) {
|
||||
super(address);
|
||||
}
|
||||
|
||||
ResolvedDockerHost(String address, boolean secure, String certificatePath) {
|
||||
ResolvedDockerHost(@Nullable String address, boolean secure, @Nullable String certificatePath) {
|
||||
super(address, secure, certificatePath);
|
||||
}
|
||||
|
||||
@@ -81,11 +82,12 @@ public class ResolvedDockerHost extends DockerHost {
|
||||
* @param connectionConfiguration the host configuration or {@code null}
|
||||
* @return the resolved docker host
|
||||
*/
|
||||
public static ResolvedDockerHost from(DockerConnectionConfiguration connectionConfiguration) {
|
||||
public static ResolvedDockerHost from(@Nullable DockerConnectionConfiguration connectionConfiguration) {
|
||||
return from(Environment.SYSTEM, connectionConfiguration);
|
||||
}
|
||||
|
||||
static ResolvedDockerHost from(Environment environment, DockerConnectionConfiguration connectionConfiguration) {
|
||||
static ResolvedDockerHost from(Environment environment,
|
||||
@Nullable DockerConnectionConfiguration connectionConfiguration) {
|
||||
DockerConfigurationMetadata environmentConfiguration = DockerConfigurationMetadata.from(environment);
|
||||
if (environment.get(DOCKER_CONTEXT) != null) {
|
||||
DockerContext context = environmentConfiguration.forContext(environment.get(DOCKER_CONTEXT));
|
||||
@@ -114,7 +116,7 @@ public class ResolvedDockerHost extends DockerHost {
|
||||
return Platform.isWindows() ? WINDOWS_NAMED_PIPE_PATH : DOMAIN_SOCKET_PATH;
|
||||
}
|
||||
|
||||
private static boolean isTrue(String value) {
|
||||
private static boolean isTrue(@Nullable String value) {
|
||||
try {
|
||||
return (value != null) && (Integer.parseInt(value) == 1);
|
||||
}
|
||||
|
||||
+3
@@ -17,4 +17,7 @@
|
||||
/**
|
||||
* Docker configuration options.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.buildpack.platform.docker.configuration;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
+3
@@ -17,4 +17,7 @@
|
||||
/**
|
||||
* A limited Docker API providing the operations needed by pack.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.buildpack.platform.docker;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
+5
-3
@@ -28,6 +28,8 @@ import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Utility methods for creating Java trust material from key and certificate files.
|
||||
*
|
||||
@@ -74,7 +76,7 @@ final class KeyStoreFactory {
|
||||
return keyStore;
|
||||
}
|
||||
|
||||
private static PrivateKey getPrivateKey(Path path) throws IOException {
|
||||
private static @Nullable PrivateKey getPrivateKey(@Nullable Path path) throws IOException {
|
||||
if (path != null && Files.exists(path)) {
|
||||
String text = Files.readString(path);
|
||||
return PemPrivateKeyParser.parse(text);
|
||||
@@ -82,8 +84,8 @@ final class KeyStoreFactory {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void addCertificates(KeyStore keyStore, X509Certificate[] certificates, PrivateKey privateKey,
|
||||
String alias) throws KeyStoreException {
|
||||
private static void addCertificates(KeyStore keyStore, X509Certificate[] certificates,
|
||||
@Nullable PrivateKey privateKey, String alias) throws KeyStoreException {
|
||||
if (privateKey != null) {
|
||||
keyStore.setKeyEntry(alias, privateKey, NO_PASSWORD, certificates);
|
||||
}
|
||||
|
||||
+2
@@ -29,6 +29,7 @@ import java.util.regex.Pattern;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.lang.Contract;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@@ -56,6 +57,7 @@ final class PemCertificateParser {
|
||||
* @param text the text to parse
|
||||
* @return the parsed certificates
|
||||
*/
|
||||
@Contract("!null -> !null")
|
||||
static @Nullable List<X509Certificate> parse(@Nullable String text) {
|
||||
if (text == null) {
|
||||
return null;
|
||||
|
||||
+3
@@ -17,4 +17,7 @@
|
||||
/**
|
||||
* Utilities and classes for managing SSL context and keys.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.buildpack.platform.docker.ssl;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
+3
-1
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.boot.buildpack.platform.docker.transport;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -45,7 +47,7 @@ public class DockerConnectionException extends RuntimeException {
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
private static String getCauseMessage(Exception cause) {
|
||||
private static @Nullable String getCauseMessage(Exception cause) {
|
||||
if (cause.getCause() != null && cause.getCause().getClass().getName().equals(JNA_EXCEPTION_CLASS_NAME)) {
|
||||
return cause.getCause().getMessage();
|
||||
}
|
||||
|
||||
+10
-8
@@ -18,6 +18,8 @@ package org.springframework.boot.buildpack.platform.docker.transport;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -34,12 +36,12 @@ public class DockerEngineException extends RuntimeException {
|
||||
|
||||
private final String reasonPhrase;
|
||||
|
||||
private final Errors errors;
|
||||
private final @Nullable Errors errors;
|
||||
|
||||
private final Message responseMessage;
|
||||
private final @Nullable Message responseMessage;
|
||||
|
||||
public DockerEngineException(String host, URI uri, int statusCode, String reasonPhrase, Errors errors,
|
||||
Message responseMessage) {
|
||||
public DockerEngineException(String host, URI uri, int statusCode, String reasonPhrase, @Nullable Errors errors,
|
||||
@Nullable Message responseMessage) {
|
||||
super(buildMessage(host, uri, statusCode, reasonPhrase, errors, responseMessage));
|
||||
this.statusCode = statusCode;
|
||||
this.reasonPhrase = reasonPhrase;
|
||||
@@ -68,7 +70,7 @@ public class DockerEngineException extends RuntimeException {
|
||||
* errors JSON could not be read.
|
||||
* @return the errors or {@code null}
|
||||
*/
|
||||
public Errors getErrors() {
|
||||
public @Nullable Errors getErrors() {
|
||||
return this.errors;
|
||||
}
|
||||
|
||||
@@ -77,12 +79,12 @@ public class DockerEngineException extends RuntimeException {
|
||||
* message JSON could not be read.
|
||||
* @return the message or {@code null}
|
||||
*/
|
||||
public Message getResponseMessage() {
|
||||
public @Nullable Message getResponseMessage() {
|
||||
return this.responseMessage;
|
||||
}
|
||||
|
||||
private static String buildMessage(String host, URI uri, int statusCode, String reasonPhrase, Errors errors,
|
||||
Message responseMessage) {
|
||||
private static String buildMessage(String host, URI uri, int statusCode, String reasonPhrase,
|
||||
@Nullable Errors errors, @Nullable Message responseMessage) {
|
||||
Assert.notNull(host, "'host' must not be null");
|
||||
Assert.notNull(uri, "'uri' must not be null");
|
||||
StringBuilder message = new StringBuilder(
|
||||
|
||||
+2
-1
@@ -23,6 +23,7 @@ import java.util.stream.Stream;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Errors returned from the Docker API.
|
||||
@@ -35,7 +36,7 @@ public class Errors implements Iterable<Errors.Error> {
|
||||
private final List<Error> errors;
|
||||
|
||||
@JsonCreator
|
||||
Errors(@JsonProperty("errors") List<Error> errors) {
|
||||
Errors(@JsonProperty("errors") @Nullable List<Error> errors) {
|
||||
this.errors = (errors != null) ? errors : Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
+6
-5
@@ -37,6 +37,7 @@ import org.apache.hc.core5.http.HttpEntity;
|
||||
import org.apache.hc.core5.http.HttpHost;
|
||||
import org.apache.hc.core5.http.HttpRequest;
|
||||
import org.apache.hc.core5.http.io.entity.AbstractHttpEntity;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.io.Content;
|
||||
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
||||
@@ -95,7 +96,7 @@ abstract class HttpClientTransport implements HttpTransport {
|
||||
* @return the operation response
|
||||
*/
|
||||
@Override
|
||||
public Response post(URI uri, String registryAuth) {
|
||||
public Response post(URI uri, @Nullable String registryAuth) {
|
||||
return execute(new HttpPost(uri), registryAuth);
|
||||
}
|
||||
|
||||
@@ -148,7 +149,7 @@ abstract class HttpClientTransport implements HttpTransport {
|
||||
return execute(request);
|
||||
}
|
||||
|
||||
private Response execute(HttpUriRequestBase request, String registryAuth) {
|
||||
private Response execute(HttpUriRequestBase request, @Nullable String registryAuth) {
|
||||
if (StringUtils.hasText(registryAuth)) {
|
||||
request.setHeader(REGISTRY_AUTH_HEADER, registryAuth);
|
||||
}
|
||||
@@ -178,7 +179,7 @@ abstract class HttpClientTransport implements HttpTransport {
|
||||
protected void beforeExecute(HttpRequest request) {
|
||||
}
|
||||
|
||||
private byte[] readContent(ClassicHttpResponse response) throws IOException {
|
||||
private byte @Nullable [] readContent(ClassicHttpResponse response) throws IOException {
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity == null) {
|
||||
return null;
|
||||
@@ -188,7 +189,7 @@ abstract class HttpClientTransport implements HttpTransport {
|
||||
}
|
||||
}
|
||||
|
||||
private Errors deserializeErrors(byte[] content) {
|
||||
private @Nullable Errors deserializeErrors(byte @Nullable [] content) {
|
||||
if (content == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -200,7 +201,7 @@ abstract class HttpClientTransport implements HttpTransport {
|
||||
}
|
||||
}
|
||||
|
||||
private Message deserializeMessage(byte[] content) {
|
||||
private @Nullable Message deserializeMessage(byte @Nullable [] content) {
|
||||
if (content == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
+4
-3
@@ -23,6 +23,7 @@ import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
|
||||
import org.apache.hc.core5.http.Header;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConnectionConfiguration;
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerHost;
|
||||
@@ -61,7 +62,7 @@ public interface HttpTransport {
|
||||
* @return the operation response
|
||||
* @throws IOException on IO error
|
||||
*/
|
||||
Response post(URI uri, String registryAuth) throws IOException;
|
||||
Response post(URI uri, @Nullable String registryAuth) throws IOException;
|
||||
|
||||
/**
|
||||
* Perform an HTTP POST operation.
|
||||
@@ -104,7 +105,7 @@ public interface HttpTransport {
|
||||
* @param connectionConfiguration the Docker host information
|
||||
* @return a {@link HttpTransport} instance
|
||||
*/
|
||||
static HttpTransport create(DockerConnectionConfiguration connectionConfiguration) {
|
||||
static HttpTransport create(@Nullable DockerConnectionConfiguration connectionConfiguration) {
|
||||
ResolvedDockerHost host = ResolvedDockerHost.from(connectionConfiguration);
|
||||
HttpTransport remote = RemoteHttpClientTransport.createIfPossible(host);
|
||||
return (remote != null) ? remote : LocalHttpClientTransport.create(host);
|
||||
@@ -122,7 +123,7 @@ public interface HttpTransport {
|
||||
*/
|
||||
InputStream getContent() throws IOException;
|
||||
|
||||
default Header getHeader(String name) {
|
||||
default @Nullable Header getHeader(String name) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -39,6 +39,7 @@ import org.apache.hc.core5.http.HttpRequest;
|
||||
import org.apache.hc.core5.http.config.Lookup;
|
||||
import org.apache.hc.core5.http.protocol.HttpContext;
|
||||
import org.apache.hc.core5.util.TimeValue;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost;
|
||||
import org.springframework.boot.buildpack.platform.socket.NamedPipeSocket;
|
||||
@@ -85,7 +86,7 @@ final class LocalHttpClientTransport extends HttpClientTransport {
|
||||
.setValidateAfterInactivity(TimeValue.NEG_ONE_MILLISECOND)
|
||||
.build();
|
||||
|
||||
private static final Lookup<TlsSocketStrategy> NO_TLS_SOCKET = (name) -> null;
|
||||
private static final Lookup<@Nullable TlsSocketStrategy> NO_TLS_SOCKET = (name) -> null;
|
||||
|
||||
LocalConnectionManager(ResolvedDockerHost dockerHost) {
|
||||
super(createhttpClientConnectionOperator(dockerHost), null);
|
||||
|
||||
+5
-4
@@ -18,6 +18,7 @@ package org.springframework.boot.buildpack.platform.docker.transport;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A message returned from the Docker API.
|
||||
@@ -27,10 +28,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
*/
|
||||
public class Message {
|
||||
|
||||
private final String message;
|
||||
private final @Nullable String message;
|
||||
|
||||
@JsonCreator
|
||||
Message(@JsonProperty("message") String message) {
|
||||
Message(@JsonProperty("message") @Nullable String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@@ -38,13 +39,13 @@ public class Message {
|
||||
* Return the message contained in the response.
|
||||
* @return the message
|
||||
*/
|
||||
public String getMessage() {
|
||||
public @Nullable String getMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.message;
|
||||
return (this.message == null) ? "<null>" : this.message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-2
@@ -30,6 +30,7 @@ import org.apache.hc.client5.http.ssl.TlsSocketStrategy;
|
||||
import org.apache.hc.core5.http.HttpHost;
|
||||
import org.apache.hc.core5.http.io.SocketConfig;
|
||||
import org.apache.hc.core5.util.Timeout;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerHost;
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost;
|
||||
@@ -51,11 +52,11 @@ final class RemoteHttpClientTransport extends HttpClientTransport {
|
||||
super(client, host);
|
||||
}
|
||||
|
||||
static RemoteHttpClientTransport createIfPossible(ResolvedDockerHost dockerHost) {
|
||||
static @Nullable RemoteHttpClientTransport createIfPossible(ResolvedDockerHost dockerHost) {
|
||||
return createIfPossible(dockerHost, new SslContextFactory());
|
||||
}
|
||||
|
||||
static RemoteHttpClientTransport createIfPossible(ResolvedDockerHost dockerHost,
|
||||
static @Nullable RemoteHttpClientTransport createIfPossible(ResolvedDockerHost dockerHost,
|
||||
SslContextFactory sslContextFactory) {
|
||||
if (!dockerHost.isRemote()) {
|
||||
return null;
|
||||
|
||||
+3
@@ -17,4 +17,7 @@
|
||||
/**
|
||||
* Docker transport classes providing HTTP operations on a local or remote engine.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.buildpack.platform.docker.transport;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
+3
-1
@@ -19,6 +19,8 @@ package org.springframework.boot.buildpack.platform.docker.type;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -90,7 +92,7 @@ public final class ApiVersion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+15
-2
@@ -21,6 +21,7 @@ import java.lang.invoke.MethodHandles;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.json.MappedObject;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A reference to a blob by its digest.
|
||||
@@ -36,8 +37,20 @@ public class BlobReference extends MappedObject {
|
||||
|
||||
BlobReference(JsonNode node) {
|
||||
super(node, MethodHandles.lookup());
|
||||
this.digest = valueAt("/digest", String.class);
|
||||
this.mediaType = valueAt("/mediaType", String.class);
|
||||
this.digest = extractDigest();
|
||||
this.mediaType = extractMediaType();
|
||||
}
|
||||
|
||||
private String extractMediaType() {
|
||||
String result = valueAt("/mediaType", String.class);
|
||||
Assert.state(result != null, "'result' must not be null");
|
||||
return result;
|
||||
}
|
||||
|
||||
private String extractDigest() {
|
||||
String result = valueAt("/digest", String.class);
|
||||
Assert.state(result != null, "'result' must not be null");
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+9
-7
@@ -29,6 +29,7 @@ import java.util.function.Consumer;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.json.SharedObjectMapper;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -48,9 +49,9 @@ public class ContainerConfig {
|
||||
|
||||
private final String json;
|
||||
|
||||
ContainerConfig(String user, ImageReference image, String command, List<String> args, Map<String, String> labels,
|
||||
List<Binding> bindings, Map<String, String> env, String networkMode, List<String> securityOptions)
|
||||
throws IOException {
|
||||
ContainerConfig(@Nullable String user, ImageReference image, String command, List<String> args,
|
||||
Map<String, String> labels, List<Binding> bindings, Map<String, String> env, @Nullable String networkMode,
|
||||
List<String> securityOptions) throws IOException {
|
||||
Assert.notNull(image, "'image' must not be null");
|
||||
Assert.hasText(command, "'command' must not be empty");
|
||||
ObjectMapper objectMapper = SharedObjectMapper.get();
|
||||
@@ -112,9 +113,9 @@ public class ContainerConfig {
|
||||
|
||||
private final ImageReference image;
|
||||
|
||||
private String user;
|
||||
private @Nullable String user;
|
||||
|
||||
private String command;
|
||||
private @Nullable String command;
|
||||
|
||||
private final List<String> args = new ArrayList<>();
|
||||
|
||||
@@ -124,7 +125,7 @@ public class ContainerConfig {
|
||||
|
||||
private final Map<String, String> env = new LinkedHashMap<>();
|
||||
|
||||
private String networkMode;
|
||||
private @Nullable String networkMode;
|
||||
|
||||
private final List<String> securityOptions = new ArrayList<>();
|
||||
|
||||
@@ -135,6 +136,7 @@ public class ContainerConfig {
|
||||
private ContainerConfig run(Consumer<Update> update) {
|
||||
update.accept(this);
|
||||
try {
|
||||
Assert.state(this.command != null, "'command' must not be null");
|
||||
return new ContainerConfig(this.user, this.image, this.command, this.args, this.labels, this.bindings,
|
||||
this.env, this.networkMode, this.securityOptions);
|
||||
}
|
||||
@@ -201,7 +203,7 @@ public class ContainerConfig {
|
||||
* connect to.
|
||||
* @param networkMode the network
|
||||
*/
|
||||
public void withNetworkMode(String networkMode) {
|
||||
public void withNetworkMode(@Nullable String networkMode) {
|
||||
this.networkMode = networkMode;
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.boot.buildpack.platform.docker.type;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -34,7 +36,7 @@ public final class ContainerReference {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+15
-6
@@ -21,8 +21,11 @@ import java.io.InputStream;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.NullNode;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.json.MappedObject;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Status details returned from {@code Docker container wait}.
|
||||
@@ -34,20 +37,26 @@ public class ContainerStatus extends MappedObject {
|
||||
|
||||
private final int statusCode;
|
||||
|
||||
private final String waitingErrorMessage;
|
||||
private final @Nullable String waitingErrorMessage;
|
||||
|
||||
ContainerStatus(int statusCode, String waitingErrorMessage) {
|
||||
super(null, null);
|
||||
ContainerStatus(int statusCode, @Nullable String waitingErrorMessage) {
|
||||
super(NullNode.getInstance(), MethodHandles.lookup());
|
||||
this.statusCode = statusCode;
|
||||
this.waitingErrorMessage = waitingErrorMessage;
|
||||
}
|
||||
|
||||
ContainerStatus(JsonNode node) {
|
||||
super(node, MethodHandles.lookup());
|
||||
this.statusCode = valueAt("/StatusCode", Integer.class);
|
||||
this.statusCode = extractStatusCode();
|
||||
this.waitingErrorMessage = valueAt("/Error/Message", String.class);
|
||||
}
|
||||
|
||||
private Integer extractStatusCode() {
|
||||
Integer result = valueAt("/StatusCode", Integer.class);
|
||||
Assert.state(result != null, "'result' must not be null");
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the container exit status code.
|
||||
* @return the exit status code
|
||||
@@ -60,7 +69,7 @@ public class ContainerStatus extends MappedObject {
|
||||
* Return a message indicating an error waiting for a container to stop.
|
||||
* @return the waiting error message
|
||||
*/
|
||||
public String getWaitingErrorMessage() {
|
||||
public @Nullable String getWaitingErrorMessage() {
|
||||
return this.waitingErrorMessage;
|
||||
}
|
||||
|
||||
@@ -81,7 +90,7 @@ public class ContainerStatus extends MappedObject {
|
||||
* @param errorMessage the error message
|
||||
* @return a new {@link ContainerStatus} instance
|
||||
*/
|
||||
public static ContainerStatus of(int statusCode, String errorMessage) {
|
||||
public static ContainerStatus of(int statusCode, @Nullable String errorMessage) {
|
||||
return new ContainerStatus(statusCode, errorMessage);
|
||||
}
|
||||
|
||||
|
||||
+9
-8
@@ -24,6 +24,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.json.MappedObject;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -43,13 +44,13 @@ public class Image extends MappedObject {
|
||||
|
||||
private final List<LayerId> layers;
|
||||
|
||||
private final String os;
|
||||
private final @Nullable String os;
|
||||
|
||||
private final String architecture;
|
||||
private final @Nullable String architecture;
|
||||
|
||||
private final String variant;
|
||||
private final @Nullable String variant;
|
||||
|
||||
private final String created;
|
||||
private final @Nullable String created;
|
||||
|
||||
Image(JsonNode node) {
|
||||
super(node, MethodHandles.lookup());
|
||||
@@ -62,7 +63,7 @@ public class Image extends MappedObject {
|
||||
this.created = valueAt("/Created", String.class);
|
||||
}
|
||||
|
||||
private List<LayerId> extractLayers(String[] layers) {
|
||||
private List<LayerId> extractLayers(String @Nullable [] layers) {
|
||||
if (layers == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -105,7 +106,7 @@ public class Image extends MappedObject {
|
||||
* Return the architecture of the image.
|
||||
* @return the image architecture
|
||||
*/
|
||||
public String getArchitecture() {
|
||||
public @Nullable String getArchitecture() {
|
||||
return this.architecture;
|
||||
}
|
||||
|
||||
@@ -113,7 +114,7 @@ public class Image extends MappedObject {
|
||||
* Return the variant of the image.
|
||||
* @return the image variant
|
||||
*/
|
||||
public String getVariant() {
|
||||
public @Nullable String getVariant() {
|
||||
return this.variant;
|
||||
}
|
||||
|
||||
@@ -121,7 +122,7 @@ public class Image extends MappedObject {
|
||||
* Return the created date of the image.
|
||||
* @return the image created date
|
||||
*/
|
||||
public String getCreated() {
|
||||
public @Nullable String getCreated() {
|
||||
return this.created;
|
||||
}
|
||||
|
||||
|
||||
+10
-8
@@ -33,6 +33,7 @@ import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.io.Content;
|
||||
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
||||
@@ -72,20 +73,21 @@ public class ImageArchive implements TarArchive {
|
||||
|
||||
private final Instant createDate;
|
||||
|
||||
private final ImageReference tag;
|
||||
private final @Nullable ImageReference tag;
|
||||
|
||||
private final String os;
|
||||
|
||||
private final String architecture;
|
||||
private final @Nullable String architecture;
|
||||
|
||||
private final String variant;
|
||||
private final @Nullable String variant;
|
||||
|
||||
private final List<LayerId> existingLayers;
|
||||
|
||||
private final List<Layer> newLayers;
|
||||
|
||||
ImageArchive(ObjectMapper objectMapper, ImageConfig imageConfig, Instant createDate, ImageReference tag, String os,
|
||||
String architecture, String variant, List<LayerId> existingLayers, List<Layer> newLayers) {
|
||||
ImageArchive(ObjectMapper objectMapper, ImageConfig imageConfig, Instant createDate, @Nullable ImageReference tag,
|
||||
String os, @Nullable String architecture, @Nullable String variant, List<LayerId> existingLayers,
|
||||
List<Layer> newLayers) {
|
||||
this.objectMapper = objectMapper;
|
||||
this.imageConfig = imageConfig;
|
||||
this.createDate = createDate;
|
||||
@@ -117,7 +119,7 @@ public class ImageArchive implements TarArchive {
|
||||
* Return the tag of the archive.
|
||||
* @return the tag
|
||||
*/
|
||||
public ImageReference getTag() {
|
||||
public @Nullable ImageReference getTag() {
|
||||
return this.tag;
|
||||
}
|
||||
|
||||
@@ -257,9 +259,9 @@ public class ImageArchive implements TarArchive {
|
||||
|
||||
private ImageConfig config;
|
||||
|
||||
private Instant createDate;
|
||||
private @Nullable Instant createDate;
|
||||
|
||||
private ImageReference tag;
|
||||
private @Nullable ImageReference tag;
|
||||
|
||||
private final List<Layer> newLayers = new ArrayList<>();
|
||||
|
||||
|
||||
+8
-1
@@ -24,6 +24,7 @@ import java.util.List;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.json.MappedObject;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Image archive index information as provided by {@code index.json}.
|
||||
@@ -42,10 +43,16 @@ public class ImageArchiveIndex extends MappedObject {
|
||||
|
||||
protected ImageArchiveIndex(JsonNode node) {
|
||||
super(node, MethodHandles.lookup());
|
||||
this.schemaVersion = valueAt("/schemaVersion", Integer.class);
|
||||
this.schemaVersion = extractSchemaVersion();
|
||||
this.manifests = childrenAt("/manifests", BlobReference::new);
|
||||
}
|
||||
|
||||
private Integer extractSchemaVersion() {
|
||||
Integer result = valueAt("/schemaVersion", Integer.class);
|
||||
Assert.state(result != null, "'result' must not be null");
|
||||
return result;
|
||||
}
|
||||
|
||||
public Integer getSchemaVersion() {
|
||||
return this.schemaVersion;
|
||||
}
|
||||
|
||||
+6
-4
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.boot.buildpack.platform.docker.type;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -41,7 +43,7 @@ public class ImageName {
|
||||
|
||||
private final String string;
|
||||
|
||||
ImageName(String domain, String path) {
|
||||
ImageName(@Nullable String domain, String path) {
|
||||
Assert.hasText(path, "'path' must not be empty");
|
||||
this.domain = getDomainOrDefault(domain);
|
||||
this.name = getNameWithDefaultPath(this.domain, path);
|
||||
@@ -65,7 +67,7 @@ public class ImageName {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
@@ -100,7 +102,7 @@ public class ImageName {
|
||||
return this.string;
|
||||
}
|
||||
|
||||
private String getDomainOrDefault(String domain) {
|
||||
private String getDomainOrDefault(@Nullable String domain) {
|
||||
if (domain == null || LEGACY_DOMAIN.equals(domain)) {
|
||||
return DEFAULT_DOMAIN;
|
||||
}
|
||||
@@ -135,7 +137,7 @@ public class ImageName {
|
||||
return new ImageName(domain, path);
|
||||
}
|
||||
|
||||
static String parseDomain(String value) {
|
||||
static @Nullable String parseDomain(String value) {
|
||||
int firstSlash = value.indexOf('/');
|
||||
String candidate = (firstSlash != -1) ? value.substring(0, firstSlash) : null;
|
||||
if (candidate != null && Regex.DOMAIN.matcher(candidate).matches()) {
|
||||
|
||||
+6
-4
@@ -18,6 +18,8 @@ package org.springframework.boot.buildpack.platform.docker.type;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -30,11 +32,11 @@ public class ImagePlatform {
|
||||
|
||||
private final String os;
|
||||
|
||||
private final String architecture;
|
||||
private final @Nullable String architecture;
|
||||
|
||||
private final String variant;
|
||||
private final @Nullable String variant;
|
||||
|
||||
ImagePlatform(String os, String architecture, String variant) {
|
||||
ImagePlatform(String os, @Nullable String architecture, @Nullable String variant) {
|
||||
Assert.hasText(os, "'os' must not be empty");
|
||||
this.os = os;
|
||||
this.architecture = architecture;
|
||||
@@ -42,7 +44,7 @@ public class ImagePlatform {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+9
-7
@@ -21,6 +21,8 @@ import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
@@ -41,13 +43,13 @@ public final class ImageReference {
|
||||
|
||||
private final ImageName name;
|
||||
|
||||
private final String tag;
|
||||
private final @Nullable String tag;
|
||||
|
||||
private final String digest;
|
||||
private final @Nullable String digest;
|
||||
|
||||
private final String string;
|
||||
|
||||
private ImageReference(ImageName name, String tag, String digest) {
|
||||
private ImageReference(ImageName name, @Nullable String tag, @Nullable String digest) {
|
||||
Assert.notNull(name, "'name' must not be null");
|
||||
this.name = name;
|
||||
this.tag = tag;
|
||||
@@ -77,7 +79,7 @@ public final class ImageReference {
|
||||
* Return the tag from the reference or {@code null}.
|
||||
* @return the referenced tag
|
||||
*/
|
||||
public String getTag() {
|
||||
public @Nullable String getTag() {
|
||||
return this.tag;
|
||||
}
|
||||
|
||||
@@ -85,12 +87,12 @@ public final class ImageReference {
|
||||
* Return the digest from the reference or {@code null}.
|
||||
* @return the referenced digest
|
||||
*/
|
||||
public String getDigest() {
|
||||
public @Nullable String getDigest() {
|
||||
return this.digest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
@@ -124,7 +126,7 @@ public final class ImageReference {
|
||||
return buildString(this.name.toLegacyString(), this.tag, this.digest);
|
||||
}
|
||||
|
||||
private String buildString(String name, String tag, String digest) {
|
||||
private String buildString(String name, @Nullable String tag, @Nullable String digest) {
|
||||
StringBuilder string = new StringBuilder(name);
|
||||
if (tag != null) {
|
||||
string.append(":").append(tag);
|
||||
|
||||
+3
-1
@@ -18,6 +18,8 @@ package org.springframework.boot.buildpack.platform.docker.type;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -57,7 +59,7 @@ public final class LayerId {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+11
-3
@@ -22,8 +22,10 @@ import java.lang.invoke.MethodHandles;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.json.MappedObject;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A manifest as defined in {@code application/vnd.docker.distribution.manifest} or
|
||||
@@ -38,22 +40,28 @@ public class Manifest extends MappedObject {
|
||||
|
||||
private final Integer schemaVersion;
|
||||
|
||||
private final String mediaType;
|
||||
private final @Nullable String mediaType;
|
||||
|
||||
private final List<BlobReference> layers;
|
||||
|
||||
protected Manifest(JsonNode node) {
|
||||
super(node, MethodHandles.lookup());
|
||||
this.schemaVersion = valueAt("/schemaVersion", Integer.class);
|
||||
this.schemaVersion = extractSchemaVersion();
|
||||
this.mediaType = valueAt("/mediaType", String.class);
|
||||
this.layers = childrenAt("/layers", BlobReference::new);
|
||||
}
|
||||
|
||||
private Integer extractSchemaVersion() {
|
||||
Integer result = valueAt("/schemaVersion", Integer.class);
|
||||
Assert.state(result != null, "'result' must not be null");
|
||||
return result;
|
||||
}
|
||||
|
||||
public Integer getSchemaVersion() {
|
||||
return this.schemaVersion;
|
||||
}
|
||||
|
||||
public String getMediaType() {
|
||||
public @Nullable String getMediaType() {
|
||||
return this.mediaType;
|
||||
}
|
||||
|
||||
|
||||
+11
-3
@@ -23,8 +23,10 @@ import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.json.MappedObject;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A distribution manifest list as defined in
|
||||
@@ -39,22 +41,28 @@ public class ManifestList extends MappedObject {
|
||||
|
||||
private final Integer schemaVersion;
|
||||
|
||||
private final String mediaType;
|
||||
private final @Nullable String mediaType;
|
||||
|
||||
private final List<BlobReference> manifests;
|
||||
|
||||
protected ManifestList(JsonNode node) {
|
||||
super(node, MethodHandles.lookup());
|
||||
this.schemaVersion = valueAt("/schemaVersion", Integer.class);
|
||||
this.schemaVersion = extractSchemaVersion();
|
||||
this.mediaType = valueAt("/mediaType", String.class);
|
||||
this.manifests = childrenAt("/manifests", BlobReference::new);
|
||||
}
|
||||
|
||||
private Integer extractSchemaVersion() {
|
||||
Integer result = valueAt("/schemaVersion", Integer.class);
|
||||
Assert.state(result != null, "'result' must not be null");
|
||||
return result;
|
||||
}
|
||||
|
||||
public Integer getSchemaVersion() {
|
||||
return this.schemaVersion;
|
||||
}
|
||||
|
||||
public String getMediaType() {
|
||||
public @Nullable String getMediaType() {
|
||||
return this.mediaType;
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -22,6 +22,8 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HexFormat;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -39,7 +41,7 @@ public final class VolumeName {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+3
@@ -17,4 +17,7 @@
|
||||
/**
|
||||
* Docker types.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.buildpack.platform.docker.type;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
+3
-1
@@ -24,6 +24,8 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
@@ -137,7 +139,7 @@ public class InspectedContent implements Content {
|
||||
|
||||
private OutputStream delegate;
|
||||
|
||||
private File tempFile;
|
||||
private @Nullable File tempFile;
|
||||
|
||||
private final byte[] singleByteBuffer = new byte[0];
|
||||
|
||||
|
||||
+3
@@ -17,4 +17,7 @@
|
||||
/**
|
||||
* IO classes and utilities.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.buildpack.platform.io;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
+2
-1
@@ -25,6 +25,7 @@ import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Utility class that allows JSON to be parsed and processed as it's received.
|
||||
@@ -78,7 +79,7 @@ public class JsonStream {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T read(JsonParser parser, Class<T> type) throws IOException {
|
||||
private <T> @Nullable T read(JsonParser parser, Class<T> type) throws IOException {
|
||||
if (ObjectNode.class.isAssignableFrom(type)) {
|
||||
ObjectNode node = this.objectMapper.readTree(parser);
|
||||
if (node == null || node.isMissingNode() || node.isEmpty()) {
|
||||
|
||||
+8
-6
@@ -32,6 +32,7 @@ import java.util.function.Function;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StreamUtils;
|
||||
@@ -74,7 +75,7 @@ public class MappedObject {
|
||||
* @param type the desired type. May be a simple JSON type or an interface
|
||||
* @return the value
|
||||
*/
|
||||
protected <T> T valueAt(String expression, Class<T> type) {
|
||||
protected <T> @Nullable T valueAt(String expression, Class<T> type) {
|
||||
return valueAt(this, this.node, this.lookup, expression, type);
|
||||
}
|
||||
|
||||
@@ -104,7 +105,7 @@ public class MappedObject {
|
||||
* @return a list of children
|
||||
* @since 3.2.6
|
||||
*/
|
||||
protected <T> List<T> childrenAt(String expression, Function<JsonNode, T> factory) {
|
||||
protected <T> List<T> childrenAt(@Nullable String expression, Function<JsonNode, T> factory) {
|
||||
JsonNode node = (expression != null) ? this.node.at(expression) : this.node;
|
||||
if (node.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
@@ -120,13 +121,14 @@ public class MappedObject {
|
||||
return (T) handler.root;
|
||||
}
|
||||
|
||||
protected static <T> T valueAt(Object proxy, String expression, Class<T> type) {
|
||||
protected static <T> @Nullable T valueAt(Object proxy, String expression, Class<T> type) {
|
||||
MappedInvocationHandler handler = (MappedInvocationHandler) Proxy.getInvocationHandler(proxy);
|
||||
return valueAt(handler.root, handler.node, handler.lookup, expression, type);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T valueAt(MappedObject root, JsonNode node, Lookup lookup, String expression, Class<T> type) {
|
||||
private static <T> @Nullable T valueAt(MappedObject root, JsonNode node, Lookup lookup, String expression,
|
||||
Class<T> type) {
|
||||
JsonNode result = node.at(expression);
|
||||
if (result.isMissingNode() && expression.startsWith("/") && expression.length() > 1
|
||||
&& Character.isLowerCase(expression.charAt(1))) {
|
||||
@@ -233,7 +235,7 @@ public class MappedObject {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
Class<?> declaringClass = method.getDeclaringClass();
|
||||
if (method.isDefault()) {
|
||||
Lookup lookup = this.lookup.in(declaringClass);
|
||||
@@ -262,7 +264,7 @@ public class MappedObject {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private Object valueForProperty(String name, Class<?> type) {
|
||||
private @Nullable Object valueForProperty(String name, Class<?> type) {
|
||||
return valueAt(this.root, this.node, this.lookup, "/" + name, type);
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -17,4 +17,7 @@
|
||||
/**
|
||||
* Utilities and classes for JSON processing.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.buildpack.platform.json;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
+6
-4
@@ -21,6 +21,8 @@ import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Abstract base class for custom socket implementation.
|
||||
*
|
||||
@@ -57,22 +59,22 @@ class AbstractSocket extends Socket {
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress getInetAddress() {
|
||||
public @Nullable InetAddress getInetAddress() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetAddress getLocalAddress() {
|
||||
public @Nullable InetAddress getLocalAddress() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getLocalSocketAddress() {
|
||||
public @Nullable SocketAddress getLocalSocketAddress() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress getRemoteSocketAddress() {
|
||||
public @Nullable SocketAddress getRemoteSocketAddress() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -17,4 +17,7 @@
|
||||
/**
|
||||
* Low-level {@link java.net.Socket} implementations required for local Docker access.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.buildpack.platform.socket;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
+3
-1
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.boot.buildpack.platform.system;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Provides access to environment variable values.
|
||||
*
|
||||
@@ -38,6 +40,6 @@ public interface Environment {
|
||||
* @return the string value of the variable, or {@code null} if the variable is not
|
||||
* defined in the environment
|
||||
*/
|
||||
String get(String name);
|
||||
@Nullable String get(String name);
|
||||
|
||||
}
|
||||
|
||||
+3
@@ -17,4 +17,7 @@
|
||||
/**
|
||||
* System abstractions.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.boot.buildpack.platform.system;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@@ -92,4 +92,5 @@
|
||||
<suppress files="LambdaSafe\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
|
||||
<suppress files="ConditionMessage\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
|
||||
<suppress files="EntityManagerFactoryBuilder\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
|
||||
<suppress files="DockerApi\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
|
||||
</suppressions>
|
||||
|
||||
Reference in New Issue
Block a user