diff options
| author | Brian Goff <cpuguy83@gmail.com> | 2022-03-22 20:22:57 +0000 |
|---|---|---|
| committer | Sebastiaan van Stijn <github@gone.nl> | 2022-10-21 01:48:59 +0200 |
| commit | 27530efedbe384a920ff111d511307d45a4c06d8 (patch) | |
| tree | 91f10d1e9c3fef3d27c8f8af0cf7981340934627 /integration/image | |
| parent | 92eca900b086200b4f270cf438a8a5ff9c8c2a3b (diff) | |
| download | docker-27530efedbe384a920ff111d511307d45a4c06d8.tar.gz | |
Validate digest in repo for pull by digest
This is accomplished by storing the distribution source in the content
labels. If the distribution source is not found then we check to the
registry to see if the digest exists in the repo, if it does exist then
the puller will use it.
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Diffstat (limited to 'integration/image')
| -rw-r--r-- | integration/image/pull_test.go | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/integration/image/pull_test.go b/integration/image/pull_test.go index 15db3295d8..940a21e1ff 100644 --- a/integration/image/pull_test.go +++ b/integration/image/pull_test.go @@ -2,11 +2,24 @@ package image import ( "context" + "encoding/json" + "io" + "os" + "path" "testing" + "github.com/containerd/containerd" + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/content/local" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/platforms" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/versions" "github.com/docker/docker/errdefs" + "github.com/docker/docker/testutil/registry" + "github.com/opencontainers/go-digest" + "github.com/opencontainers/image-spec/specs-go" + imagespec "github.com/opencontainers/image-spec/specs-go/v1" "gotest.tools/v3/assert" "gotest.tools/v3/skip" ) @@ -22,3 +35,125 @@ func TestImagePullPlatformInvalid(t *testing.T) { assert.ErrorContains(t, err, "unknown operating system or architecture") assert.Assert(t, errdefs.IsInvalidParameter(err)) } + +func createTestImage(ctx context.Context, t testing.TB, store content.Store) imagespec.Descriptor { + w, err := store.Writer(ctx, content.WithRef("layer")) + assert.NilError(t, err) + defer w.Close() + + // Empty layer with just a root dir + const layer = `./0000775000000000000000000000000014201045023007702 5ustar rootroot` + + _, err = w.Write([]byte(layer)) + assert.NilError(t, err) + + err = w.Commit(ctx, int64(len(layer)), digest.FromBytes([]byte(layer))) + assert.NilError(t, err) + + layerDigest := w.Digest() + w.Close() + + platform := platforms.DefaultSpec() + + img := imagespec.Image{ + Architecture: platform.Architecture, + OS: platform.OS, + RootFS: imagespec.RootFS{Type: "layers", DiffIDs: []digest.Digest{layerDigest}}, + Config: imagespec.ImageConfig{WorkingDir: "/"}, + } + imgJSON, err := json.Marshal(img) + assert.NilError(t, err) + + w, err = store.Writer(ctx, content.WithRef("config")) + assert.NilError(t, err) + defer w.Close() + _, err = w.Write(imgJSON) + assert.NilError(t, err) + assert.NilError(t, w.Commit(ctx, int64(len(imgJSON)), digest.FromBytes(imgJSON))) + + configDigest := w.Digest() + w.Close() + + info, err := store.Info(ctx, layerDigest) + assert.NilError(t, err) + + manifest := imagespec.Manifest{ + Versioned: specs.Versioned{ + SchemaVersion: 2, + }, + MediaType: images.MediaTypeDockerSchema2Manifest, + Config: imagespec.Descriptor{ + MediaType: images.MediaTypeDockerSchema2Config, + Digest: configDigest, + Size: int64(len(imgJSON)), + }, + Layers: []imagespec.Descriptor{{ + MediaType: images.MediaTypeDockerSchema2Layer, + Digest: layerDigest, + Size: info.Size, + }}, + } + + manifestJSON, err := json.Marshal(manifest) + assert.NilError(t, err) + + w, err = store.Writer(ctx, content.WithRef("manifest")) + assert.NilError(t, err) + defer w.Close() + _, err = w.Write(manifestJSON) + assert.NilError(t, err) + assert.NilError(t, w.Commit(ctx, int64(len(manifestJSON)), digest.FromBytes(manifestJSON))) + + manifestDigest := w.Digest() + w.Close() + + return imagespec.Descriptor{ + MediaType: images.MediaTypeDockerSchema2Manifest, + Digest: manifestDigest, + Size: int64(len(manifestJSON)), + } +} + +// Make sure that pulling by an already cached digest but for a different ref (that should not have that digest) +// verifies with the remote that the digest exists in that repo. +func TestImagePullStoredfDigestForOtherRepo(t *testing.T) { + defer setupTest(t)() + + reg := registry.NewV2(t, registry.WithStdout(os.Stdout), registry.WithStderr(os.Stderr)) + defer reg.Close() + reg.WaitReady(t) + + ctx := context.Background() + + // First create an image and upload it to our local registry + // Then we'll download it so that we can make sure the content is available in dockerd's manifest cache. + // Then we'll try to pull the same digest but with a different repo name. + + dir := t.TempDir() + store, err := local.NewStore(dir) + assert.NilError(t, err) + + desc := createTestImage(ctx, t, store) + + remote := path.Join(registry.DefaultURL, "test:latest") + + c8dClient, err := containerd.New("", containerd.WithServices(containerd.WithContentStore(store))) + assert.NilError(t, err) + + c8dClient.Push(ctx, remote, desc) + assert.NilError(t, err) + + client := testEnv.APIClient() + rdr, err := client.ImagePull(ctx, remote, types.ImagePullOptions{}) + assert.NilError(t, err) + defer rdr.Close() + io.Copy(io.Discard, rdr) + + // Now, pull a totally different repo with a the same digest + rdr, err = client.ImagePull(ctx, path.Join(registry.DefaultURL, "other:image@"+desc.Digest.String()), types.ImagePullOptions{}) + if rdr != nil { + rdr.Close() + } + assert.Assert(t, err != nil, "Expected error, got none: %v", err) + assert.Assert(t, errdefs.IsNotFound(err), err) +} |
