Write buildpack directories to builder layer

When a custom buildpack is provided for image building, the contents
of the buildpack directory, tgz file, or image are copied as tar
entries to a new layer in the ephemeral builder image. Prior to this
commit, only file entries from the buildpack source were copied as
builder layer tar entries; intermediate directory entries from the
source were not copied. This results in directories being created in
the builder container using default permissions. This worked on most
Linux-like OSs where the default permissions allow others-read
access. On some OSs like Arch Linux where the default directory
permissions do not allow others-read, this prevented the lifecycle
processes from reading the buildpack files.

This commit explicitly creates all intermediate directory tar entries
in the builder image layer to ensure that the buildpack directories
and files can be read by the lifecycle processes.

Fixes gh-26658
This commit is contained in:
Scott Frederick
2021-06-02 14:06:54 -05:00
parent e2cba40db0
commit f560e86f03
11 changed files with 172 additions and 36 deletions

View File

@@ -42,6 +42,7 @@ import org.junit.jupiter.api.condition.OS;
import org.springframework.boot.buildpack.platform.docker.DockerApi;
import org.springframework.boot.buildpack.platform.docker.type.ImageName;
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
import org.springframework.boot.buildpack.platform.io.FilePermissions;
import org.springframework.boot.gradle.junit.GradleCompatibility;
import org.springframework.boot.gradle.testkit.GradleBuild;
import org.springframework.boot.testsupport.testcontainers.DisabledIfDockerUnavailable;
@@ -312,8 +313,14 @@ class BootBuildImageIntegrationTests {
}
private void writeBuildpackContent() throws IOException {
FileAttribute<Set<PosixFilePermission>> dirAttribute = PosixFilePermissions
.asFileAttribute(PosixFilePermissions.fromString("rwxr-xr-x"));
FileAttribute<Set<PosixFilePermission>> execFileAttribute = PosixFilePermissions
.asFileAttribute(PosixFilePermissions.fromString("rwxrwxrwx"));
File buildpackDir = new File(this.gradleBuild.getProjectDir(), "buildpack/hello-world");
buildpackDir.mkdirs();
Files.createDirectories(buildpackDir.toPath(), dirAttribute);
File binDir = new File(buildpackDir, "bin");
Files.createDirectories(binDir.toPath(), dirAttribute);
File descriptor = new File(buildpackDir, "buildpack.toml");
try (PrintWriter writer = new PrintWriter(new FileWriter(descriptor))) {
writer.println("api = \"0.2\"");
@@ -325,17 +332,13 @@ class BootBuildImageIntegrationTests {
writer.println("[[stacks]]\n");
writer.println("id = \"io.buildpacks.stacks.bionic\"");
}
File binDir = new File(buildpackDir, "bin");
binDir.mkdirs();
FileAttribute<Set<PosixFilePermission>> attribute = PosixFilePermissions
.asFileAttribute(PosixFilePermissions.fromString("rwxrwxrwx"));
File detect = Files.createFile(Paths.get(binDir.getAbsolutePath(), "detect"), attribute).toFile();
File detect = Files.createFile(Paths.get(binDir.getAbsolutePath(), "detect"), execFileAttribute).toFile();
try (PrintWriter writer = new PrintWriter(new FileWriter(detect))) {
writer.println("#!/usr/bin/env bash");
writer.println("set -eo pipefail");
writer.println("exit 0");
}
File build = Files.createFile(Paths.get(binDir.getAbsolutePath(), "build"), attribute).toFile();
File build = Files.createFile(Paths.get(binDir.getAbsolutePath(), "build"), execFileAttribute).toFile();
try (PrintWriter writer = new PrintWriter(new FileWriter(build))) {
writer.println("#!/usr/bin/env bash");
writer.println("set -eo pipefail");
@@ -349,16 +352,33 @@ class BootBuildImageIntegrationTests {
Path tarGzipPath = Paths.get(this.gradleBuild.getProjectDir().getAbsolutePath(), "hello-world.tgz");
try (TarArchiveOutputStream tar = new TarArchiveOutputStream(
new GzipCompressorOutputStream(Files.newOutputStream(Files.createFile(tarGzipPath))))) {
writeFileToTar(tar, new File(this.gradleBuild.getProjectDir(), "buildpack/hello-world/buildpack.toml"),
"buildpack.toml", 0644);
writeFileToTar(tar, new File(this.gradleBuild.getProjectDir(), "buildpack/hello-world/bin/detect"),
"bin/detect", 0777);
writeFileToTar(tar, new File(this.gradleBuild.getProjectDir(), "buildpack/hello-world/bin/build"),
"bin/build", 0777);
File buildpackDir = new File(this.gradleBuild.getProjectDir(), "buildpack/hello-world");
writeDirectoryToTar(tar, buildpackDir, buildpackDir.getAbsolutePath());
}
}
private void writeFileToTar(TarArchiveOutputStream tar, File file, String name, int mode) throws IOException {
private void writeDirectoryToTar(TarArchiveOutputStream tar, File dir, String baseDirPath) throws IOException {
for (File file : dir.listFiles()) {
String name = file.getAbsolutePath().replace(baseDirPath, "");
int mode = FilePermissions.umaskForPath(file.toPath());
if (file.isDirectory()) {
writeTarEntry(tar, name + "/", mode);
writeDirectoryToTar(tar, file, baseDirPath);
}
else {
writeTarEntry(tar, file, name, mode);
}
}
}
private void writeTarEntry(TarArchiveOutputStream tar, String name, int mode) throws IOException {
TarArchiveEntry entry = new TarArchiveEntry(name);
entry.setMode(mode);
tar.putArchiveEntry(entry);
tar.closeArchiveEntry();
}
private void writeTarEntry(TarArchiveOutputStream tar, File file, String name, int mode) throws IOException {
TarArchiveEntry entry = new TarArchiveEntry(file, name);
entry.setMode(mode);
tar.putArchiveEntry(entry);