github.com/toplink-cn/moby@v0.0.0-20240305205811-460b4aebdf81/integration-cli/docker_cli_images_test.go (about) 1 package main 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "path/filepath" 8 "reflect" 9 "sort" 10 "strings" 11 "testing" 12 "time" 13 14 "github.com/docker/docker/integration-cli/cli" 15 "github.com/docker/docker/integration-cli/cli/build" 16 "github.com/docker/docker/pkg/stringid" 17 "gotest.tools/v3/assert" 18 is "gotest.tools/v3/assert/cmp" 19 "gotest.tools/v3/icmd" 20 ) 21 22 type DockerCLIImagesSuite struct { 23 ds *DockerSuite 24 } 25 26 func (s *DockerCLIImagesSuite) TearDownTest(ctx context.Context, c *testing.T) { 27 s.ds.TearDownTest(ctx, c) 28 } 29 30 func (s *DockerCLIImagesSuite) OnTimeout(c *testing.T) { 31 s.ds.OnTimeout(c) 32 } 33 34 func (s *DockerCLIImagesSuite) TestImagesEnsureImageIsListed(c *testing.T) { 35 imagesOut := cli.DockerCmd(c, "images").Stdout() 36 assert.Assert(c, strings.Contains(imagesOut, "busybox")) 37 } 38 39 func (s *DockerCLIImagesSuite) TestImagesEnsureImageWithTagIsListed(c *testing.T) { 40 const name = "imagewithtag" 41 cli.DockerCmd(c, "tag", "busybox", name+":v1") 42 cli.DockerCmd(c, "tag", "busybox", name+":v1v1") 43 cli.DockerCmd(c, "tag", "busybox", name+":v2") 44 45 imagesOut := cli.DockerCmd(c, "images", name+":v1").Stdout() 46 assert.Assert(c, strings.Contains(imagesOut, name)) 47 assert.Assert(c, strings.Contains(imagesOut, "v1")) 48 assert.Assert(c, !strings.Contains(imagesOut, "v2")) 49 assert.Assert(c, !strings.Contains(imagesOut, "v1v1")) 50 imagesOut = cli.DockerCmd(c, "images", name).Stdout() 51 assert.Assert(c, strings.Contains(imagesOut, name)) 52 assert.Assert(c, strings.Contains(imagesOut, "v1")) 53 assert.Assert(c, strings.Contains(imagesOut, "v1v1")) 54 assert.Assert(c, strings.Contains(imagesOut, "v2")) 55 } 56 57 func (s *DockerCLIImagesSuite) TestImagesEnsureImageWithBadTagIsNotListed(c *testing.T) { 58 imagesOut := cli.DockerCmd(c, "images", "busybox:nonexistent").Stdout() 59 assert.Assert(c, !strings.Contains(imagesOut, "busybox")) 60 } 61 62 func (s *DockerCLIImagesSuite) TestImagesOrderedByCreationDate(c *testing.T) { 63 buildImageSuccessfully(c, "order:test_a", build.WithDockerfile(`FROM busybox 64 MAINTAINER dockerio1`)) 65 id1 := getIDByName(c, "order:test_a") 66 time.Sleep(1 * time.Second) 67 buildImageSuccessfully(c, "order:test_c", build.WithDockerfile(`FROM busybox 68 MAINTAINER dockerio2`)) 69 id2 := getIDByName(c, "order:test_c") 70 time.Sleep(1 * time.Second) 71 buildImageSuccessfully(c, "order:test_b", build.WithDockerfile(`FROM busybox 72 MAINTAINER dockerio3`)) 73 id3 := getIDByName(c, "order:test_b") 74 75 out := cli.DockerCmd(c, "images", "-q", "--no-trunc").Stdout() 76 imgs := strings.Split(out, "\n") 77 assert.Equal(c, imgs[0], id3, fmt.Sprintf("First image must be %s, got %s", id3, imgs[0])) 78 assert.Equal(c, imgs[1], id2, fmt.Sprintf("First image must be %s, got %s", id2, imgs[1])) 79 assert.Equal(c, imgs[2], id1, fmt.Sprintf("First image must be %s, got %s", id1, imgs[2])) 80 } 81 82 func (s *DockerCLIImagesSuite) TestImagesErrorWithInvalidFilterNameTest(c *testing.T) { 83 out, _, err := dockerCmdWithError("images", "-f", "FOO=123") 84 assert.ErrorContains(c, err, "") 85 assert.Assert(c, strings.Contains(out, "invalid filter")) 86 } 87 88 func (s *DockerCLIImagesSuite) TestImagesFilterLabelMatch(c *testing.T) { 89 const imageName1 = "images_filter_test1" 90 const imageName2 = "images_filter_test2" 91 const imageName3 = "images_filter_test3" 92 buildImageSuccessfully(c, imageName1, build.WithDockerfile(`FROM busybox 93 LABEL match me`)) 94 image1ID := getIDByName(c, imageName1) 95 96 buildImageSuccessfully(c, imageName2, build.WithDockerfile(`FROM busybox 97 LABEL match="me too"`)) 98 image2ID := getIDByName(c, imageName2) 99 100 buildImageSuccessfully(c, imageName3, build.WithDockerfile(`FROM busybox 101 LABEL nomatch me`)) 102 image3ID := getIDByName(c, imageName3) 103 104 out := cli.DockerCmd(c, "images", "--no-trunc", "-q", "-f", "label=match").Stdout() 105 out = strings.TrimSpace(out) 106 assert.Assert(c, is.Regexp(fmt.Sprintf("^[\\s\\w:]*%s[\\s\\w:]*$", image1ID), out)) 107 108 assert.Assert(c, is.Regexp(fmt.Sprintf("^[\\s\\w:]*%s[\\s\\w:]*$", image2ID), out)) 109 110 assert.Assert(c, !is.Regexp(fmt.Sprintf("^[\\s\\w:]*%s[\\s\\w:]*$", image3ID), out)().Success()) 111 112 out = cli.DockerCmd(c, "images", "--no-trunc", "-q", "-f", "label=match=me too").Stdout() 113 out = strings.TrimSpace(out) 114 assert.Equal(c, out, image2ID) 115 } 116 117 // Regression : #15659 118 func (s *DockerCLIImagesSuite) TestCommitWithFilterLabel(c *testing.T) { 119 // Create a container 120 cli.DockerCmd(c, "run", "--name", "bar", "busybox", "/bin/sh") 121 // Commit with labels "using changes" 122 imageID := cli.DockerCmd(c, "commit", "-c", "LABEL foo.version=1.0.0-1", "-c", "LABEL foo.name=bar", "-c", "LABEL foo.author=starlord", "bar", "bar:1.0.0-1").Stdout() 123 imageID = strings.TrimSpace(imageID) 124 125 out := cli.DockerCmd(c, "images", "--no-trunc", "-q", "-f", "label=foo.version=1.0.0-1").Stdout() 126 out = strings.TrimSpace(out) 127 assert.Equal(c, out, imageID) 128 } 129 130 func (s *DockerCLIImagesSuite) TestImagesFilterSinceAndBefore(c *testing.T) { 131 buildImageSuccessfully(c, "image:1", build.WithDockerfile(`FROM `+minimalBaseImage()+` 132 LABEL number=1`)) 133 imageID1 := getIDByName(c, "image:1") 134 buildImageSuccessfully(c, "image:2", build.WithDockerfile(`FROM `+minimalBaseImage()+` 135 LABEL number=2`)) 136 imageID2 := getIDByName(c, "image:2") 137 buildImageSuccessfully(c, "image:3", build.WithDockerfile(`FROM `+minimalBaseImage()+` 138 LABEL number=3`)) 139 imageID3 := getIDByName(c, "image:3") 140 141 expected := []string{imageID3, imageID2} 142 143 out := cli.DockerCmd(c, "images", "-f", "since=image:1", "image").Stdout() 144 assert.Equal(c, assertImageList(out, expected), true, fmt.Sprintf("SINCE filter: Image list is not in the correct order: %v\n%s", expected, out)) 145 146 out = cli.DockerCmd(c, "images", "-f", "since="+imageID1, "image").Stdout() 147 assert.Equal(c, assertImageList(out, expected), true, fmt.Sprintf("SINCE filter: Image list is not in the correct order: %v\n%s", expected, out)) 148 149 expected = []string{imageID3} 150 151 out = cli.DockerCmd(c, "images", "-f", "since=image:2", "image").Stdout() 152 assert.Equal(c, assertImageList(out, expected), true, fmt.Sprintf("SINCE filter: Image list is not in the correct order: %v\n%s", expected, out)) 153 154 out = cli.DockerCmd(c, "images", "-f", "since="+imageID2, "image").Stdout() 155 assert.Equal(c, assertImageList(out, expected), true, fmt.Sprintf("SINCE filter: Image list is not in the correct order: %v\n%s", expected, out)) 156 157 expected = []string{imageID2, imageID1} 158 159 out = cli.DockerCmd(c, "images", "-f", "before=image:3", "image").Stdout() 160 assert.Equal(c, assertImageList(out, expected), true, fmt.Sprintf("BEFORE filter: Image list is not in the correct order: %v\n%s", expected, out)) 161 162 out = cli.DockerCmd(c, "images", "-f", "before="+imageID3, "image").Stdout() 163 assert.Equal(c, assertImageList(out, expected), true, fmt.Sprintf("BEFORE filter: Image list is not in the correct order: %v\n%s", expected, out)) 164 165 expected = []string{imageID1} 166 167 out = cli.DockerCmd(c, "images", "-f", "before=image:2", "image").Stdout() 168 assert.Equal(c, assertImageList(out, expected), true, fmt.Sprintf("BEFORE filter: Image list is not in the correct order: %v\n%s", expected, out)) 169 170 out = cli.DockerCmd(c, "images", "-f", "before="+imageID2, "image").Stdout() 171 assert.Equal(c, assertImageList(out, expected), true, fmt.Sprintf("BEFORE filter: Image list is not in the correct order: %v\n%s", expected, out)) 172 } 173 174 func assertImageList(out string, expected []string) bool { 175 lines := strings.Split(strings.Trim(out, "\n "), "\n") 176 177 if len(lines)-1 != len(expected) { 178 return false 179 } 180 181 imageIDIndex := strings.Index(lines[0], "IMAGE ID") 182 for i := 0; i < len(expected); i++ { 183 imageID := lines[i+1][imageIDIndex : imageIDIndex+12] 184 found := false 185 for _, e := range expected { 186 if imageID == e[7:19] { 187 found = true 188 break 189 } 190 } 191 if !found { 192 return false 193 } 194 } 195 196 return true 197 } 198 199 // FIXME(vdemeester) should be a unit test on `docker image ls` 200 func (s *DockerCLIImagesSuite) TestImagesFilterSpaceTrimCase(c *testing.T) { 201 const imageName = "images_filter_test" 202 // Build a image and fail to build so that we have dangling images ? 203 buildImage(imageName, build.WithDockerfile(`FROM busybox 204 RUN touch /test/foo 205 RUN touch /test/bar 206 RUN touch /test/baz`)).Assert(c, icmd.Expected{ 207 ExitCode: 1, 208 }) 209 210 filters := []string{ 211 "dangling=true", 212 "Dangling=true", 213 " dangling=true", 214 "dangling=true ", 215 "dangling = true", 216 } 217 218 imageListings := make([][]string, 5) 219 for idx, filter := range filters { 220 out := cli.DockerCmd(c, "images", "-q", "-f", filter).Stdout() 221 listing := strings.Split(out, "\n") 222 sort.Strings(listing) 223 imageListings[idx] = listing 224 } 225 226 for idx, listing := range imageListings { 227 if idx < 4 && !reflect.DeepEqual(listing, imageListings[idx+1]) { 228 for idx, errListing := range imageListings { 229 fmt.Printf("out %d\n", idx) 230 for _, img := range errListing { 231 fmt.Print(img) 232 } 233 fmt.Print("") 234 } 235 c.Fatalf("All output must be the same") 236 } 237 } 238 } 239 240 func (s *DockerCLIImagesSuite) TestImagesEnsureDanglingImageOnlyListedOnce(c *testing.T) { 241 testRequires(c, DaemonIsLinux) 242 // create container 1 243 containerID1 := cli.DockerCmd(c, "run", "-d", "busybox", "true").Stdout() 244 containerID1 = strings.TrimSpace(containerID1) 245 246 // tag as foobox 247 imageID := cli.DockerCmd(c, "commit", containerID1, "foobox").Stdout() 248 imageID = stringid.TruncateID(strings.TrimSpace(imageID)) 249 250 // overwrite the tag, making the previous image dangling 251 cli.DockerCmd(c, "tag", "busybox", "foobox") 252 253 out := cli.DockerCmd(c, "images", "-q", "-f", "dangling=true").Stdout() 254 // Expect one dangling image 255 assert.Equal(c, strings.Count(out, imageID), 1) 256 257 out = cli.DockerCmd(c, "images", "-q", "-f", "dangling=false").Stdout() 258 // dangling=false would not include dangling images 259 assert.Assert(c, !strings.Contains(out, imageID)) 260 out = cli.DockerCmd(c, "images").Stdout() 261 // docker images still include dangling images 262 assert.Assert(c, strings.Contains(out, imageID)) 263 } 264 265 // FIXME(vdemeester) should be a unit test for `docker image ls` 266 func (s *DockerCLIImagesSuite) TestImagesWithIncorrectFilter(c *testing.T) { 267 out, _, err := dockerCmdWithError("images", "-f", "dangling=invalid") 268 assert.ErrorContains(c, err, "") 269 assert.Assert(c, strings.Contains(out, "invalid filter")) 270 } 271 272 func (s *DockerCLIImagesSuite) TestImagesEnsureOnlyHeadsImagesShown(c *testing.T) { 273 const dockerfile = ` 274 FROM busybox 275 MAINTAINER docker 276 ENV foo bar` 277 const name = "scratch-image" 278 result := buildImage(name, build.WithDockerfile(dockerfile)) 279 result.Assert(c, icmd.Success) 280 id := getIDByName(c, name) 281 282 // this is just the output of docker build 283 // we're interested in getting the image id of the MAINTAINER instruction 284 // and that's located at output, line 5, from 7 to end 285 split := strings.Split(result.Combined(), "\n") 286 intermediate := strings.TrimSpace(split[5][7:]) 287 288 out := cli.DockerCmd(c, "images").Stdout() 289 // images shouldn't show non-heads images 290 assert.Assert(c, !strings.Contains(out, intermediate)) 291 // images should contain final built images 292 assert.Assert(c, strings.Contains(out, stringid.TruncateID(id))) 293 } 294 295 func (s *DockerCLIImagesSuite) TestImagesEnsureImagesFromScratchShown(c *testing.T) { 296 testRequires(c, DaemonIsLinux) // Windows does not support FROM scratch 297 const dockerfile = ` 298 FROM scratch 299 MAINTAINER docker` 300 301 const name = "scratch-image" 302 buildImageSuccessfully(c, name, build.WithDockerfile(dockerfile)) 303 id := getIDByName(c, name) 304 305 out := cli.DockerCmd(c, "images").Stdout() 306 // images should contain images built from scratch 307 assert.Assert(c, strings.Contains(out, stringid.TruncateID(id))) 308 } 309 310 // For W2W - equivalent to TestImagesEnsureImagesFromScratchShown but Windows 311 // doesn't support from scratch 312 func (s *DockerCLIImagesSuite) TestImagesEnsureImagesFromBusyboxShown(c *testing.T) { 313 const dockerfile = ` 314 FROM busybox 315 MAINTAINER docker` 316 const name = "busybox-image" 317 318 buildImageSuccessfully(c, name, build.WithDockerfile(dockerfile)) 319 id := getIDByName(c, name) 320 321 out := cli.DockerCmd(c, "images").Stdout() 322 // images should contain images built from busybox 323 assert.Assert(c, strings.Contains(out, stringid.TruncateID(id))) 324 } 325 326 // #18181 327 func (s *DockerCLIImagesSuite) TestImagesFilterNameWithPort(c *testing.T) { 328 const tag = "a.b.c.d:5000/hello" 329 cli.DockerCmd(c, "tag", "busybox", tag) 330 out := cli.DockerCmd(c, "images", tag).Stdout() 331 assert.Assert(c, strings.Contains(out, tag)) 332 out = cli.DockerCmd(c, "images", tag+":latest").Stdout() 333 assert.Assert(c, strings.Contains(out, tag)) 334 out = cli.DockerCmd(c, "images", tag+":no-such-tag").Stdout() 335 assert.Assert(c, !strings.Contains(out, tag)) 336 } 337 338 func (s *DockerCLIImagesSuite) TestImagesFormat(c *testing.T) { 339 // testRequires(c, DaemonIsLinux) 340 const imageName = "myimage" 341 cli.DockerCmd(c, "tag", "busybox", imageName+":v1") 342 cli.DockerCmd(c, "tag", "busybox", imageName+":v2") 343 344 out := cli.DockerCmd(c, "images", "--format", "{{.Repository}}", imageName).Stdout() 345 lines := strings.Split(strings.TrimSpace(out), "\n") 346 347 expected := []string{imageName, imageName} 348 var names []string 349 names = append(names, lines...) 350 assert.Assert(c, is.DeepEqual(names, expected), "Expected array with truncated names: %v, got: %v", expected, names) 351 } 352 353 // ImagesDefaultFormatAndQuiet 354 func (s *DockerCLIImagesSuite) TestImagesFormatDefaultFormat(c *testing.T) { 355 testRequires(c, DaemonIsLinux) 356 357 // create container 1 358 containerID1 := cli.DockerCmd(c, "run", "-d", "busybox", "true").Stdout() 359 containerID1 = strings.TrimSpace(containerID1) 360 361 // tag as foobox 362 imageID := cli.DockerCmd(c, "commit", containerID1, "myimage").Stdout() 363 imageID = stringid.TruncateID(strings.TrimSpace(imageID)) 364 365 const config = `{ 366 "imagesFormat": "{{ .ID }} default" 367 }` 368 d, err := os.MkdirTemp("", "integration-cli-") 369 assert.NilError(c, err) 370 defer os.RemoveAll(d) 371 372 err = os.WriteFile(filepath.Join(d, "config.json"), []byte(config), 0o644) 373 assert.NilError(c, err) 374 375 out := cli.DockerCmd(c, "--config", d, "images", "-q", "myimage").Stdout() 376 assert.Equal(c, out, imageID+"\n", "Expected to print only the image id, got %v\n", out) 377 }