Skip to content

Commit adf0a37

Browse files
authored
fix: remove superfluous userxattr whiteouts (#629)
overlayfs creates whiteout entries whenever a dir is created which is not ideal. Instead, we inspect all the lower layers and only if a dir exists, we emit a whiteout entry. Note that we link against the 'stacker' branch of project-stacker/umoci Signed-off-by: Ramkumar Chinchani <[email protected]>
1 parent f494a9a commit adf0a37

File tree

10 files changed

+121
-19
lines changed

10 files changed

+121
-19
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ BATS = $(TOOLS_D)/bin/bats
4646
BATS_VERSION := v1.10.0
4747
# OCI registry
4848
ZOT := $(TOOLS_D)/bin/zot
49-
ZOT_VERSION := v2.0.2
49+
ZOT_VERSION := v2.1.0
5050

5151
export PATH := $(TOOLS_D)/bin:$(PATH)
5252

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,6 @@ require (
284284
)
285285

286286
replace (
287-
github.com/opencontainers/umoci => github.com/project-stacker/umoci v0.0.0-20240417195808-16c510104378
287+
github.com/opencontainers/umoci => github.com/project-stacker/umoci v0.0.0-20240731171528-3a75e000071d
288288
stackerbuild.io/stacker-bom => github.com/project-stacker/stacker-bom v0.0.0-20240509203427-4d685e046780
289289
)

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -795,8 +795,8 @@ github.com/proglottis/gpgme v0.1.3 h1:Crxx0oz4LKB3QXc5Ea0J19K/3ICfy3ftr5exgUK1AU
795795
github.com/proglottis/gpgme v0.1.3/go.mod h1:fPbW/EZ0LvwQtH8Hy7eixhp1eF3G39dtx7GUN+0Gmy0=
796796
github.com/project-stacker/stacker-bom v0.0.0-20240509203427-4d685e046780 h1:VJQ/G6xlNQqEvdzTdtXJ/XNvxv9LQTDJORik1wuxXJU=
797797
github.com/project-stacker/stacker-bom v0.0.0-20240509203427-4d685e046780/go.mod h1:S7hlUdKwPKIMNx2ceiqmLKyXq+FOVaVnAuk77AzdhtI=
798-
github.com/project-stacker/umoci v0.0.0-20240417195808-16c510104378 h1:NHGVwIe0Icrbn8b7WW55FQjETHm5B0i6Gt6UZmzaF9A=
799-
github.com/project-stacker/umoci v0.0.0-20240417195808-16c510104378/go.mod h1:XUXUpCpA/Y8aJWezK1i8o4WDR0Y/vhMcWg+FUNQkKMQ=
798+
github.com/project-stacker/umoci v0.0.0-20240731171528-3a75e000071d h1:MKBVRErs7R34UBQP6ITh97XPPT5GgTDxsme/KxufP5c=
799+
github.com/project-stacker/umoci v0.0.0-20240731171528-3a75e000071d/go.mod h1:XUXUpCpA/Y8aJWezK1i8o4WDR0Y/vhMcWg+FUNQkKMQ=
800800
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
801801
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
802802
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=

install-build-deps.sh

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ installdeps_fedora() {
1818
sudo dnf install golang
1919
go version
2020
fi
21+
sudo dnf install bsdtar
2122
}
2223

2324
installdeps_ubuntu() {
@@ -40,7 +41,8 @@ installdeps_ubuntu() {
4041
parallel \
4142
pkg-config \
4243
squashfs-tools \
43-
squashfuse
44+
squashfuse \
45+
libarchive-tools
4446
# skopeo deps
4547
sudo apt -yy install \
4648
libgpgme-dev \

pkg/overlay/overlay-dirs.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func generateOverlayDirLayer(name string, layerType types.LayerType, overlayDir
4848
defer oci.Close()
4949

5050
contents := path.Join(config.RootFSDir, name, "overlay_dirs", path.Base(overlayDir.Source))
51-
blob, mediaType, rootHash, err := generateBlob(layerType, contents, config.OCIDir)
51+
blob, mediaType, rootHash, err := generateBlob(layerType, contents, config.OCIDir, nil)
5252
if err != nil {
5353
return ispec.Descriptor{}, err
5454
}

pkg/overlay/pack.go

+27-10
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ func ConvertAndOutput(config types.StackerConfig, tag, name string, layerType ty
135135
bundlePath := overlayPath(config.RootFSDir, theLayer.Digest)
136136
overlayDir := path.Join(bundlePath, "overlay")
137137
// generate blob
138-
blob, mediaType, rootHash, err := generateBlob(layerType, overlayDir, config.OCIDir)
138+
blob, mediaType, rootHash, err := generateBlob(layerType, overlayDir, config.OCIDir, nil)
139139
if err != nil {
140140
return err
141141
}
@@ -283,23 +283,23 @@ func (o *overlay) initializeBasesInOutput(name string, layerTypes []types.LayerT
283283
return nil
284284
}
285285

286-
func (o *overlay) Repack(name string, layerTypes []types.LayerType, sfm types.StackerFiles) error {
286+
func (o *overlay) Repack(name string, layer types.Layer, layerTypes []types.LayerType, sfm types.StackerFiles) error {
287287
err := o.initializeBasesInOutput(name, layerTypes, sfm)
288288
if err != nil {
289289
return err
290290
}
291291

292-
return repackOverlay(o.config, name, layerTypes)
292+
return repackOverlay(o.config, name, layer, layerTypes)
293293
}
294294

295295
// generateBlob generates either a tar blob or a squashfs blob based on layerType
296-
func generateBlob(layerType types.LayerType, contents string, ociDir string) (io.ReadCloser, string, string, error) {
296+
func generateBlob(layerType types.LayerType, contents string, ociDir string, lowerDirs []string) (io.ReadCloser, string, string, error) {
297297
var blob io.ReadCloser
298298
var err error
299299
var mediaType string
300300
var rootHash string
301301
if layerType.Type == "tar" {
302-
packOptions := layer.RepackOptions{TranslateOverlayWhiteouts: true}
302+
packOptions := layer.RepackOptions{TranslateOverlayWhiteouts: true, OverlayLowerDirs: lowerDirs}
303303
blob = layer.GenerateInsertLayer(contents, "/", false, &packOptions)
304304
mediaType = ispec.MediaTypeImageLayer
305305
} else {
@@ -382,7 +382,9 @@ func stripOverlayAttrsUnder(dirPath string) error {
382382
})
383383
}
384384

385-
func generateLayer(config types.StackerConfig, oci casext.Engine, mutators []*mutate.Mutator, name string, layerTypes []types.LayerType) (bool, error) {
385+
func generateLayer(config types.StackerConfig, oci casext.Engine, mutators []*mutate.Mutator,
386+
name string, layer types.Layer, layerTypes []types.LayerType,
387+
) (bool, error) {
386388
dir := path.Join(config.RootFSDir, name, "overlay")
387389
ents, err := os.ReadDir(dir)
388390
if err != nil {
@@ -430,12 +432,27 @@ func generateLayer(config types.StackerConfig, oci casext.Engine, mutators []*mu
430432
return false, err
431433
}
432434

435+
var ovl overlayMetadata
436+
if layer.From.Type != types.BuiltLayer {
437+
ovl, err = readOverlayMetadata(config.RootFSDir, name)
438+
} else {
439+
ovl, err = readOverlayMetadata(config.RootFSDir, layer.From.Tag)
440+
}
441+
if err != nil {
442+
return false, err
443+
}
444+
433445
descs := []ispec.Descriptor{}
434446
for i, layerType := range layerTypes {
435447
mutator := mutators[i]
436448
var desc ispec.Descriptor
437449

438-
blob, mediaType, rootHash, err := generateBlob(layerType, dir, config.OCIDir)
450+
lowerDirs := []string{}
451+
for i := len(ovl.Manifests[layerType].Layers) - 1; i >= 0; i-- {
452+
lowerDirs = append(lowerDirs, overlayPath(config.RootFSDir, ovl.Manifests[layerType].Layers[i].Digest))
453+
}
454+
455+
blob, mediaType, rootHash, err := generateBlob(layerType, dir, config.OCIDir, lowerDirs)
439456
if err != nil {
440457
return false, err
441458
}
@@ -547,7 +564,7 @@ func generateLayer(config types.StackerConfig, oci casext.Engine, mutators []*mu
547564
return true, nil
548565
}
549566

550-
func repackOverlay(config types.StackerConfig, name string, layerTypes []types.LayerType) error {
567+
func repackOverlay(config types.StackerConfig, name string, layer types.Layer, layerTypes []types.LayerType) error {
551568
oci, err := umoci.OpenLayout(config.OCIDir)
552569
if err != nil {
553570
return err
@@ -602,7 +619,7 @@ func repackOverlay(config types.StackerConfig, name string, layerTypes []types.L
602619
// generate blobs for each build layer
603620
for _, buildLayer := range ovl.BuiltLayers {
604621

605-
didMutate, err := generateLayer(config, oci, mutators, buildLayer, layerTypes)
622+
didMutate, err := generateLayer(config, oci, mutators, buildLayer, layer, layerTypes)
606623
if err != nil {
607624
return err
608625
}
@@ -637,7 +654,7 @@ func repackOverlay(config types.StackerConfig, name string, layerTypes []types.L
637654
return err
638655
}
639656

640-
didMutate, err := generateLayer(config, oci, mutators, name, layerTypes)
657+
didMutate, err := generateLayer(config, oci, mutators, name, layer, layerTypes)
641658
if err != nil {
642659
return err
643660
}

pkg/stacker/build.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ func (b *Builder) build(s types.Storage, file string) error {
551551
continue
552552
}
553553

554-
err = s.Repack(name, opts.LayerTypes, b.builtStackerfiles)
554+
err = s.Repack(name, l, opts.LayerTypes, b.builtStackerfiles)
555555
if err != nil {
556556
return err
557557
}

pkg/types/storage.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ type Storage interface {
4747
Unpack(tag, name string) error
4848

4949
// Repack repacks the specified working dir into the specified OCI dir.
50-
Repack(name string, layerTypes []LayerType, sfm StackerFiles) error
50+
Repack(name string, layer Layer, layerTypes []LayerType, sfm StackerFiles) error
5151

5252
// GetLXCRootfsConfig returns the string that should be set as
5353
// lxc.rootfs.path in the LXC container's config.

test/publish.bats

+9-1
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,12 @@ parent:
260260
run: |
261261
rm -rf /etc/apk/repositories
262262
EOF
263+
stacker check
263264
stacker build
264-
stacker publish --skip-tls --url docker://${REGISTRY_URL} --tag latest --substitute BUSYBOX_OCI=${BUSYBOX_OCI}
265+
manifest0=$(cat oci/index.json | jq -r .manifests[0].digest | cut -f2 -d:)
266+
layers0=$(cat oci/blobs/sha256/$manifest0 | jq -r .layers[1].digest | cut -f2 -d:)
267+
bsdtar -tvf oci/blobs/sha256/$layers0
268+
stacker publish --skip-tls --url docker://${REGISTRY_URL} --tag latest
265269
stacker clean
266270

267271
cat > stacker.yaml <<"EOF"
@@ -271,6 +275,10 @@ child:
271275
url: docker://${{REGISTRY_URL}}/parent:latest
272276
insecure: true
273277
run: |
278+
ps
279+
ls -l /
280+
ls -l /etc
281+
ls -l /etc/apk
274282
[ ! -f /etc/apk/repositories ]
275283
EOF
276284
stacker build --substitute REGISTRY_URL=${REGISTRY_URL}

test/whiteout.bats

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
load helpers
2+
3+
function setup() {
4+
stacker_setup
5+
}
6+
7+
function teardown() {
8+
cleanup
9+
}
10+
11+
@test "test not adding extraneous whiteouts" {
12+
cat > stacker.yaml <<EOF
13+
image:
14+
from:
15+
type: docker
16+
url: docker://public.ecr.aws/ubuntu/ubuntu:latest
17+
run: |
18+
apt-get update
19+
apt-get -y install libsensors-config
20+
EOF
21+
22+
stacker build
23+
echo "checking"
24+
for f in $(ls oci/blobs/sha256/); do
25+
file oci/blobs/sha256/$f | grep "gzip" || {
26+
echo "skipping $f"
27+
continue
28+
}
29+
bsdtar -tvf oci/blobs/sha256/$f
30+
run "bsdtar -tvf oci/blobs/sha256/$f | grep '.wh.sensors.d'"
31+
if [ "$status" -eq 0 ]; then
32+
echo "should not have a sensors.d whiteout!";
33+
exit 1;
34+
fi
35+
done
36+
}
37+
38+
@test "dont emit whiteout for new dir creates" {
39+
cat > stacker.yaml <<EOF
40+
# a1.tar has /a1/file
41+
bb:
42+
from:
43+
type: docker
44+
url: docker://busybox
45+
run: |
46+
mkdir /a1
47+
touch /a1/file
48+
49+
nodir:
50+
from:
51+
type: built
52+
tag: bb
53+
run: |
54+
rm -rf /a1
55+
56+
emptydir:
57+
from:
58+
type: built
59+
tag: bb
60+
run: |
61+
rm -rf /a1
62+
mkdir /a1
63+
64+
fulldir:
65+
from:
66+
type: built
67+
tag: bb
68+
run: |
69+
rm -rf /a1
70+
mkdir /a1
71+
touch /a1/newfile
72+
EOF
73+
74+
stacker build
75+
}

0 commit comments

Comments
 (0)