Skip to content

Commit b676f7e

Browse files
committed
Fix TempDirForURL leaks for --build-context and ADD git
Signed-off-by: Jan Rodák <hony.com@seznam.cz>
1 parent fc53f95 commit b676f7e

5 files changed

Lines changed: 72 additions & 21 deletions

File tree

add.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,12 +602,12 @@ func (b *Builder) Add(destination string, extract bool, options AddAndCopyOption
602602
go func() {
603603
defer wg.Done()
604604
defer pipeWriter.Close()
605-
// TODO: the returned cloneDir is never cleaned up, leaking disk space.
606605
var cloneDir, subdir string
607606
cloneDir, subdir, getErr = define.TempDirForURL(tmpdir.GetTempDir(), "", src)
608607
if getErr != nil {
609608
return
610609
}
610+
defer os.RemoveAll(cloneDir)
611611
getOptions := copier.GetOptions{
612612
UIDMap: srcUIDMap,
613613
GIDMap: srcGIDMap,

define/build.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ type AdditionalBuildContext struct {
2626
// from an external tar archive. This will be populated only if the
2727
// build context was a URL and its contents have been downloaded.
2828
DownloadedCache string
29+
// DownloadedTempDir is the TempDirForURL root to remove when the build ends.
30+
// It may differ from DownloadedCache, which is the path inside that tree.
31+
DownloadedTempDir string
2932
}
3033

3134
// CommonBuildOptions are resources that can be defined by flags for both buildah from and build

imagebuildah/executor.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,15 @@ func (b *executor) Build(ctx context.Context, stages imagebuilder.Stages) (image
857857
}
858858
cleanupImages = nil
859859

860+
for _, additionalBuildContext := range b.additionalBuildContexts {
861+
if additionalBuildContext.DownloadedTempDir != "" {
862+
if err := os.RemoveAll(additionalBuildContext.DownloadedTempDir); err != nil {
863+
logrus.Debugf("Failed to cleanup additional build context temp dir %q: %v", additionalBuildContext.DownloadedTempDir, err)
864+
lastErr = err
865+
}
866+
}
867+
}
868+
860869
if b.rusageLogFile != nil && b.rusageLogFile != b.out {
861870
// we deliberately ignore the error here, as this
862871
// function can be called multiple times

imagebuildah/stage_executor.go

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -512,18 +512,14 @@ func (s *stageExecutor) performCopy(excludes []string, copies ...imagebuilder.Co
512512
// additional context contains a tar file
513513
// so download and explode tar to buildah
514514
// temp and point context to that.
515-
// TODO: the returned path is never cleaned up, leaking disk space.
516515
path, subdir, err := define.TempDirForURL(tmpdir.GetTempDir(), internal.BuildahExternalArtifactsDir, additionalBuildContext.Value)
517516
if err != nil {
518517
return fmt.Errorf("unable to download context from external source %q: %w", additionalBuildContext.Value, err)
519518
}
520-
// point context dir to the extracted path
521-
contextDir = filepath.Join(path, subdir)
522-
// populate cache for next RUN step
523-
additionalBuildContext.DownloadedCache = contextDir
524-
} else {
525-
contextDir = additionalBuildContext.DownloadedCache
519+
additionalBuildContext.DownloadedTempDir = path
520+
additionalBuildContext.DownloadedCache = filepath.Join(path, subdir)
526521
}
522+
contextDir = additionalBuildContext.DownloadedCache
527523
} else {
528524
// This points to a path on the filesystem
529525
// Check to see if there's a .containerignore
@@ -733,18 +729,14 @@ func (s *stageExecutor) runStageMountPoints(mountList []string) (map[string]inte
733729
// additional context contains a tar file
734730
// so download and explode tar to buildah
735731
// temp and point context to that.
736-
// TODO: the returned path is never cleaned up, leaking disk space.
737732
path, subdir, err := define.TempDirForURL(tmpdir.GetTempDir(), internal.BuildahExternalArtifactsDir, additionalBuildContext.Value)
738733
if err != nil {
739734
return nil, fmt.Errorf("unable to download context from external source %q: %w", additionalBuildContext.Value, err)
740735
}
741-
// point context dir to the extracted path
742-
mountPoint = filepath.Join(path, subdir)
743-
// populate cache for next RUN step
744-
additionalBuildContext.DownloadedCache = mountPoint
745-
} else {
746-
mountPoint = additionalBuildContext.DownloadedCache
736+
additionalBuildContext.DownloadedTempDir = path
737+
additionalBuildContext.DownloadedCache = filepath.Join(path, subdir)
747738
}
739+
mountPoint = additionalBuildContext.DownloadedCache
748740
}
749741
stageMountPoints[from] = internal.StageMountDetails{
750742
IsAdditionalBuildContext: true,

tests/bud.bats

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3396,6 +3396,13 @@ _EOF
33963396
done
33973397
}
33983398

3399+
# assert_no_tempdir_leaks checks $1 has no leftover temp download directories.
3400+
function assert_no_tempdir_leaks() {
3401+
local tmpdir=$1
3402+
local leftovers=$(find $tmpdir -mindepth 1 -maxdepth 1 -print)
3403+
assert "$leftovers" == ""
3404+
}
3405+
33993406
# Helper function for several of the tests which pull from http.
34003407
#
34013408
# Usage: _test_http SUBDIRECTORY URL_PATH [EXTRA ARGS]
@@ -3416,11 +3423,14 @@ function _test_http() {
34163423

34173424
starthttpd "$BUDFILES/$testdir"
34183425
target=scratch-image
3419-
run_buildah build $WITH_POLICY_JSON \
3426+
local tmpdir=${TEST_SCRATCH_DIR}/tmpdir
3427+
mkdir -p $tmpdir
3428+
TMPDIR=$tmpdir run_buildah build $WITH_POLICY_JSON \
34203429
-t ${target} \
34213430
"$@" \
34223431
http://0.0.0.0:${HTTP_SERVER_PORT}/$urlpath
34233432
stophttpd
3433+
assert_no_tempdir_leaks $tmpdir
34243434
run_buildah from ${target}
34253435
}
34263436

@@ -3500,9 +3510,7 @@ function validate_instance_compression {
35003510
mkdir -p "${tmpdir}"
35013511
TMPDIR="${tmpdir}" run_buildah build $WITH_POLICY_JSON -t ${target} "${gitrepo}"
35023512
run_buildah from "${target}"
3503-
run find "${tmpdir}" -type d -print
3504-
echo "$output"
3505-
test "${#lines[*]}" -le 2
3513+
assert_no_tempdir_leaks $tmpdir
35063514
}
35073515

35083516
@test "bud-git-context-failure" {
@@ -8180,7 +8188,10 @@ _EOF
81808188
file.txt
81818189
_EOF
81828190

8183-
run_buildah build -f $contextdir/Dockerfile -t add-git-ignoring-files $contextdir
8191+
local tmpdir=${TEST_SCRATCH_DIR}/tmpdir
8192+
mkdir -p $tmpdir
8193+
TMPDIR=$tmpdir run_buildah build -f $contextdir/Dockerfile -t add-git-ignoring-files $contextdir
8194+
assert_no_tempdir_leaks $tmpdir
81848195
}
81858196

81868197
@test "bud with ADD with git repository source escape directory" {
@@ -8213,8 +8224,11 @@ ADD http://0.0.0.0:${HTTP_SERVER_PORT}/git/test-bug.git#main:proj${secretdir} /m
82138224
RUN cat /mydir/secretfile
82148225
_EOF
82158226

8216-
run_buildah 125 build -f $contextdir/Containerfile -t escape-image --no-cache $contextdir
8227+
local tmpdir=${TEST_SCRATCH_DIR}/tmpdir
8228+
mkdir -p $tmpdir
8229+
TMPDIR=$tmpdir run_buildah 125 build -f $contextdir/Containerfile -t escape-image --no-cache $contextdir
82178230
assert "$output" !~ "mysecret"
8231+
assert_no_tempdir_leaks $tmpdir
82188232
}
82198233

82208234
@test "bud with http context symlinked Dockerfile does not write through symlink on fallback" {
@@ -10508,3 +10522,36 @@ _EOF
1050810522
# Verify final image IDs match (complete cache hit)
1050910523
assert "$first_build_final_image_id" "==" "$second_build_final_image_id" "final image ID should match when cache is fully reused"
1051010524
}
10525+
10526+
@test "build-with-additional-build-context URL temp dirs are cleaned up" {
10527+
_prefetch alpine
10528+
10529+
local tmpdir=${TEST_SCRATCH_DIR}/tmpdir
10530+
mkdir -p $tmpdir
10531+
local tarball_content=${TEST_SCRATCH_DIR}/tarball-src
10532+
mkdir -p $tarball_content/myproject
10533+
echo secret-content > $tarball_content/myproject/file.txt
10534+
local tarball=${TEST_SCRATCH_DIR}/context.tar.gz
10535+
tar -czf $tarball -C $tarball_content .
10536+
local httpdir=${TEST_SCRATCH_DIR}/http
10537+
mkdir -p $httpdir
10538+
cp $tarball $httpdir/context.tar.gz
10539+
10540+
starthttpd $httpdir
10541+
local contextdir=${TEST_SCRATCH_DIR}/bud/context
10542+
mkdir -p $contextdir
10543+
cat > $contextdir/Dockerfile << _EOF
10544+
FROM alpine
10545+
COPY --from=ctx myproject/file.txt /file.txt
10546+
RUN cat /file.txt
10547+
RUN --mount=type=bind,src=myproject,from=ctx2,target=/mnt,z ls /mnt
10548+
_EOF
10549+
10550+
TMPDIR=$tmpdir run_buildah build $WITH_POLICY_JSON \
10551+
--build-context ctx=http://0.0.0.0:${HTTP_SERVER_PORT}/context.tar.gz \
10552+
--build-context ctx2=http://0.0.0.0:${HTTP_SERVER_PORT}/context.tar.gz \
10553+
-t source -f $contextdir/Dockerfile $contextdir
10554+
expect_output --substring "secret-content"
10555+
expect_output --substring "file.txt"
10556+
assert_no_tempdir_leaks $tmpdir
10557+
}

0 commit comments

Comments
 (0)