123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- package image
- import (
- "context"
- "encoding/json"
- "fmt"
- "io"
- "os"
- "path"
- "strings"
- "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/errdefs"
- "github.com/docker/docker/testutil/registry"
- "github.com/opencontainers/go-digest"
- "github.com/opencontainers/image-spec/specs-go"
- ocispec "github.com/opencontainers/image-spec/specs-go/v1"
- "gotest.tools/v3/assert"
- is "gotest.tools/v3/assert/cmp"
- "gotest.tools/v3/skip"
- )
- func TestImagePullPlatformInvalid(t *testing.T) {
- ctx := setupTest(t)
- client := testEnv.APIClient()
- _, err := client.ImagePull(ctx, "docker.io/library/hello-world:latest", types.ImagePullOptions{Platform: "foobar"})
- assert.Assert(t, err != nil)
- 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) ocispec.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()
- img := ocispec.Image{
- Platform: platforms.DefaultSpec(),
- RootFS: ocispec.RootFS{Type: "layers", DiffIDs: []digest.Digest{layerDigest}},
- Config: ocispec.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 := ocispec.Manifest{
- Versioned: specs.Versioned{
- SchemaVersion: 2,
- },
- MediaType: images.MediaTypeDockerSchema2Manifest,
- Config: ocispec.Descriptor{
- MediaType: images.MediaTypeDockerSchema2Config,
- Digest: configDigest,
- Size: int64(len(imgJSON)),
- },
- Layers: []ocispec.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 ocispec.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) {
- skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
- skip.If(t, testEnv.DaemonInfo.OSType == "windows", "We don't run a test registry on Windows")
- skip.If(t, testEnv.IsRootless, "Rootless has a different view of localhost (needed for test registry access)")
- ctx := setupTest(t)
- reg := registry.NewV2(t, registry.WithStdout(os.Stdout), registry.WithStderr(os.Stderr))
- defer reg.Close()
- reg.WaitReady(t)
- // 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)
- }
- // TestImagePullNonExisting pulls non-existing images from the central registry, with different
- // combinations of implicit tag and library prefix.
- func TestImagePullNonExisting(t *testing.T) {
- ctx := setupTest(t)
- for _, ref := range []string{
- "asdfasdf:foobar",
- "library/asdfasdf:foobar",
- "asdfasdf",
- "asdfasdf:latest",
- "library/asdfasdf",
- "library/asdfasdf:latest",
- } {
- ref := ref
- all := strings.Contains(ref, ":")
- t.Run(ref, func(t *testing.T) {
- t.Parallel()
- client := testEnv.APIClient()
- rdr, err := client.ImagePull(ctx, ref, types.ImagePullOptions{
- All: all,
- })
- if err == nil {
- rdr.Close()
- }
- expectedMsg := fmt.Sprintf("pull access denied for %s, repository does not exist or may require 'docker login'", "asdfasdf")
- assert.Assert(t, is.ErrorContains(err, expectedMsg))
- assert.Check(t, is.ErrorType(err, errdefs.IsNotFound))
- if all {
- // pull -a on a nonexistent registry should fall back as well
- assert.Check(t, !strings.Contains(err.Error(), "unauthorized"), `message should not contain "unauthorized"`)
- }
- })
- }
- }
|