github.com/rita33cool1/iot-system-gateway@v0.0.0-20200911033302-e65bde238cc5/docker-engine/integration-cli/docker_api_build_test.go (about) 1 package main 2 3 import ( 4 "archive/tar" 5 "bytes" 6 "encoding/json" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "net/http" 11 "regexp" 12 "strings" 13 14 "github.com/docker/docker/api/types" 15 "github.com/docker/docker/integration-cli/checker" 16 "github.com/docker/docker/integration-cli/request" 17 "github.com/docker/docker/internal/test/fakecontext" 18 "github.com/docker/docker/internal/test/fakegit" 19 "github.com/docker/docker/internal/test/fakestorage" 20 "github.com/go-check/check" 21 "github.com/gotestyourself/gotestyourself/assert" 22 is "github.com/gotestyourself/gotestyourself/assert/cmp" 23 "golang.org/x/net/context" 24 ) 25 26 func (s *DockerSuite) TestBuildAPIDockerFileRemote(c *check.C) { 27 testRequires(c, NotUserNamespace) 28 29 var testD string 30 if testEnv.OSType == "windows" { 31 testD = `FROM busybox 32 RUN find / -name ba* 33 RUN find /tmp/` 34 } else { 35 // -xdev is required because sysfs can cause EPERM 36 testD = `FROM busybox 37 RUN find / -xdev -name ba* 38 RUN find /tmp/` 39 } 40 server := fakestorage.New(c, "", fakecontext.WithFiles(map[string]string{"testD": testD})) 41 defer server.Close() 42 43 res, body, err := request.Post("/build?dockerfile=baz&remote="+server.URL()+"/testD", request.JSON) 44 c.Assert(err, checker.IsNil) 45 c.Assert(res.StatusCode, checker.Equals, http.StatusOK) 46 47 buf, err := request.ReadBody(body) 48 c.Assert(err, checker.IsNil) 49 50 // Make sure Dockerfile exists. 51 // Make sure 'baz' doesn't exist ANYWHERE despite being mentioned in the URL 52 out := string(buf) 53 c.Assert(out, checker.Contains, "RUN find /tmp") 54 c.Assert(out, checker.Not(checker.Contains), "baz") 55 } 56 57 func (s *DockerSuite) TestBuildAPIRemoteTarballContext(c *check.C) { 58 buffer := new(bytes.Buffer) 59 tw := tar.NewWriter(buffer) 60 defer tw.Close() 61 62 dockerfile := []byte("FROM busybox") 63 err := tw.WriteHeader(&tar.Header{ 64 Name: "Dockerfile", 65 Size: int64(len(dockerfile)), 66 }) 67 // failed to write tar file header 68 c.Assert(err, checker.IsNil) 69 70 _, err = tw.Write(dockerfile) 71 // failed to write tar file content 72 c.Assert(err, checker.IsNil) 73 74 // failed to close tar archive 75 c.Assert(tw.Close(), checker.IsNil) 76 77 server := fakestorage.New(c, "", fakecontext.WithBinaryFiles(map[string]*bytes.Buffer{ 78 "testT.tar": buffer, 79 })) 80 defer server.Close() 81 82 res, b, err := request.Post("/build?remote="+server.URL()+"/testT.tar", request.ContentType("application/tar")) 83 c.Assert(err, checker.IsNil) 84 c.Assert(res.StatusCode, checker.Equals, http.StatusOK) 85 b.Close() 86 } 87 88 func (s *DockerSuite) TestBuildAPIRemoteTarballContextWithCustomDockerfile(c *check.C) { 89 buffer := new(bytes.Buffer) 90 tw := tar.NewWriter(buffer) 91 defer tw.Close() 92 93 dockerfile := []byte(`FROM busybox 94 RUN echo 'wrong'`) 95 err := tw.WriteHeader(&tar.Header{ 96 Name: "Dockerfile", 97 Size: int64(len(dockerfile)), 98 }) 99 // failed to write tar file header 100 c.Assert(err, checker.IsNil) 101 102 _, err = tw.Write(dockerfile) 103 // failed to write tar file content 104 c.Assert(err, checker.IsNil) 105 106 custom := []byte(`FROM busybox 107 RUN echo 'right' 108 `) 109 err = tw.WriteHeader(&tar.Header{ 110 Name: "custom", 111 Size: int64(len(custom)), 112 }) 113 114 // failed to write tar file header 115 c.Assert(err, checker.IsNil) 116 117 _, err = tw.Write(custom) 118 // failed to write tar file content 119 c.Assert(err, checker.IsNil) 120 121 // failed to close tar archive 122 c.Assert(tw.Close(), checker.IsNil) 123 124 server := fakestorage.New(c, "", fakecontext.WithBinaryFiles(map[string]*bytes.Buffer{ 125 "testT.tar": buffer, 126 })) 127 defer server.Close() 128 129 url := "/build?dockerfile=custom&remote=" + server.URL() + "/testT.tar" 130 res, body, err := request.Post(url, request.ContentType("application/tar")) 131 c.Assert(err, checker.IsNil) 132 c.Assert(res.StatusCode, checker.Equals, http.StatusOK) 133 134 defer body.Close() 135 content, err := request.ReadBody(body) 136 c.Assert(err, checker.IsNil) 137 138 // Build used the wrong dockerfile. 139 c.Assert(string(content), checker.Not(checker.Contains), "wrong") 140 } 141 142 func (s *DockerSuite) TestBuildAPILowerDockerfile(c *check.C) { 143 git := fakegit.New(c, "repo", map[string]string{ 144 "dockerfile": `FROM busybox 145 RUN echo from dockerfile`, 146 }, false) 147 defer git.Close() 148 149 res, body, err := request.Post("/build?remote="+git.RepoURL, request.JSON) 150 c.Assert(err, checker.IsNil) 151 c.Assert(res.StatusCode, checker.Equals, http.StatusOK) 152 153 buf, err := request.ReadBody(body) 154 c.Assert(err, checker.IsNil) 155 156 out := string(buf) 157 c.Assert(out, checker.Contains, "from dockerfile") 158 } 159 160 func (s *DockerSuite) TestBuildAPIBuildGitWithF(c *check.C) { 161 git := fakegit.New(c, "repo", map[string]string{ 162 "baz": `FROM busybox 163 RUN echo from baz`, 164 "Dockerfile": `FROM busybox 165 RUN echo from Dockerfile`, 166 }, false) 167 defer git.Close() 168 169 // Make sure it tries to 'dockerfile' query param value 170 res, body, err := request.Post("/build?dockerfile=baz&remote="+git.RepoURL, request.JSON) 171 c.Assert(err, checker.IsNil) 172 c.Assert(res.StatusCode, checker.Equals, http.StatusOK) 173 174 buf, err := request.ReadBody(body) 175 c.Assert(err, checker.IsNil) 176 177 out := string(buf) 178 c.Assert(out, checker.Contains, "from baz") 179 } 180 181 func (s *DockerSuite) TestBuildAPIDoubleDockerfile(c *check.C) { 182 testRequires(c, UnixCli) // dockerfile overwrites Dockerfile on Windows 183 git := fakegit.New(c, "repo", map[string]string{ 184 "Dockerfile": `FROM busybox 185 RUN echo from Dockerfile`, 186 "dockerfile": `FROM busybox 187 RUN echo from dockerfile`, 188 }, false) 189 defer git.Close() 190 191 // Make sure it tries to 'dockerfile' query param value 192 res, body, err := request.Post("/build?remote="+git.RepoURL, request.JSON) 193 c.Assert(err, checker.IsNil) 194 c.Assert(res.StatusCode, checker.Equals, http.StatusOK) 195 196 buf, err := request.ReadBody(body) 197 c.Assert(err, checker.IsNil) 198 199 out := string(buf) 200 c.Assert(out, checker.Contains, "from Dockerfile") 201 } 202 203 func (s *DockerSuite) TestBuildAPIUnnormalizedTarPaths(c *check.C) { 204 // Make sure that build context tars with entries of the form 205 // x/./y don't cause caching false positives. 206 207 buildFromTarContext := func(fileContents []byte) string { 208 buffer := new(bytes.Buffer) 209 tw := tar.NewWriter(buffer) 210 defer tw.Close() 211 212 dockerfile := []byte(`FROM busybox 213 COPY dir /dir/`) 214 err := tw.WriteHeader(&tar.Header{ 215 Name: "Dockerfile", 216 Size: int64(len(dockerfile)), 217 }) 218 //failed to write tar file header 219 c.Assert(err, checker.IsNil) 220 221 _, err = tw.Write(dockerfile) 222 // failed to write Dockerfile in tar file content 223 c.Assert(err, checker.IsNil) 224 225 err = tw.WriteHeader(&tar.Header{ 226 Name: "dir/./file", 227 Size: int64(len(fileContents)), 228 }) 229 //failed to write tar file header 230 c.Assert(err, checker.IsNil) 231 232 _, err = tw.Write(fileContents) 233 // failed to write file contents in tar file content 234 c.Assert(err, checker.IsNil) 235 236 // failed to close tar archive 237 c.Assert(tw.Close(), checker.IsNil) 238 239 res, body, err := request.Post("/build", request.RawContent(ioutil.NopCloser(buffer)), request.ContentType("application/x-tar")) 240 c.Assert(err, checker.IsNil) 241 c.Assert(res.StatusCode, checker.Equals, http.StatusOK) 242 243 out, err := request.ReadBody(body) 244 c.Assert(err, checker.IsNil) 245 lines := strings.Split(string(out), "\n") 246 c.Assert(len(lines), checker.GreaterThan, 1) 247 c.Assert(lines[len(lines)-2], checker.Matches, ".*Successfully built [0-9a-f]{12}.*") 248 249 re := regexp.MustCompile("Successfully built ([0-9a-f]{12})") 250 matches := re.FindStringSubmatch(lines[len(lines)-2]) 251 return matches[1] 252 } 253 254 imageA := buildFromTarContext([]byte("abc")) 255 imageB := buildFromTarContext([]byte("def")) 256 257 c.Assert(imageA, checker.Not(checker.Equals), imageB) 258 } 259 260 func (s *DockerSuite) TestBuildOnBuildWithCopy(c *check.C) { 261 dockerfile := ` 262 FROM ` + minimalBaseImage() + ` as onbuildbase 263 ONBUILD COPY file /file 264 265 FROM onbuildbase 266 ` 267 ctx := fakecontext.New(c, "", 268 fakecontext.WithDockerfile(dockerfile), 269 fakecontext.WithFile("file", "some content"), 270 ) 271 defer ctx.Close() 272 273 res, body, err := request.Post( 274 "/build", 275 request.RawContent(ctx.AsTarReader(c)), 276 request.ContentType("application/x-tar")) 277 c.Assert(err, checker.IsNil) 278 c.Assert(res.StatusCode, checker.Equals, http.StatusOK) 279 280 out, err := request.ReadBody(body) 281 c.Assert(err, checker.IsNil) 282 c.Assert(string(out), checker.Contains, "Successfully built") 283 } 284 285 func (s *DockerSuite) TestBuildOnBuildCache(c *check.C) { 286 build := func(dockerfile string) []byte { 287 ctx := fakecontext.New(c, "", 288 fakecontext.WithDockerfile(dockerfile), 289 ) 290 defer ctx.Close() 291 292 res, body, err := request.Post( 293 "/build", 294 request.RawContent(ctx.AsTarReader(c)), 295 request.ContentType("application/x-tar")) 296 assert.NilError(c, err) 297 assert.Check(c, is.DeepEqual(http.StatusOK, res.StatusCode)) 298 299 out, err := request.ReadBody(body) 300 assert.NilError(c, err) 301 assert.Check(c, is.Contains(string(out), "Successfully built")) 302 return out 303 } 304 305 dockerfile := ` 306 FROM ` + minimalBaseImage() + ` as onbuildbase 307 ENV something=bar 308 ONBUILD ENV foo=bar 309 ` 310 build(dockerfile) 311 312 dockerfile += "FROM onbuildbase" 313 out := build(dockerfile) 314 315 imageIDs := getImageIDsFromBuild(c, out) 316 assert.Check(c, is.Len(imageIDs, 2)) 317 parentID, childID := imageIDs[0], imageIDs[1] 318 319 client := testEnv.APIClient() 320 321 // check parentID is correct 322 image, _, err := client.ImageInspectWithRaw(context.Background(), childID) 323 assert.NilError(c, err) 324 assert.Check(c, is.Equal(parentID, image.Parent)) 325 } 326 327 func (s *DockerRegistrySuite) TestBuildCopyFromForcePull(c *check.C) { 328 client := testEnv.APIClient() 329 330 repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL) 331 // tag the image to upload it to the private registry 332 err := client.ImageTag(context.TODO(), "busybox", repoName) 333 assert.Check(c, err) 334 // push the image to the registry 335 rc, err := client.ImagePush(context.TODO(), repoName, types.ImagePushOptions{RegistryAuth: "{}"}) 336 assert.Check(c, err) 337 _, err = io.Copy(ioutil.Discard, rc) 338 assert.Check(c, err) 339 340 dockerfile := fmt.Sprintf(` 341 FROM %s AS foo 342 RUN touch abc 343 FROM %s 344 COPY --from=foo /abc / 345 `, repoName, repoName) 346 347 ctx := fakecontext.New(c, "", 348 fakecontext.WithDockerfile(dockerfile), 349 ) 350 defer ctx.Close() 351 352 res, body, err := request.Post( 353 "/build?pull=1", 354 request.RawContent(ctx.AsTarReader(c)), 355 request.ContentType("application/x-tar")) 356 assert.NilError(c, err) 357 assert.Check(c, is.DeepEqual(http.StatusOK, res.StatusCode)) 358 359 out, err := request.ReadBody(body) 360 assert.NilError(c, err) 361 assert.Check(c, is.Contains(string(out), "Successfully built")) 362 } 363 364 func (s *DockerSuite) TestBuildAddRemoteNoDecompress(c *check.C) { 365 buffer := new(bytes.Buffer) 366 tw := tar.NewWriter(buffer) 367 dt := []byte("contents") 368 err := tw.WriteHeader(&tar.Header{ 369 Name: "foo", 370 Size: int64(len(dt)), 371 Mode: 0600, 372 Typeflag: tar.TypeReg, 373 }) 374 assert.NilError(c, err) 375 _, err = tw.Write(dt) 376 assert.NilError(c, err) 377 err = tw.Close() 378 assert.NilError(c, err) 379 380 server := fakestorage.New(c, "", fakecontext.WithBinaryFiles(map[string]*bytes.Buffer{ 381 "test.tar": buffer, 382 })) 383 defer server.Close() 384 385 dockerfile := fmt.Sprintf(` 386 FROM busybox 387 ADD %s/test.tar / 388 RUN [ -f test.tar ] 389 `, server.URL()) 390 391 ctx := fakecontext.New(c, "", 392 fakecontext.WithDockerfile(dockerfile), 393 ) 394 defer ctx.Close() 395 396 res, body, err := request.Post( 397 "/build", 398 request.RawContent(ctx.AsTarReader(c)), 399 request.ContentType("application/x-tar")) 400 assert.NilError(c, err) 401 assert.Check(c, is.DeepEqual(http.StatusOK, res.StatusCode)) 402 403 out, err := request.ReadBody(body) 404 assert.NilError(c, err) 405 assert.Check(c, is.Contains(string(out), "Successfully built")) 406 } 407 408 func (s *DockerSuite) TestBuildChownOnCopy(c *check.C) { 409 testRequires(c, DaemonIsLinux) 410 dockerfile := `FROM busybox 411 RUN echo 'test1:x:1001:1001::/bin:/bin/false' >> /etc/passwd 412 RUN echo 'test1:x:1001:' >> /etc/group 413 RUN echo 'test2:x:1002:' >> /etc/group 414 COPY --chown=test1:1002 . /new_dir 415 RUN ls -l / 416 RUN [ $(ls -l / | grep new_dir | awk '{print $3":"$4}') = 'test1:test2' ] 417 RUN [ $(ls -nl / | grep new_dir | awk '{print $3":"$4}') = '1001:1002' ] 418 ` 419 ctx := fakecontext.New(c, "", 420 fakecontext.WithDockerfile(dockerfile), 421 fakecontext.WithFile("test_file1", "some test content"), 422 ) 423 defer ctx.Close() 424 425 res, body, err := request.Post( 426 "/build", 427 request.RawContent(ctx.AsTarReader(c)), 428 request.ContentType("application/x-tar")) 429 c.Assert(err, checker.IsNil) 430 c.Assert(res.StatusCode, checker.Equals, http.StatusOK) 431 432 out, err := request.ReadBody(body) 433 assert.NilError(c, err) 434 assert.Check(c, is.Contains(string(out), "Successfully built")) 435 } 436 437 func (s *DockerSuite) TestBuildCopyCacheOnFileChange(c *check.C) { 438 439 dockerfile := `FROM busybox 440 COPY file /file` 441 442 ctx1 := fakecontext.New(c, "", 443 fakecontext.WithDockerfile(dockerfile), 444 fakecontext.WithFile("file", "foo")) 445 ctx2 := fakecontext.New(c, "", 446 fakecontext.WithDockerfile(dockerfile), 447 fakecontext.WithFile("file", "bar")) 448 449 var build = func(ctx *fakecontext.Fake) string { 450 res, body, err := request.Post("/build", 451 request.RawContent(ctx.AsTarReader(c)), 452 request.ContentType("application/x-tar")) 453 454 assert.NilError(c, err) 455 assert.Check(c, is.DeepEqual(http.StatusOK, res.StatusCode)) 456 457 out, err := request.ReadBody(body) 458 assert.NilError(c, err) 459 460 ids := getImageIDsFromBuild(c, out) 461 return ids[len(ids)-1] 462 } 463 464 id1 := build(ctx1) 465 id2 := build(ctx1) 466 id3 := build(ctx2) 467 468 if id1 != id2 { 469 c.Fatal("didn't use the cache") 470 } 471 if id1 == id3 { 472 c.Fatal("COPY With different source file should not share same cache") 473 } 474 } 475 476 func (s *DockerSuite) TestBuildAddCacheOnFileChange(c *check.C) { 477 478 dockerfile := `FROM busybox 479 ADD file /file` 480 481 ctx1 := fakecontext.New(c, "", 482 fakecontext.WithDockerfile(dockerfile), 483 fakecontext.WithFile("file", "foo")) 484 ctx2 := fakecontext.New(c, "", 485 fakecontext.WithDockerfile(dockerfile), 486 fakecontext.WithFile("file", "bar")) 487 488 var build = func(ctx *fakecontext.Fake) string { 489 res, body, err := request.Post("/build", 490 request.RawContent(ctx.AsTarReader(c)), 491 request.ContentType("application/x-tar")) 492 493 assert.NilError(c, err) 494 assert.Check(c, is.DeepEqual(http.StatusOK, res.StatusCode)) 495 496 out, err := request.ReadBody(body) 497 assert.NilError(c, err) 498 499 ids := getImageIDsFromBuild(c, out) 500 return ids[len(ids)-1] 501 } 502 503 id1 := build(ctx1) 504 id2 := build(ctx1) 505 id3 := build(ctx2) 506 507 if id1 != id2 { 508 c.Fatal("didn't use the cache") 509 } 510 if id1 == id3 { 511 c.Fatal("COPY With different source file should not share same cache") 512 } 513 } 514 515 func (s *DockerSuite) TestBuildScratchCopy(c *check.C) { 516 testRequires(c, DaemonIsLinux) 517 dockerfile := `FROM scratch 518 ADD Dockerfile / 519 ENV foo bar` 520 ctx := fakecontext.New(c, "", 521 fakecontext.WithDockerfile(dockerfile), 522 ) 523 defer ctx.Close() 524 525 res, body, err := request.Post( 526 "/build", 527 request.RawContent(ctx.AsTarReader(c)), 528 request.ContentType("application/x-tar")) 529 c.Assert(err, checker.IsNil) 530 c.Assert(res.StatusCode, checker.Equals, http.StatusOK) 531 532 out, err := request.ReadBody(body) 533 assert.NilError(c, err) 534 assert.Check(c, is.Contains(string(out), "Successfully built")) 535 } 536 537 type buildLine struct { 538 Stream string 539 Aux struct { 540 ID string 541 } 542 } 543 544 func getImageIDsFromBuild(c *check.C, output []byte) []string { 545 ids := []string{} 546 for _, line := range bytes.Split(output, []byte("\n")) { 547 if len(line) == 0 { 548 continue 549 } 550 entry := buildLine{} 551 assert.NilError(c, json.Unmarshal(line, &entry)) 552 if entry.Aux.ID != "" { 553 ids = append(ids, entry.Aux.ID) 554 } 555 } 556 return ids 557 }