Skip to content

Commit

Permalink
fix: handle ancient empty docker layers (project-stacker#522)
Browse files Browse the repository at this point in the history
Earlier versions of docker images had empty layers of 1024 zero-valued
octets.

moby/moby#20917 (comment)

(cherry picked from commit f0f9642)

Signed-off-by: Ramkumar Chinchani <[email protected]>
  • Loading branch information
rchincha committed Nov 8, 2023
1 parent 4170fc0 commit 4b578c9
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 3 deletions.
4 changes: 4 additions & 0 deletions pkg/log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ func Infof(msg string, v ...interface{}) {
addStackerLogSentinel(log.NewEntry(log.Log.(*log.Logger))).Infof(msg, v...)
}

func Warnf(msg string, v ...interface{}) {
addStackerLogSentinel(log.NewEntry(log.Log.(*log.Logger))).Warnf(msg, v...)
}

func Errorf(msg string, v ...interface{}) {
addStackerLogSentinel(log.NewEntry(log.Log.(*log.Logger))).Errorf(msg, v...)
}
Expand Down
11 changes: 10 additions & 1 deletion pkg/overlay/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"io/fs"
"os"
"path"

Expand Down Expand Up @@ -118,7 +119,15 @@ func (ovl overlayMetadata) lxcRootfsString(config types.StackerConfig, tag strin
for _, layer := range manifest.Layers {
contents := overlayPath(config, layer.Digest, "overlay")
if _, err := os.Stat(contents); err != nil {
return "", errors.Wrapf(err, "%s does not exist", contents)
if errors.Is(err, fs.ErrNotExist) {
// some docker layers may be empty tars, so ignore these
// https://github.com/moby/moby/issues/20917#issuecomment-191901912
log.Warnf("%s skipping empty tar layer", layer.Digest)

continue
}

return "", errors.Wrapf(err, "%s unable to stat", contents)
}
lowerdirs = append(lowerdirs, contents)
}
Expand Down
6 changes: 5 additions & 1 deletion pkg/stacker/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,11 @@ func SetupRootfs(o BaseLayerOpts) error {
case types.OCILayer:
fallthrough
case types.DockerLayer:
return setupContainersImageRootfs(o)
err := setupContainersImageRootfs(o)
if err != nil && errors.Is(err, types.ErrEmptyLayers) {
return o.Storage.SetupEmptyRootfs(o.Name)
}
return err
default:
return errors.Errorf("unknown layer type: %v", o.Layer.From.Type)
}
Expand Down
4 changes: 3 additions & 1 deletion pkg/types/layer_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"stackerbuild.io/stacker/pkg/squashfs"
)

var ErrEmptyLayers = errors.New("empty layers")

type LayerType struct {
Type string
Verity squashfs.VerityMetadata
Expand Down Expand Up @@ -53,7 +55,7 @@ func NewLayerType(lt string, verity squashfs.VerityMetadata) (LayerType, error)

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

switch manifest.Layers[0].MediaType {
Expand Down
25 changes: 25 additions & 0 deletions test/empty-layers.bats
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,28 @@ EOF

[ "$layers0" = "$layers1" ]
}

@test "an image with empty layers" {
umoci init --layout oci
umoci new --image oci:emptylayer
chmod -R a+rw oci

cat > stacker.yaml <<EOF
test_empty_layer:
from:
type: oci
url: oci:emptylayer
EOF
stacker build
}

@test "a real-world docker image with empty/filler layer" {
cat > stacker.yaml <<EOF
image:
from:
type: docker
url: docker://ghcr.io/project-stacker/grafana-oss:10.1.2-ubuntu
EOF
stacker build
}

0 comments on commit 4b578c9

Please sign in to comment.