import: Raise error if the imported image is filtered out

During import, if an image does not match the host's platform,
it won't have any children labels set, which will result in the
Garbage Collector deleting its content later, resulting in an
unusable image. In this case, we should fail early.

This can still be bypassed by using ctr import --all-platforms.

Signed-off-by: Claudiu Belu <cbelu@cloudbasesolutions.com>
This commit is contained in:
Claudiu Belu 2021-08-28 16:02:06 -07:00
parent c23f52af30
commit e087b47e98
3 changed files with 52 additions and 5 deletions

View File

@ -40,6 +40,10 @@ var (
// This applies only to a single descriptor in a handler
// chain and does not apply to descendant descriptors.
ErrStopHandler = fmt.Errorf("stop handler")
// ErrEmptyWalk is used when the WalkNotEmpty handlers return no
// children (e.g.: they were filtered out).
ErrEmptyWalk = fmt.Errorf("image might be filtered out")
)
// Handler handles image manifests
@ -99,6 +103,36 @@ func Walk(ctx context.Context, handler Handler, descs ...ocispec.Descriptor) err
}
}
}
return nil
}
// WalkNotEmpty works the same way Walk does, with the exception that it ensures that
// some children are still found by Walking the descriptors (for example, not all of
// them have been filtered out by one of the handlers). If there are no children,
// then an ErrEmptyWalk error is returned.
func WalkNotEmpty(ctx context.Context, handler Handler, descs ...ocispec.Descriptor) error {
isEmpty := true
var notEmptyHandler HandlerFunc = func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
children, err := handler.Handle(ctx, desc)
if err != nil {
return children, err
}
if len(children) > 0 {
isEmpty = false
}
return children, nil
}
err := Walk(ctx, notEmptyHandler, descs...)
if err != nil {
return err
}
if isEmpty {
return ErrEmptyWalk
}
return nil
}

View File

@ -185,7 +185,7 @@ func (c *Client) Import(ctx context.Context, reader io.Reader, opts ...ImportOpt
handler = images.FilterPlatforms(handler, platformMatcher)
handler = images.SetChildrenLabels(cs, handler)
if err := images.Walk(ctx, handler, index); err != nil {
if err := images.WalkNotEmpty(ctx, handler, index); err != nil {
return nil, err
}

View File

@ -25,6 +25,7 @@ import (
"io/ioutil"
"math/rand"
"reflect"
"runtime"
"testing"
. "github.com/containerd/containerd"
@ -98,7 +99,8 @@ func TestImport(t *testing.T) {
empty := []byte("{}")
version := []byte("1.0")
c1, d2 := createConfig()
c1, d2 := createConfig(runtime.GOOS, runtime.GOARCH)
badConfig, _ := createConfig("foo", "lish")
m1, d3, expManifest := createManifest(c1, [][]byte{b1})
@ -172,6 +174,17 @@ func TestImport(t *testing.T) {
checkManifest(ctx, t, imgs[0].Target, nil)
},
},
{
Name: "DockerV2.1-BadOSArch",
Writer: tartest.TarAll(
tc.Dir("bd765cd43e95212f7aa2cab51d0a", 0755),
tc.File("bd765cd43e95212f7aa2cab51d0a/json", empty, 0644),
tc.File("bd765cd43e95212f7aa2cab51d0a/layer.tar", b1, 0644),
tc.File("bd765cd43e95212f7aa2cab51d0a/VERSION", version, 0644),
tc.File("e95212f7aa2cab51d0abd765cd43.json", badConfig, 0644),
tc.File("manifest.json", []byte(`[{"Config":"e95212f7aa2cab51d0abd765cd43.json","RepoTags":["test-import:notlatest", "another/repo:tag"],"Layers":["bd765cd43e95212f7aa2cab51d0a/layer.tar"]}]`), 0644),
),
},
{
Name: "OCI-BadFormat",
Writer: tartest.TarAll(
@ -302,10 +315,10 @@ func createContent(size int64, seed int64) ([]byte, digest.Digest) {
return b, digest.FromBytes(b)
}
func createConfig() ([]byte, digest.Digest) {
func createConfig(osName, archName string) ([]byte, digest.Digest) {
image := ocispec.Image{
OS: "any",
Architecture: "any",
OS: osName,
Architecture: archName,
Author: "test",
}
b, _ := json.Marshal(image)