github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/integration-cli/docker_cli_pull_test.go (about) 1 package main 2 3 import ( 4 "context" 5 "regexp" 6 "strings" 7 "testing" 8 "time" 9 10 "github.com/opencontainers/go-digest" 11 "gotest.tools/v3/assert" 12 is "gotest.tools/v3/assert/cmp" 13 "gotest.tools/v3/skip" 14 ) 15 16 type DockerCLIPullSuite struct { 17 ds *DockerSuite 18 } 19 20 func (s *DockerCLIPullSuite) TearDownTest(ctx context.Context, c *testing.T) { 21 s.ds.TearDownTest(ctx, c) 22 } 23 24 func (s *DockerCLIPullSuite) OnTimeout(c *testing.T) { 25 s.ds.OnTimeout(c) 26 } 27 28 // TestPullFromCentralRegistry pulls an image from the central registry and verifies that the client 29 // prints all expected output. 30 func (s *DockerHubPullSuite) TestPullFromCentralRegistry(c *testing.T) { 31 testRequires(c, DaemonIsLinux) 32 out := s.Cmd(c, "pull", "hello-world") 33 defer deleteImages("hello-world") 34 35 assert.Assert(c, strings.Contains(out, "Using default tag: latest"), "expected the 'latest' tag to be automatically assumed") 36 assert.Assert(c, strings.Contains(out, "Pulling from library/hello-world"), "expected the 'library/' prefix to be automatically assumed") 37 assert.Assert(c, strings.Contains(out, "Downloaded newer image for hello-world:latest")) 38 39 matches := regexp.MustCompile(`Digest: (.+)\n`).FindAllStringSubmatch(out, -1) 40 assert.Equal(c, len(matches), 1, "expected exactly one image digest in the output") 41 assert.Equal(c, len(matches[0]), 2, "unexpected number of submatches for the digest") 42 _, err := digest.Parse(matches[0][1]) 43 assert.NilError(c, err, "invalid digest %q in output", matches[0][1]) 44 45 // We should have a single entry in images. 46 img := strings.TrimSpace(s.Cmd(c, "images")) 47 splitImg := strings.Split(img, "\n") 48 assert.Equal(c, len(splitImg), 2) 49 match, _ := regexp.MatchString(`hello-world\s+latest.*?`, splitImg[1]) 50 assert.Assert(c, match, "invalid output for `docker images` (expected image and tag name)") 51 } 52 53 // TestPullFromCentralRegistryImplicitRefParts pulls an image from the central registry and verifies 54 // that pulling the same image with different combinations of implicit elements of the image 55 // reference (tag, repository, central registry url, ...) doesn't trigger a new pull nor leads to 56 // multiple images. 57 func (s *DockerHubPullSuite) TestPullFromCentralRegistryImplicitRefParts(c *testing.T) { 58 testRequires(c, DaemonIsLinux) 59 60 // Pull hello-world from v2 61 pullFromV2 := func(ref string) (int, string) { 62 out := s.Cmd(c, "pull", "hello-world") 63 v1Retries := 0 64 for strings.Contains(out, "this image was pulled from a legacy registry") { 65 // Some network errors may cause fallbacks to the v1 66 // protocol, which would violate the test's assumption 67 // that it will get the same images. To make the test 68 // more robust against these network glitches, allow a 69 // few retries if we end up with a v1 pull. 70 71 if v1Retries > 2 { 72 c.Fatalf("too many v1 fallback incidents when pulling %s", ref) 73 } 74 75 s.Cmd(c, "rmi", ref) 76 out = s.Cmd(c, "pull", ref) 77 78 v1Retries++ 79 } 80 81 return v1Retries, out 82 } 83 84 pullFromV2("hello-world") 85 defer deleteImages("hello-world") 86 87 s.Cmd(c, "tag", "hello-world", "hello-world-backup") 88 89 for _, ref := range []string{ 90 "hello-world", 91 "hello-world:latest", 92 "library/hello-world", 93 "library/hello-world:latest", 94 "docker.io/library/hello-world", 95 "index.docker.io/library/hello-world", 96 } { 97 var out string 98 for { 99 var v1Retries int 100 v1Retries, out = pullFromV2(ref) 101 102 // Keep repeating the test case until we don't hit a v1 103 // fallback case. We won't get the right "Image is up 104 // to date" message if the local image was replaced 105 // with one pulled from v1. 106 if v1Retries == 0 { 107 break 108 } 109 s.Cmd(c, "rmi", ref) 110 s.Cmd(c, "tag", "hello-world-backup", "hello-world") 111 } 112 assert.Assert(c, strings.Contains(out, "Image is up to date for hello-world:latest")) 113 } 114 115 s.Cmd(c, "rmi", "hello-world-backup") 116 117 // We should have a single entry in images. 118 img := strings.TrimSpace(s.Cmd(c, "images")) 119 splitImg := strings.Split(img, "\n") 120 assert.Equal(c, len(splitImg), 2) 121 match, _ := regexp.MatchString(`hello-world\s+latest.*?`, splitImg[1]) 122 assert.Assert(c, match, "invalid output for `docker images` (expected image and tag name)") 123 } 124 125 // TestPullScratchNotAllowed verifies that pulling 'scratch' is rejected. 126 func (s *DockerHubPullSuite) TestPullScratchNotAllowed(c *testing.T) { 127 testRequires(c, DaemonIsLinux) 128 out, err := s.CmdWithError("pull", "scratch") 129 assert.ErrorContains(c, err, "", "expected pull of scratch to fail") 130 assert.Assert(c, strings.Contains(out, "'scratch' is a reserved name")) 131 assert.Assert(c, !strings.Contains(out, "Pulling repository scratch")) 132 } 133 134 // TestPullAllTagsFromCentralRegistry pulls using `all-tags` for a given image and verifies that it 135 // results in more images than a naked pull. 136 func (s *DockerHubPullSuite) TestPullAllTagsFromCentralRegistry(c *testing.T) { 137 // See https://github.com/moby/moby/issues/46632 138 skip.If(c, testEnv.UsingSnapshotter, "The image dockercore/engine-pull-all-test-fixture is a hand-made image that contains an error in the manifest, the size is reported as 424 but its real size is 524, containerd fails to pull it because it checks that the sizes reported are right") 139 testRequires(c, DaemonIsLinux) 140 s.Cmd(c, "pull", "dockercore/engine-pull-all-test-fixture") 141 outImageCmd := s.Cmd(c, "images", "dockercore/engine-pull-all-test-fixture") 142 splitOutImageCmd := strings.Split(strings.TrimSpace(outImageCmd), "\n") 143 assert.Equal(c, len(splitOutImageCmd), 2) 144 145 s.Cmd(c, "pull", "--all-tags=true", "dockercore/engine-pull-all-test-fixture") 146 outImageAllTagCmd := s.Cmd(c, "images", "dockercore/engine-pull-all-test-fixture") 147 linesCount := strings.Count(outImageAllTagCmd, "\n") 148 assert.Assert(c, linesCount > 2, "pulling all tags should provide more than two images, got %s", outImageAllTagCmd) 149 150 // Verify that the line for 'dockercore/engine-pull-all-test-fixture:latest' is left unchanged. 151 var latestLine string 152 for _, line := range strings.Split(outImageAllTagCmd, "\n") { 153 if strings.HasPrefix(line, "dockercore/engine-pull-all-test-fixture") && strings.Contains(line, "latest") { 154 latestLine = line 155 break 156 } 157 } 158 assert.Assert(c, latestLine != "", "no entry for dockercore/engine-pull-all-test-fixture:latest found after pulling all tags") 159 160 splitLatest := strings.Fields(latestLine) 161 splitCurrent := strings.Fields(splitOutImageCmd[1]) 162 163 // Clear relative creation times, since these can easily change between 164 // two invocations of "docker images". Without this, the test can fail 165 // like this: 166 // ... obtained []string = []string{"busybox", "latest", "d9551b4026f0", "27", "minutes", "ago", "1.113", "MB"} 167 // ... expected []string = []string{"busybox", "latest", "d9551b4026f0", "26", "minutes", "ago", "1.113", "MB"} 168 splitLatest[3] = "" 169 splitLatest[4] = "" 170 splitLatest[5] = "" 171 splitCurrent[3] = "" 172 splitCurrent[4] = "" 173 splitCurrent[5] = "" 174 175 assert.Assert(c, is.DeepEqual(splitLatest, splitCurrent), "dockercore/engine-pull-all-test-fixture:latest was changed after pulling all tags") 176 } 177 178 // TestPullClientDisconnect kills the client during a pull operation and verifies that the operation 179 // gets cancelled. 180 // 181 // Ref: docker/docker#15589 182 func (s *DockerHubPullSuite) TestPullClientDisconnect(c *testing.T) { 183 testRequires(c, DaemonIsLinux) 184 const imgRepo = "hello-world:latest" 185 186 pullCmd := s.MakeCmd("pull", imgRepo) 187 stdout, err := pullCmd.StdoutPipe() 188 assert.NilError(c, err) 189 err = pullCmd.Start() 190 assert.NilError(c, err) 191 go pullCmd.Wait() 192 193 // Cancel as soon as we get some output. 194 buf := make([]byte, 10) 195 _, err = stdout.Read(buf) 196 assert.NilError(c, err) 197 198 err = pullCmd.Process.Kill() 199 assert.NilError(c, err) 200 201 time.Sleep(2 * time.Second) 202 _, err = s.CmdWithError("inspect", imgRepo) 203 assert.ErrorContains(c, err, "", "image was pulled after client disconnected") 204 } 205 206 // Regression test for https://github.com/Prakhar-Agarwal-byte/moby/issues/26429 207 func (s *DockerCLIPullSuite) TestPullLinuxImageFailsOnWindows(c *testing.T) { 208 testRequires(c, DaemonIsWindows, Network) 209 _, _, err := dockerCmdWithError("pull", "ubuntu") 210 assert.ErrorContains(c, err, "no matching manifest for windows") 211 } 212 213 // Regression test for https://github.com/Prakhar-Agarwal-byte/moby/issues/28892 214 func (s *DockerCLIPullSuite) TestPullWindowsImageFailsOnLinux(c *testing.T) { 215 testRequires(c, DaemonIsLinux, Network) 216 _, _, err := dockerCmdWithError("pull", "mcr.microsoft.com/windows/servercore:ltsc2022") 217 assert.ErrorContains(c, err, "no matching manifest for linux") 218 }