github.com/rish1988/moby@v25.0.2+incompatible/integration/image/pull_test.go (about) 1 package image 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "io" 8 "os" 9 "path" 10 "strings" 11 "testing" 12 13 "github.com/containerd/containerd" 14 "github.com/containerd/containerd/content" 15 "github.com/containerd/containerd/content/local" 16 "github.com/containerd/containerd/images" 17 "github.com/containerd/containerd/platforms" 18 "github.com/docker/docker/api/types/image" 19 "github.com/docker/docker/errdefs" 20 "github.com/docker/docker/testutil/registry" 21 "github.com/opencontainers/go-digest" 22 "github.com/opencontainers/image-spec/specs-go" 23 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 24 "gotest.tools/v3/assert" 25 is "gotest.tools/v3/assert/cmp" 26 "gotest.tools/v3/skip" 27 ) 28 29 func TestImagePullPlatformInvalid(t *testing.T) { 30 ctx := setupTest(t) 31 32 client := testEnv.APIClient() 33 34 _, err := client.ImagePull(ctx, "docker.io/library/hello-world:latest", image.PullOptions{Platform: "foobar"}) 35 assert.Assert(t, err != nil) 36 assert.Check(t, is.ErrorContains(err, "unknown operating system or architecture")) 37 assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter)) 38 } 39 40 func createTestImage(ctx context.Context, t testing.TB, store content.Store) ocispec.Descriptor { 41 w, err := store.Writer(ctx, content.WithRef("layer")) 42 assert.NilError(t, err) 43 defer w.Close() 44 45 // Empty layer with just a root dir 46 const layer = `./0000775000000000000000000000000014201045023007702 5ustar rootroot` 47 48 _, err = w.Write([]byte(layer)) 49 assert.NilError(t, err) 50 51 err = w.Commit(ctx, int64(len(layer)), digest.FromBytes([]byte(layer))) 52 assert.NilError(t, err) 53 54 layerDigest := w.Digest() 55 assert.Check(t, w.Close()) 56 57 img := ocispec.Image{ 58 Platform: platforms.DefaultSpec(), 59 RootFS: ocispec.RootFS{Type: "layers", DiffIDs: []digest.Digest{layerDigest}}, 60 Config: ocispec.ImageConfig{WorkingDir: "/"}, 61 } 62 imgJSON, err := json.Marshal(img) 63 assert.NilError(t, err) 64 65 w, err = store.Writer(ctx, content.WithRef("config")) 66 assert.NilError(t, err) 67 defer w.Close() 68 _, err = w.Write(imgJSON) 69 assert.NilError(t, err) 70 assert.NilError(t, w.Commit(ctx, int64(len(imgJSON)), digest.FromBytes(imgJSON))) 71 72 configDigest := w.Digest() 73 assert.Check(t, w.Close()) 74 75 info, err := store.Info(ctx, layerDigest) 76 assert.NilError(t, err) 77 78 manifest := ocispec.Manifest{ 79 Versioned: specs.Versioned{ 80 SchemaVersion: 2, 81 }, 82 MediaType: images.MediaTypeDockerSchema2Manifest, 83 Config: ocispec.Descriptor{ 84 MediaType: images.MediaTypeDockerSchema2Config, 85 Digest: configDigest, 86 Size: int64(len(imgJSON)), 87 }, 88 Layers: []ocispec.Descriptor{{ 89 MediaType: images.MediaTypeDockerSchema2Layer, 90 Digest: layerDigest, 91 Size: info.Size, 92 }}, 93 } 94 95 manifestJSON, err := json.Marshal(manifest) 96 assert.NilError(t, err) 97 98 w, err = store.Writer(ctx, content.WithRef("manifest")) 99 assert.NilError(t, err) 100 defer w.Close() 101 _, err = w.Write(manifestJSON) 102 assert.NilError(t, err) 103 assert.NilError(t, w.Commit(ctx, int64(len(manifestJSON)), digest.FromBytes(manifestJSON))) 104 105 manifestDigest := w.Digest() 106 assert.Check(t, w.Close()) 107 108 return ocispec.Descriptor{ 109 MediaType: images.MediaTypeDockerSchema2Manifest, 110 Digest: manifestDigest, 111 Size: int64(len(manifestJSON)), 112 } 113 } 114 115 // Make sure that pulling by an already cached digest but for a different ref (that should not have that digest) 116 // verifies with the remote that the digest exists in that repo. 117 func TestImagePullStoredDigestForOtherRepo(t *testing.T) { 118 skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon") 119 skip.If(t, testEnv.DaemonInfo.OSType == "windows", "We don't run a test registry on Windows") 120 skip.If(t, testEnv.IsRootless, "Rootless has a different view of localhost (needed for test registry access)") 121 ctx := setupTest(t) 122 123 reg := registry.NewV2(t, registry.WithStdout(os.Stdout), registry.WithStderr(os.Stderr)) 124 defer reg.Close() 125 reg.WaitReady(t) 126 127 // First create an image and upload it to our local registry 128 // Then we'll download it so that we can make sure the content is available in dockerd's manifest cache. 129 // Then we'll try to pull the same digest but with a different repo name. 130 131 dir := t.TempDir() 132 store, err := local.NewStore(dir) 133 assert.NilError(t, err) 134 135 desc := createTestImage(ctx, t, store) 136 137 remote := path.Join(registry.DefaultURL, "test:latest") 138 139 c8dClient, err := containerd.New("", containerd.WithServices(containerd.WithContentStore(store))) 140 assert.NilError(t, err) 141 142 c8dClient.Push(ctx, remote, desc) 143 assert.NilError(t, err) 144 145 client := testEnv.APIClient() 146 rdr, err := client.ImagePull(ctx, remote, image.PullOptions{}) 147 assert.NilError(t, err) 148 defer rdr.Close() 149 _, err = io.Copy(io.Discard, rdr) 150 assert.Check(t, err) 151 152 // Now, pull a totally different repo with a the same digest 153 rdr, err = client.ImagePull(ctx, path.Join(registry.DefaultURL, "other:image@"+desc.Digest.String()), image.PullOptions{}) 154 if rdr != nil { 155 assert.Check(t, rdr.Close()) 156 } 157 assert.Assert(t, err != nil, "Expected error, got none: %v", err) 158 assert.Assert(t, errdefs.IsNotFound(err), err) 159 assert.Check(t, is.ErrorType(err, errdefs.IsNotFound)) 160 } 161 162 // TestImagePullNonExisting pulls non-existing images from the central registry, with different 163 // combinations of implicit tag and library prefix. 164 func TestImagePullNonExisting(t *testing.T) { 165 ctx := setupTest(t) 166 167 for _, ref := range []string{ 168 "asdfasdf:foobar", 169 "library/asdfasdf:foobar", 170 "asdfasdf", 171 "asdfasdf:latest", 172 "library/asdfasdf", 173 "library/asdfasdf:latest", 174 } { 175 ref := ref 176 all := strings.Contains(ref, ":") 177 t.Run(ref, func(t *testing.T) { 178 t.Parallel() 179 180 client := testEnv.APIClient() 181 rdr, err := client.ImagePull(ctx, ref, image.PullOptions{ 182 All: all, 183 }) 184 if err == nil { 185 rdr.Close() 186 } 187 188 expectedMsg := fmt.Sprintf("pull access denied for %s, repository does not exist or may require 'docker login'", "asdfasdf") 189 assert.Check(t, is.ErrorContains(err, expectedMsg)) 190 assert.Check(t, is.ErrorType(err, errdefs.IsNotFound)) 191 if all { 192 // pull -a on a nonexistent registry should fall back as well 193 assert.Check(t, !strings.Contains(err.Error(), "unauthorized"), `message should not contain "unauthorized"`) 194 } 195 }) 196 } 197 }