Skip to content

Commit f0f9642

Browse files
authored
fix: handle ancient empty docker layers (#522)
Earlier versions of docker images had empty layers of 1024 zero-valued octets. moby/moby#20917 (comment) Signed-off-by: Ramkumar Chinchani <rchincha@cisco.com>
1 parent 0cf2d70 commit f0f9642

File tree

5 files changed

+47
-3
lines changed

5 files changed

+47
-3
lines changed

pkg/log/log.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ func Infof(msg string, v ...interface{}) {
4646
addStackerLogSentinel(log.NewEntry(log.Log.(*log.Logger))).Infof(msg, v...)
4747
}
4848

49+
func Warnf(msg string, v ...interface{}) {
50+
addStackerLogSentinel(log.NewEntry(log.Log.(*log.Logger))).Warnf(msg, v...)
51+
}
52+
4953
func Errorf(msg string, v ...interface{}) {
5054
addStackerLogSentinel(log.NewEntry(log.Log.(*log.Logger))).Errorf(msg, v...)
5155
}

pkg/overlay/metadata.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package overlay
33
import (
44
"bytes"
55
"encoding/json"
6+
"io/fs"
67
"os"
78
"path"
89

@@ -117,7 +118,15 @@ func (ovl overlayMetadata) lxcRootfsString(config types.StackerConfig, tag strin
117118
for _, layer := range manifest.Layers {
118119
contents := overlayPath(config.RootFSDir, layer.Digest, "overlay")
119120
if _, err := os.Stat(contents); err != nil {
120-
return "", errors.Wrapf(err, "%s does not exist", contents)
121+
if errors.Is(err, fs.ErrNotExist) {
122+
// some docker layers may be empty tars, so ignore these
123+
// https://github.com/moby/moby/issues/20917#issuecomment-191901912
124+
log.Warnf("%s skipping empty tar layer", layer.Digest)
125+
126+
continue
127+
}
128+
129+
return "", errors.Wrapf(err, "%s unable to stat", contents)
121130
}
122131
lowerdirs = append(lowerdirs, contents)
123132
}

pkg/stacker/base.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,11 @@ func SetupRootfs(o BaseLayerOpts) error {
8787
case types.OCILayer:
8888
fallthrough
8989
case types.DockerLayer:
90-
return setupContainersImageRootfs(o)
90+
err := setupContainersImageRootfs(o)
91+
if err != nil && errors.Is(err, types.ErrEmptyLayers) {
92+
return o.Storage.SetupEmptyRootfs(o.Name)
93+
}
94+
return err
9195
default:
9296
return errors.Errorf("unknown layer type: %v", o.Layer.From.Type)
9397
}

pkg/types/layer_type.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"stackerbuild.io/stacker/pkg/squashfs"
1111
)
1212

13+
var ErrEmptyLayers = errors.New("empty layers")
14+
1315
type LayerType struct {
1416
Type string
1517
Verity squashfs.VerityMetadata
@@ -60,7 +62,7 @@ func NewLayerType(lt string, verity squashfs.VerityMetadata) (LayerType, error)
6062

6163
func NewLayerTypeManifest(manifest ispec.Manifest) (LayerType, error) {
6264
if len(manifest.Layers) == 0 {
63-
return LayerType{}, errors.Errorf("no existing layers to determine layer type")
65+
return NewLayerType("tar", squashfs.VerityMetadataMissing)
6466
}
6567

6668
switch manifest.Layers[0].MediaType {

test/empty-layers.bats

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,28 @@ EOF
6161

6262
[ "$layers0" = "$layers1" ]
6363
}
64+
65+
@test "an image with empty layers" {
66+
umoci init --layout oci
67+
umoci new --image oci:emptylayer
68+
chmod -R a+rw oci
69+
70+
cat > stacker.yaml <<EOF
71+
test_empty_layer:
72+
from:
73+
type: oci
74+
url: oci:emptylayer
75+
EOF
76+
stacker build
77+
}
78+
79+
@test "a real-world docker image with empty/filler layer" {
80+
cat > stacker.yaml <<EOF
81+
image:
82+
from:
83+
type: docker
84+
url: docker://ghcr.io/project-stacker/grafana-oss:10.1.2-ubuntu
85+
EOF
86+
stacker build
87+
}
88+

0 commit comments

Comments
 (0)