Support podman for building images

Closes gh-30196
pull/30406/head
Scott Frederick 3 years ago
parent 7ad538cd84
commit de321b00b7

@ -187,7 +187,7 @@ public class DockerApi {
listener.onUpdate(event); listener.onUpdate(event);
}); });
} }
return inspect(reference.withDigest(digestCapture.getCapturedDigest())); return inspect(reference);
} }
finally { finally {
listener.onFinish(); listener.onFinish();

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -61,6 +61,8 @@ public class ImageArchive implements TarArchive {
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ISO_ZONED_DATE_TIME private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ISO_ZONED_DATE_TIME
.withZone(ZoneOffset.UTC); .withZone(ZoneOffset.UTC);
private static final String EMPTY_LAYER_NAME_PREFIX = "blank_";
private static final IOConsumer<Update> NO_UPDATES = (update) -> { private static final IOConsumer<Update> NO_UPDATES = (update) -> {
}; };
@ -125,6 +127,9 @@ public class ImageArchive implements TarArchive {
} }
private List<LayerId> writeLayers(Layout writer) throws IOException { private List<LayerId> writeLayers(Layout writer) throws IOException {
for (int i = 0; i < this.existingLayers.size(); i++) {
writeEmptyLayer(writer, EMPTY_LAYER_NAME_PREFIX + i);
}
List<LayerId> writtenLayers = new ArrayList<>(); List<LayerId> writtenLayers = new ArrayList<>();
for (Layer layer : this.newLayers) { for (Layer layer : this.newLayers) {
writtenLayers.add(writeLayer(writer, layer)); writtenLayers.add(writeLayer(writer, layer));
@ -132,9 +137,13 @@ public class ImageArchive implements TarArchive {
return Collections.unmodifiableList(writtenLayers); return Collections.unmodifiableList(writtenLayers);
} }
private void writeEmptyLayer(Layout writer, String name) throws IOException {
writer.file(name, Owner.ROOT, Content.of(""));
}
private LayerId writeLayer(Layout writer, Layer layer) throws IOException { private LayerId writeLayer(Layout writer, Layer layer) throws IOException {
LayerId id = layer.getId(); LayerId id = layer.getId();
writer.file("/" + id.getHash() + ".tar", Owner.ROOT, layer); writer.file(id.getHash() + ".tar", Owner.ROOT, layer);
return id; return id;
} }
@ -144,7 +153,7 @@ public class ImageArchive implements TarArchive {
String json = this.objectMapper.writeValueAsString(config).replace("\r\n", "\n"); String json = this.objectMapper.writeValueAsString(config).replace("\r\n", "\n");
MessageDigest digest = MessageDigest.getInstance("SHA-256"); MessageDigest digest = MessageDigest.getInstance("SHA-256");
InspectedContent content = InspectedContent.of(Content.of(json), digest::update); InspectedContent content = InspectedContent.of(Content.of(json), digest::update);
String name = "/" + LayerId.ofSha256Digest(digest.digest()).getHash() + ".json"; String name = LayerId.ofSha256Digest(digest.digest()).getHash() + ".json";
writer.file(name, Owner.ROOT, content); writer.file(name, Owner.ROOT, content);
return name; return name;
} }
@ -187,7 +196,7 @@ public class ImageArchive implements TarArchive {
private void writeManifest(Layout writer, String config, List<LayerId> writtenLayers) throws IOException { private void writeManifest(Layout writer, String config, List<LayerId> writtenLayers) throws IOException {
ArrayNode manifest = createManifest(config, writtenLayers); ArrayNode manifest = createManifest(config, writtenLayers);
String manifestJson = this.objectMapper.writeValueAsString(manifest); String manifestJson = this.objectMapper.writeValueAsString(manifest);
writer.file("/manifest.json", Owner.ROOT, Content.of(manifestJson)); writer.file("manifest.json", Owner.ROOT, Content.of(manifestJson));
} }
private ArrayNode createManifest(String config, List<LayerId> writtenLayers) { private ArrayNode createManifest(String config, List<LayerId> writtenLayers) {
@ -204,7 +213,7 @@ public class ImageArchive implements TarArchive {
private ArrayNode getManifestLayers(List<LayerId> writtenLayers) { private ArrayNode getManifestLayers(List<LayerId> writtenLayers) {
ArrayNode layers = this.objectMapper.createArrayNode(); ArrayNode layers = this.objectMapper.createArrayNode();
for (int i = 0; i < this.existingLayers.size(); i++) { for (int i = 0; i < this.existingLayers.size(); i++) {
layers.add(""); layers.add(EMPTY_LAYER_NAME_PREFIX + i);
} }
writtenLayers.stream().map((id) -> id.getHash() + ".tar").forEach(layers::add); writtenLayers.stream().map((id) -> id.getHash() + ".tar").forEach(layers::add);
return layers; return layers;

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2021 the original author or authors. * Copyright 2012-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -58,6 +58,8 @@ import static org.assertj.core.api.Assertions.entry;
*/ */
class EphemeralBuilderTests extends AbstractJsonTests { class EphemeralBuilderTests extends AbstractJsonTests {
private static final int EXISTING_IMAGE_LAYER_COUNT = 43;
@TempDir @TempDir
File temp; File temp;
@ -131,7 +133,7 @@ class EphemeralBuilderTests extends AbstractJsonTests {
void getArchiveContainsEnvLayer() throws Exception { void getArchiveContainsEnvLayer() throws Exception {
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata, EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata,
this.creator, this.env, this.buildpacks); this.creator, this.env, this.buildpacks);
File directory = unpack(getLayer(builder.getArchive(), 0), "env"); File directory = unpack(getLayer(builder.getArchive(), EXISTING_IMAGE_LAYER_COUNT), "env");
assertThat(new File(directory, "platform/env/spring")).usingCharset(StandardCharsets.UTF_8).hasContent("boot"); assertThat(new File(directory, "platform/env/spring")).usingCharset(StandardCharsets.UTF_8).hasContent("boot");
assertThat(new File(directory, "platform/env/empty")).usingCharset(StandardCharsets.UTF_8).hasContent(""); assertThat(new File(directory, "platform/env/empty")).usingCharset(StandardCharsets.UTF_8).hasContent("");
} }
@ -154,10 +156,13 @@ class EphemeralBuilderTests extends AbstractJsonTests {
this.buildpacks = Buildpacks.of(buildpackList); this.buildpacks = Buildpacks.of(buildpackList);
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata, EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata,
this.creator, null, this.buildpacks); this.creator, null, this.buildpacks);
assertBuildpackLayerContent(builder, 0, "/cnb/buildpacks/example_buildpack1/0.0.1/buildpack.toml"); assertBuildpackLayerContent(builder, EXISTING_IMAGE_LAYER_COUNT,
assertBuildpackLayerContent(builder, 1, "/cnb/buildpacks/example_buildpack2/0.0.2/buildpack.toml"); "/cnb/buildpacks/example_buildpack1/0.0.1/buildpack.toml");
assertBuildpackLayerContent(builder, 2, "/cnb/buildpacks/example_buildpack3/0.0.3/buildpack.toml"); assertBuildpackLayerContent(builder, EXISTING_IMAGE_LAYER_COUNT + 1,
File orderDirectory = unpack(getLayer(builder.getArchive(), 3), "order"); "/cnb/buildpacks/example_buildpack2/0.0.2/buildpack.toml");
assertBuildpackLayerContent(builder, EXISTING_IMAGE_LAYER_COUNT + 2,
"/cnb/buildpacks/example_buildpack3/0.0.3/buildpack.toml");
File orderDirectory = unpack(getLayer(builder.getArchive(), EXISTING_IMAGE_LAYER_COUNT + 3), "order");
assertThat(new File(orderDirectory, "cnb/order.toml")).usingCharset(StandardCharsets.UTF_8) assertThat(new File(orderDirectory, "cnb/order.toml")).usingCharset(StandardCharsets.UTF_8)
.hasContent(content("order.toml")); .hasContent(content("order.toml"));
} }

@ -164,8 +164,7 @@ class DockerApiTests {
void pullPullsImageAndProducesEvents() throws Exception { void pullPullsImageAndProducesEvents() throws Exception {
ImageReference reference = ImageReference.of("gcr.io/paketo-buildpacks/builder:base"); ImageReference reference = ImageReference.of("gcr.io/paketo-buildpacks/builder:base");
URI createUri = new URI(IMAGES_URL + "/create?fromImage=gcr.io%2Fpaketo-buildpacks%2Fbuilder%3Abase"); URI createUri = new URI(IMAGES_URL + "/create?fromImage=gcr.io%2Fpaketo-buildpacks%2Fbuilder%3Abase");
String imageHash = "4acb6bfd6c4f0cabaf7f3690e444afe51f1c7de54d51da7e63fac709c56f1c30"; URI imageUri = new URI(IMAGES_URL + "/gcr.io/paketo-buildpacks/builder:base/json");
URI imageUri = new URI(IMAGES_URL + "/gcr.io/paketo-buildpacks/builder@sha256:" + imageHash + "/json");
given(http().post(eq(createUri), isNull())).willReturn(responseOf("pull-stream.json")); given(http().post(eq(createUri), isNull())).willReturn(responseOf("pull-stream.json"));
given(http().get(imageUri)).willReturn(responseOf("type/image.json")); given(http().get(imageUri)).willReturn(responseOf("type/image.json"));
Image image = this.api.pull(reference, this.pullListener); Image image = this.api.pull(reference, this.pullListener);
@ -180,8 +179,7 @@ class DockerApiTests {
void pullWithRegistryAuthPullsImageAndProducesEvents() throws Exception { void pullWithRegistryAuthPullsImageAndProducesEvents() throws Exception {
ImageReference reference = ImageReference.of("gcr.io/paketo-buildpacks/builder:base"); ImageReference reference = ImageReference.of("gcr.io/paketo-buildpacks/builder:base");
URI createUri = new URI(IMAGES_URL + "/create?fromImage=gcr.io%2Fpaketo-buildpacks%2Fbuilder%3Abase"); URI createUri = new URI(IMAGES_URL + "/create?fromImage=gcr.io%2Fpaketo-buildpacks%2Fbuilder%3Abase");
String imageHash = "4acb6bfd6c4f0cabaf7f3690e444afe51f1c7de54d51da7e63fac709c56f1c30"; URI imageUri = new URI(IMAGES_URL + "/gcr.io/paketo-buildpacks/builder:base/json");
URI imageUri = new URI(IMAGES_URL + "/gcr.io/paketo-buildpacks/builder@sha256:" + imageHash + "/json");
given(http().post(eq(createUri), eq("auth token"))).willReturn(responseOf("pull-stream.json")); given(http().post(eq(createUri), eq("auth token"))).willReturn(responseOf("pull-stream.json"));
given(http().get(imageUri)).willReturn(responseOf("type/image.json")); given(http().get(imageUri)).willReturn(responseOf("type/image.json"));
Image image = this.api.pull(reference, this.pullListener, "auth token"); Image image = this.api.pull(reference, this.pullListener, "auth token");

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -40,6 +40,8 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
class ImageArchiveTests extends AbstractJsonTests { class ImageArchiveTests extends AbstractJsonTests {
private static final int EXISTING_IMAGE_LAYER_COUNT = 46;
@Test @Test
void fromImageWritesToValidArchiveTar() throws Exception { void fromImageWritesToValidArchiveTar() throws Exception {
Image image = Image.of(getContent("image.json")); Image image = Image.of(getContent("image.json"));
@ -51,13 +53,16 @@ class ImageArchiveTests extends AbstractJsonTests {
archive.writeTo(outputStream); archive.writeTo(outputStream);
try (TarArchiveInputStream tar = new TarArchiveInputStream( try (TarArchiveInputStream tar = new TarArchiveInputStream(
new ByteArrayInputStream(outputStream.toByteArray()))) { new ByteArrayInputStream(outputStream.toByteArray()))) {
for (int i = 0; i < EXISTING_IMAGE_LAYER_COUNT; i++) {
TarArchiveEntry blankEntry = tar.getNextTarEntry();
assertThat(blankEntry.getName()).isEqualTo("blank_" + i);
}
TarArchiveEntry layerEntry = tar.getNextTarEntry(); TarArchiveEntry layerEntry = tar.getNextTarEntry();
byte[] layerContent = read(tar, layerEntry.getSize()); byte[] layerContent = read(tar, layerEntry.getSize());
TarArchiveEntry configEntry = tar.getNextTarEntry(); TarArchiveEntry configEntry = tar.getNextTarEntry();
byte[] configContent = read(tar, configEntry.getSize()); byte[] configContent = read(tar, configEntry.getSize());
TarArchiveEntry manifestEntry = tar.getNextTarEntry(); TarArchiveEntry manifestEntry = tar.getNextTarEntry();
byte[] manifestContent = read(tar, manifestEntry.getSize()); byte[] manifestContent = read(tar, manifestEntry.getSize());
assertThat(tar.getNextTarEntry()).isNull();
assertExpectedLayer(layerEntry, layerContent); assertExpectedLayer(layerEntry, layerContent);
assertExpectedConfig(configEntry, configContent); assertExpectedConfig(configEntry, configContent);
assertExpectedManifest(manifestEntry, manifestContent); assertExpectedManifest(manifestEntry, manifestContent);
@ -65,7 +70,7 @@ class ImageArchiveTests extends AbstractJsonTests {
} }
private void assertExpectedLayer(TarArchiveEntry entry, byte[] content) throws Exception { private void assertExpectedLayer(TarArchiveEntry entry, byte[] content) throws Exception {
assertThat(entry.getName()).isEqualTo("/bb09e17fd1bd2ee47155f1349645fcd9fff31e1247c7ed99cad469f1c16a4216.tar"); assertThat(entry.getName()).isEqualTo("bb09e17fd1bd2ee47155f1349645fcd9fff31e1247c7ed99cad469f1c16a4216.tar");
try (TarArchiveInputStream tar = new TarArchiveInputStream(new ByteArrayInputStream(content))) { try (TarArchiveInputStream tar = new TarArchiveInputStream(new ByteArrayInputStream(content))) {
TarArchiveEntry contentEntry = tar.getNextTarEntry(); TarArchiveEntry contentEntry = tar.getNextTarEntry();
assertThat(contentEntry.getName()).isEqualTo("/spring/"); assertThat(contentEntry.getName()).isEqualTo("/spring/");
@ -73,14 +78,14 @@ class ImageArchiveTests extends AbstractJsonTests {
} }
private void assertExpectedConfig(TarArchiveEntry entry, byte[] content) throws Exception { private void assertExpectedConfig(TarArchiveEntry entry, byte[] content) throws Exception {
assertThat(entry.getName()).isEqualTo("/682f8d24b9d9c313d1190a0e955dcb5e65ec9beea40420999839c6f0cbb38382.json"); assertThat(entry.getName()).isEqualTo("682f8d24b9d9c313d1190a0e955dcb5e65ec9beea40420999839c6f0cbb38382.json");
String actualJson = new String(content, StandardCharsets.UTF_8); String actualJson = new String(content, StandardCharsets.UTF_8);
String expectedJson = StreamUtils.copyToString(getContent("image-archive-config.json"), StandardCharsets.UTF_8); String expectedJson = StreamUtils.copyToString(getContent("image-archive-config.json"), StandardCharsets.UTF_8);
JSONAssert.assertEquals(expectedJson, actualJson, false); JSONAssert.assertEquals(expectedJson, actualJson, false);
} }
private void assertExpectedManifest(TarArchiveEntry entry, byte[] content) throws Exception { private void assertExpectedManifest(TarArchiveEntry entry, byte[] content) throws Exception {
assertThat(entry.getName()).isEqualTo("/manifest.json"); assertThat(entry.getName()).isEqualTo("manifest.json");
String actualJson = new String(content, StandardCharsets.UTF_8); String actualJson = new String(content, StandardCharsets.UTF_8);
String expectedJson = StreamUtils.copyToString(getContent("image-archive-manifest.json"), String expectedJson = StreamUtils.copyToString(getContent("image-archive-manifest.json"),
StandardCharsets.UTF_8); StandardCharsets.UTF_8);

@ -1,53 +1,53 @@
[ [
{ {
"Config": "/682f8d24b9d9c313d1190a0e955dcb5e65ec9beea40420999839c6f0cbb38382.json", "Config": "682f8d24b9d9c313d1190a0e955dcb5e65ec9beea40420999839c6f0cbb38382.json",
"Layers": [ "Layers": [
"", "blank_0",
"", "blank_1",
"", "blank_2",
"", "blank_3",
"", "blank_4",
"", "blank_5",
"", "blank_6",
"", "blank_7",
"", "blank_8",
"", "blank_9",
"", "blank_10",
"", "blank_11",
"", "blank_12",
"", "blank_13",
"", "blank_14",
"", "blank_15",
"", "blank_16",
"", "blank_17",
"", "blank_18",
"", "blank_19",
"", "blank_20",
"", "blank_21",
"", "blank_22",
"", "blank_23",
"", "blank_24",
"", "blank_25",
"", "blank_26",
"", "blank_27",
"", "blank_28",
"", "blank_29",
"", "blank_30",
"", "blank_31",
"", "blank_32",
"", "blank_33",
"", "blank_34",
"", "blank_35",
"", "blank_36",
"", "blank_37",
"", "blank_38",
"", "blank_39",
"", "blank_40",
"", "blank_41",
"", "blank_42",
"", "blank_43",
"", "blank_44",
"", "blank_45",
"bb09e17fd1bd2ee47155f1349645fcd9fff31e1247c7ed99cad469f1c16a4216.tar" "bb09e17fd1bd2ee47155f1349645fcd9fff31e1247c7ed99cad469f1c16a4216.tar"
], ],
"RepoTags": [ "RepoTags": [

@ -16,7 +16,7 @@ The `bootBuildImage` task requires access to a Docker daemon.
By default, it will communicate with a Docker daemon over a local connection. By default, it will communicate with a Docker daemon over a local connection.
This works with https://docs.docker.com/install/[Docker Engine] on all supported platforms without configuration. This works with https://docs.docker.com/install/[Docker Engine] on all supported platforms without configuration.
Environment variables can be set to configure the `bootBuildImage` task to use the https://minikube.sigs.k8s.io/docs/tasks/docker_daemon/[Docker daemon provided by minikube]. Environment variables can be set to configure the `bootBuildImage` task to use an alternative local or remote connection.
The following table shows the environment variables and their values: The following table shows the environment variables and their values:
|=== |===
@ -32,8 +32,6 @@ The following table shows the environment variables and their values:
| Path to certificate and key files for HTTPS (required if `DOCKER_TLS_VERIFY=1`, ignored otherwise) | Path to certificate and key files for HTTPS (required if `DOCKER_TLS_VERIFY=1`, ignored otherwise)
|=== |===
On Linux and macOS, these environment variables can be set using the command `eval $(minikube docker-env)` after minikube has been started.
Docker daemon connection information can also be provided using `docker` properties in the plugin configuration. Docker daemon connection information can also be provided using `docker` properties in the plugin configuration.
The following table summarizes the available properties: The following table summarizes the available properties:
@ -416,7 +414,14 @@ include::../gradle/packaging/boot-build-image-caches.gradle.kts[tags=caches]
[[build-image.examples.docker]] [[build-image.examples.docker]]
=== Docker Configuration === Docker Configuration
If you need the plugin to communicate with the Docker daemon using a remote connection instead of the default local connection, the connection details can be provided using `docker` properties as shown in the following example: [[build-image.examples.docker.minikube]]
==== Docker Configuration for minikube
The plugin can communicate with the https://minikube.sigs.k8s.io/docs/tasks/docker_daemon/[Docker daemon provided by minikube] instead of the default local connection.
On Linux and macOS, environment variables can be set using the command `eval $(minikube docker-env)` after minikube has been started.
The plugin can also be configured to use the minikube daemon by providing connection details similar to those shown in the following example:
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"] [source,groovy,indent=0,subs="verbatim,attributes",role="primary"]
.Groovy .Groovy
@ -430,6 +435,28 @@ include::../gradle/packaging/boot-build-image-docker-host.gradle[tags=docker-hos
include::../gradle/packaging/boot-build-image-docker-host.gradle.kts[tags=docker-host] include::../gradle/packaging/boot-build-image-docker-host.gradle.kts[tags=docker-host]
---- ----
[[build-image.examples.docker.podman]]
==== Docker Configuration for podman
The plugin can communicate with a https://podman.io/[podman container engine].
The plugin can be configured to use podman local connection by providing connection details similar to those shown in the following example:
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"]
.Groovy
----
include::../gradle/packaging/boot-build-image-docker-host-podman.gradle[tags=docker-host]
----
[source,kotlin,indent=0,subs="verbatim,attributes",role="secondary"]
.Kotlin
----
include::../gradle/packaging/boot-build-image-docker-host-podman.gradle.kts[tags=docker-host]
----
[[build-image.examples.docker.auth]]
==== Docker Configuration for Authentication
If the builder or run image are stored in a private Docker registry that supports user authentication, authentication details can be provided using `docker.builderRegistry` properties as shown in the following example: If the builder or run image are stored in a private Docker registry that supports user authentication, authentication details can be provided using `docker.builderRegistry` properties as shown in the following example:
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"] [source,groovy,indent=0,subs="verbatim,attributes",role="primary"]

@ -0,0 +1,24 @@
plugins {
id 'java'
id 'org.springframework.boot' version '{gradle-project-version}'
}
tasks.named("bootJar") {
mainClass = 'com.example.ExampleApplication'
}
// tag::docker-host[]
tasks.named("bootBuildImage") {
docker {
host = "unix:///run/user/1000/podman/podman.sock"
bindHostToBuilder = true
}
}
// end::docker-host[]
tasks.register("bootBuildImageDocker") {
doFirst {
println("host=${tasks.bootBuildImage.docker.host}")
println("bindHostToBuilder=${tasks.bootBuildImage.docker.bindHostToBuilder}")
}
}

@ -0,0 +1,27 @@
import org.springframework.boot.gradle.tasks.bundling.BootJar
import org.springframework.boot.gradle.tasks.bundling.BootBuildImage
plugins {
java
id("org.springframework.boot") version "{gradle-project-version}"
}
tasks.named<BootJar>("bootJar") {
mainClass.set("com.example.ExampleApplication")
}
// tag::docker-host[]
tasks.named<BootBuildImage>("bootBuildImage") {
docker {
host = "unix:///run/user/1000/podman/podman.sock"
isBindHostToBuilder = true
}
}
// end::docker-host[]
tasks.register("bootBuildImageDocker") {
doFirst {
println("host=${tasks.getByName<BootBuildImage>("bootBuildImage").docker.host}")
println("bindHostToBuilder=${tasks.getByName<BootBuildImage>("bootBuildImage").docker.isBindHostToBuilder}")
}
}

@ -12,7 +12,7 @@ tasks.named("bootBuildImage") {
docker { docker {
host = "tcp://192.168.99.100:2376" host = "tcp://192.168.99.100:2376"
tlsVerify = true tlsVerify = true
certPath = "/home/users/.minikube/certs" certPath = "/home/user/.minikube/certs"
} }
} }
// end::docker-host[] // end::docker-host[]

@ -15,7 +15,7 @@ tasks.named<BootBuildImage>("bootBuildImage") {
docker { docker {
host = "tcp://192.168.99.100:2376" host = "tcp://192.168.99.100:2376"
isTlsVerify = true isTlsVerify = true
certPath = "/home/users/.minikube/certs" certPath = "/home/user/.minikube/certs"
} }
} }
// end::docker-host[] // end::docker-host[]

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2021 the original author or authors. * Copyright 2012-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -279,11 +279,19 @@ class PackagingDocumentationTests {
} }
@TestTemplate @TestTemplate
void bootBuildImageWithDockerHost() { void bootBuildImageWithDockerHostMinikube() {
BuildResult result = this.gradleBuild.script("src/docs/gradle/packaging/boot-build-image-docker-host") BuildResult result = this.gradleBuild.script("src/docs/gradle/packaging/boot-build-image-docker-host")
.build("bootBuildImageDocker"); .build("bootBuildImageDocker");
assertThat(result.getOutput()).contains("host=tcp://192.168.99.100:2376").contains("tlsVerify=true") assertThat(result.getOutput()).contains("host=tcp://192.168.99.100:2376").contains("tlsVerify=true")
.contains("certPath=/home/users/.minikube/certs"); .contains("certPath=/home/user/.minikube/certs");
}
@TestTemplate
void bootBuildImageWithDockerHostPodman() {
BuildResult result = this.gradleBuild.script("src/docs/gradle/packaging/boot-build-image-docker-host-podman")
.build("bootBuildImageDocker");
assertThat(result.getOutput()).contains("host=unix:///run/user/1000/podman/podman.sock")
.contains("bindHostToBuilder=true");
} }
@TestTemplate @TestTemplate

@ -25,7 +25,7 @@ The `build-image` goal requires access to a Docker daemon.
By default, it will communicate with a Docker daemon over a local connection. By default, it will communicate with a Docker daemon over a local connection.
This works with https://docs.docker.com/install/[Docker Engine] on all supported platforms without configuration. This works with https://docs.docker.com/install/[Docker Engine] on all supported platforms without configuration.
Environment variables can be set to configure the `build-image` goal to use the https://minikube.sigs.k8s.io/docs/tasks/docker_daemon/[Docker daemon provided by minikube]. Environment variables can be set to configure the `build-image` goal to use an alternative local or remote connection.
The following table shows the environment variables and their values: The following table shows the environment variables and their values:
|=== |===
@ -41,8 +41,6 @@ The following table shows the environment variables and their values:
| Path to certificate and key files for HTTPS (required if `DOCKER_TLS_VERIFY=1`, ignored otherwise) | Path to certificate and key files for HTTPS (required if `DOCKER_TLS_VERIFY=1`, ignored otherwise)
|=== |===
On Linux and macOS, these environment variables can be set using the command `eval $(minikube docker-env)` after minikube has been started.
Docker daemon connection information can also be provided using `docker` parameters in the plugin configuration. Docker daemon connection information can also be provided using `docker` parameters in the plugin configuration.
The following table summarizes the available parameters: The following table summarizes the available parameters:
@ -372,13 +370,35 @@ include::../maven/packaging-oci-image/caches-pom.xml[tags=caches]
[[build-image.examples.docker]] [[build-image.examples.docker]]
=== Docker Configuration === Docker Configuration
If you need the plugin to communicate with the Docker daemon using a remote connection instead of the default local connection, the connection details can be provided using `docker` parameters as shown in the following example: [[build-image.examples.docker.minikube]]
==== Docker Configuration for minikube
The plugin can communicate with the https://minikube.sigs.k8s.io/docs/tasks/docker_daemon/[Docker daemon provided by minikube] instead of the default local connection.
On Linux and macOS, environment variables can be set using the command `eval $(minikube docker-env)` after minikube has been started.
The plugin can also be configured to use the minikube daemon by providing connection details similar to those shown in the following example:
[source,xml,indent=0,subs="verbatim,attributes",tabsize=4]
----
include::../maven/packaging-oci-image/docker-minikube-pom.xml[tags=docker-minikube]
----
[[build-image.examples.docker.podman]]
==== Docker Configuration for podman
The plugin can communicate with a https://podman.io/[podman container engine].
The plugin can be configured to use podman local connection by providing connection details similar to those shown in the following example:
[source,xml,indent=0,subs="verbatim,attributes",tabsize=4] [source,xml,indent=0,subs="verbatim,attributes",tabsize=4]
---- ----
include::../maven/packaging-oci-image/docker-remote-pom.xml[tags=docker-remote] include::../maven/packaging-oci-image/docker-podman-pom.xml[tags=docker-podman]
---- ----
[[build-image.examples.docker.auth]]
==== Docker Configuration for Authentication
If the builder or run image are stored in a private Docker registry that supports user authentication, authentication details can be provided using `docker.builderRegistry` parameters as shown in the following example: If the builder or run image are stored in a private Docker registry that supports user authentication, authentication details can be provided using `docker.builderRegistry` parameters as shown in the following example:
[source,xml,indent=0,subs="verbatim,attributes",tabsize=4] [source,xml,indent=0,subs="verbatim,attributes",tabsize=4]

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- tag::docker-remote[] --> <!-- tag::docker-minikube[] -->
<project> <project>
<build> <build>
<plugins> <plugins>
@ -17,5 +17,5 @@
</plugins> </plugins>
</build> </build>
</project> </project>
<!-- end::docker-remote[] --> <!-- end::docker-minikube[] -->

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- tag::docker-podman[] -->
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<docker>
<host>unix:///run/user/1000/podman/podman.sock</host>
<bindHostToBuilder>true</bindHostToBuilder>
</docker>
</configuration>
</plugin>
</plugins>
</build>
</project>
<!-- end::docker-podman[] -->
Loading…
Cancel
Save