github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/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/Prakhar-Agarwal-byte/moby/api/types" 14 "github.com/Prakhar-Agarwal-byte/moby/api/types/versions" 15 "github.com/Prakhar-Agarwal-byte/moby/errdefs" 16 "github.com/Prakhar-Agarwal-byte/moby/testutil/registry" 17 "github.com/containerd/containerd" 18 "github.com/containerd/containerd/content" 19 "github.com/containerd/containerd/content/local" 20 "github.com/containerd/containerd/images" 21 "github.com/containerd/containerd/platforms" 22 "github.com/opencontainers/go-digest" 23 "github.com/opencontainers/image-spec/specs-go" 24 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 25 "gotest.tools/v3/assert" 26 is "gotest.tools/v3/assert/cmp" 27 "gotest.tools/v3/skip" 28 ) 29 30 func TestImagePullPlatformInvalid(t *testing.T) { 31 skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "experimental in older versions") 32 ctx := setupTest(t) 33 34 client := testEnv.APIClient() 35 36 _, err := client.ImagePull(ctx, "docker.io/library/hello-world:latest", types.ImagePullOptions{Platform: "foobar"}) 37 assert.Assert(t, err != nil) 38 assert.ErrorContains(t, err, "unknown operating system or architecture") 39 assert.Assert(t, errdefs.IsInvalidParameter(err)) 40 } 41 42 func createTestImage(ctx context.Context, t testing.TB, store content.Store) ocispec.Descriptor { 43 w, err := store.Writer(ctx, content.WithRef("layer")) 44 assert.NilError(t, err) 45 defer w.Close() 46 47 // Empty layer with just a root dir 48 const layer = `./0000775000000000000000000000000014201045023007702 5ustar rootroot` 49 50 _, err = w.Write([]byte(layer)) 51 assert.NilError(t, err) 52 53 err = w.Commit(ctx, int64(len(layer)), digest.FromBytes([]byte(layer))) 54 assert.NilError(t, err) 55 56 layerDigest := w.Digest() 57 w.Close() 58 59 img := ocispec.Image{ 60 Platform: platforms.DefaultSpec(), 61 RootFS: ocispec.RootFS{Type: "layers", DiffIDs: []digest.Digest{layerDigest}}, 62 Config: ocispec.ImageConfig{WorkingDir: "/"}, 63 } 64 imgJSON, err := json.Marshal(img) 65 assert.NilError(t, err) 66 67 w, err = store.Writer(ctx, content.WithRef("config")) 68 assert.NilError(t, err) 69 defer w.Close() 70 _, err = w.Write(imgJSON) 71 assert.NilError(t, err) 72 assert.NilError(t, w.Commit(ctx, int64(len(imgJSON)), digest.FromBytes(imgJSON))) 73 74 configDigest := w.Digest() 75 w.Close() 76 77 info, err := store.Info(ctx, layerDigest) 78 assert.NilError(t, err) 79 80 manifest := ocispec.Manifest{ 81 Versioned: specs.Versioned{ 82 SchemaVersion: 2, 83 }, 84 MediaType: images.MediaTypeDockerSchema2Manifest, 85 Config: ocispec.Descriptor{ 86 MediaType: images.MediaTypeDockerSchema2Config, 87 Digest: configDigest, 88 Size: int64(len(imgJSON)), 89 }, 90 Layers: []ocispec.Descriptor{{ 91 MediaType: images.MediaTypeDockerSchema2Layer, 92 Digest: layerDigest, 93 Size: info.Size, 94 }}, 95 } 96 97 manifestJSON, err := json.Marshal(manifest) 98 assert.NilError(t, err) 99 100 w, err = store.Writer(ctx, content.WithRef("manifest")) 101 assert.NilError(t, err) 102 defer w.Close() 103 _, err = w.Write(manifestJSON) 104 assert.NilError(t, err) 105 assert.NilError(t, w.Commit(ctx, int64(len(manifestJSON)), digest.FromBytes(manifestJSON))) 106 107 manifestDigest := w.Digest() 108 w.Close() 109 110 return ocispec.Descriptor{ 111 MediaType: images.MediaTypeDockerSchema2Manifest, 112 Digest: manifestDigest, 113 Size: int64(len(manifestJSON)), 114 } 115 } 116 117 // Make sure that pulling by an already cached digest but for a different ref (that should not have that digest) 118 // verifies with the remote that the digest exists in that repo. 119 func TestImagePullStoredfDigestForOtherRepo(t *testing.T) { 120 skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon") 121 skip.If(t, testEnv.DaemonInfo.OSType == "windows", "We don't run a test registry on Windows") 122 skip.If(t, testEnv.IsRootless, "Rootless has a different view of localhost (needed for test registry access)") 123 ctx := setupTest(t) 124 125 reg := registry.NewV2(t, registry.WithStdout(os.Stdout), registry.WithStderr(os.Stderr)) 126 defer reg.Close() 127 reg.WaitReady(t) 128 129 // First create an image and upload it to our local registry 130 // Then we'll download it so that we can make sure the content is available in dockerd's manifest cache. 131 // Then we'll try to pull the same digest but with a different repo name. 132 133 dir := t.TempDir() 134 store, err := local.NewStore(dir) 135 assert.NilError(t, err) 136 137 desc := createTestImage(ctx, t, store) 138 139 remote := path.Join(registry.DefaultURL, "test:latest") 140 141 c8dClient, err := containerd.New("", containerd.WithServices(containerd.WithContentStore(store))) 142 assert.NilError(t, err) 143 144 c8dClient.Push(ctx, remote, desc) 145 assert.NilError(t, err) 146 147 client := testEnv.APIClient() 148 rdr, err := client.ImagePull(ctx, remote, types.ImagePullOptions{}) 149 assert.NilError(t, err) 150 defer rdr.Close() 151 io.Copy(io.Discard, rdr) 152 153 // Now, pull a totally different repo with a the same digest 154 rdr, err = client.ImagePull(ctx, path.Join(registry.DefaultURL, "other:image@"+desc.Digest.String()), types.ImagePullOptions{}) 155 if rdr != nil { 156 rdr.Close() 157 } 158 assert.Assert(t, err != nil, "Expected error, got none: %v", err) 159 assert.Assert(t, errdefs.IsNotFound(err), err) 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, types.ImagePullOptions{ 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.Assert(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 }