summaryrefslogtreecommitdiff
path: root/integration/image
diff options
context:
space:
mode:
authorBrian Goff <cpuguy83@gmail.com>2022-03-22 20:22:57 +0000
committerSebastiaan van Stijn <github@gone.nl>2022-10-21 01:48:59 +0200
commit27530efedbe384a920ff111d511307d45a4c06d8 (patch)
tree91f10d1e9c3fef3d27c8f8af0cf7981340934627 /integration/image
parent92eca900b086200b4f270cf438a8a5ff9c8c2a3b (diff)
downloaddocker-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.go135
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)
+}