Skip to content

Commit 62c2c2c

Browse files
committed
wip: apply some cleanups
Signed-off-by: Mateusz Gozdek <mgozdek@microsoft.com>
1 parent 1601fc7 commit 62c2c2c

File tree

1 file changed

+116
-7
lines changed

1 file changed

+116
-7
lines changed

targets/linux/deb/distro/container.go

Lines changed: 116 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -265,12 +265,6 @@ for f in $(echo "${deb_files}" | tr ' ' '\n' | grep -E '/(usrmerge|usr-is-merged
265265
# Remove the deb file so it won't be re-installed.
266266
rm "${f}"
267267
done
268-
269-
# Copy apt sources from worker into rootfs so the final container can install packages. Do we want that?
270-
# There is no guarantee that the final image will have access to the same sources worker had (e.g. with mounted repos).
271-
#
272-
# At the moment this is necessary so we can for example install test dependencies without using worker image.
273-
#cp -ar /etc/apt/sources.list* "${rootfs}/etc/apt/"
274268
`
275269

276270
opts = append(opts, dalec.ProgressGroup("Fetch DEB Packages"))
@@ -290,9 +284,124 @@ done
290284
frontend.IgnoreCache(input.Client, targets.IgnoreCacheKeyContainer),
291285
).AddMount("/tmp/rootfs", baseImageFromSpec(baseImg, input))
292286

293-
return baseImg.With(installPackagesInContainer(input, []llb.RunOption{
287+
result := baseImg.With(installPackagesInContainer(input, []llb.RunOption{
294288
dalec.ProgressGroup("Install DEB Packages"),
295289
llb.AddEnv("DEBIAN_FRONTEND", "noninteractive"),
296290
llb.Args([]string{"/usr/bin/sh", "-c", "dpkg --install --force-depends /var/cache/apt/archives/*.deb && rm -rf /var/cache/apt/archives/*.deb"}),
297291
}))
292+
293+
result = cleanupBootstrapContainer(result, input, opts...)
294+
295+
// Squash all layers into one by copying the final filesystem into a fresh
296+
// scratch state. Without this, files extracted in the bootstrap layer but
297+
// removed during cleanup still occupy space in the earlier layer.
298+
squashOpts := append(opts, dalec.ProgressGroup("Squash container layers"))
299+
return llb.Scratch().File(llb.Copy(result, "/", "/", &llb.CopyInfo{
300+
CopyDirContentsOnly: true,
301+
CreateDestPath: true,
302+
AllowWildcard: true,
303+
}), squashOpts...)
304+
}
305+
306+
// cleanupBootstrapContainer removes package manager infrastructure, unnecessary
307+
// packages, and caches from the container image.
308+
func cleanupBootstrapContainer(st llb.State, input buildContainerInput, opts ...llb.ConstraintsOpt) llb.State {
309+
cleanupOpts := append(opts, dalec.ProgressGroup("Cleanup Bootstrap Container"))
310+
311+
hasDocs := input.Spec.GetArtifacts(input.Target).HasDocs()
312+
313+
// Directories to remove.
314+
rmDirsCmd := `rm -rf /var/lib/apt /var/cache/apt /var/log/apt /var/log/dpkg.log \
315+
/var/cache/debconf /etc/apt \
316+
/usr/lib/apt /usr/share/apt \
317+
/usr/share/debconf /usr/share/bug /usr/share/lintian /usr/share/bash-completion \
318+
/usr/share/locale`
319+
if !hasDocs {
320+
rmDirsCmd += ` \
321+
/usr/share/doc /usr/share/man /usr/share/info`
322+
}
323+
324+
script := `#!/bin/sh
325+
326+
# --- Remove directories ---
327+
` + rmDirsCmd + `
328+
329+
# --- Remove unnecessary packages ---
330+
331+
332+
# Helper: collect installed packages from a candidate list.
333+
collect_installed() {
334+
result=""
335+
for pkg in $@; do
336+
if dpkg-query -W -f='${Status}' "${pkg}" 2>/dev/null | grep -q 'install ok installed'; then
337+
result="${result} ${pkg}"
338+
fi
339+
done
340+
echo "${result}"
341+
}
342+
343+
# Phase 1: Purge apt and packages whose removal scripts may load shared libs.
344+
phase1=$(collect_installed \
345+
apt apt-utils libapt-pkg6.0 libapt-pkg7.0 \
346+
debconf debconf-i18n libdebconfclient0 \
347+
debian-archive-keyring ubuntu-keyring \
348+
passwd login.defs base-passwd \
349+
bash-completion manpages sensible-utils sqv \
350+
ncurses-base ncurses-bin \
351+
perl-base libtext-charwidth-perl libtext-iconv-perl libtext-wrapi18n-perl \
352+
liblocale-gettext-perl \
353+
bash sed debianutils findutils mawk \
354+
bsdutils bsdextrautils util-linux sysvinit-utils \
355+
init-system-helpers \
356+
libpam-modules libpam-modules-bin libpam-runtime libpam0g \
357+
libdb5.3 libdb5.3t64 libsqlite3-0 \
358+
libaudit-common libaudit1 \
359+
liblastlog2-2 \
360+
)
361+
362+
if [ -n "${phase1}" ]; then
363+
dpkg --purge --force-depends --force-remove-essential ${phase1} || true
364+
fi
365+
366+
# Phase 2: Remove low-level libraries that phase 1 packages depended on.
367+
phase2=$(collect_installed \
368+
libsemanage-common libsemanage2 \
369+
libsystemd0 libudev1 \
370+
libseccomp2 \
371+
libmount1 libblkid1 libsmartcols1 \
372+
libcap-ng0 \
373+
libtinfo6 \
374+
grep \
375+
hostname \
376+
gzip
377+
)
378+
379+
if [ -n "${phase2}" ]; then
380+
dpkg --purge --force-depends --force-remove-essential ${phase2} || true
381+
fi
382+
383+
# Phase 3: Remove dpkg, its libraries, coreutils, dash, and remaining tools
384+
# in a single dpkg invocation. Once dpkg is loaded in memory, it won't
385+
# re-read the shared libraries, so removing them in the same call is safe.
386+
# dash is the shell running this script — the kernel keeps the fd open.
387+
# dpkg uses --remove (not --purge) to preserve /var/lib/dpkg/status
388+
# for security scanners.
389+
dpkg --remove --force-depends --force-remove-essential \
390+
diffutils \
391+
libbsd0 libmd0 libselinux1 libsepol2 libpcre2-8-0 \
392+
libacl1 libattr1 libcap2 \
393+
libbz2-1.0 liblz4-1 liblzma5 libxxhash0 \
394+
libuuid1 libcrypt1 tar dash coreutils \
395+
dpkg || true
396+
`
397+
398+
scriptSt := llb.Scratch().File(llb.Mkfile("cleanup.sh", 0o755, []byte(script)), cleanupOpts...)
399+
400+
st = st.Run(
401+
dalec.WithConstraints(cleanupOpts...),
402+
llb.AddMount("/tmp/dalec-cleanup.sh", scriptSt, llb.SourcePath("cleanup.sh"), llb.Readonly),
403+
llb.Args([]string{"/usr/bin/sh", "/tmp/dalec-cleanup.sh"}),
404+
).Root()
405+
406+
return st
298407
}

0 commit comments

Comments
 (0)