github.com/chenbh/concourse/v6@v6.4.2/fly/integration/execute_test.go (about) 1 package integration_test 2 3 import ( 4 "archive/tar" 5 "compress/gzip" 6 "encoding/json" 7 "fmt" 8 "io/ioutil" 9 "net/http" 10 "net/url" 11 "os" 12 "os/exec" 13 "path" 14 "path/filepath" 15 "runtime" 16 "strings" 17 "syscall" 18 "time" 19 20 "github.com/chenbh/concourse/v6/atc" 21 "github.com/chenbh/concourse/v6/atc/event" 22 "github.com/chenbh/concourse/v6/atc/testhelpers" 23 . "github.com/onsi/ginkgo" 24 . "github.com/onsi/gomega" 25 "github.com/onsi/gomega/gbytes" 26 "github.com/onsi/gomega/gexec" 27 "github.com/onsi/gomega/ghttp" 28 "github.com/vito/go-sse/sse" 29 ) 30 31 var _ = Describe("Fly CLI", func() { 32 var tmpdir string 33 var buildDir string 34 var taskConfigPath string 35 36 var streaming chan struct{} 37 var events chan atc.Event 38 var uploadingBits <-chan struct{} 39 40 var expectedPlan atc.Plan 41 var taskPlan atc.Plan 42 var workerArtifact = atc.WorkerArtifact{ 43 ID: 125, 44 Name: "some-dir", 45 } 46 47 BeforeEach(func() { 48 var err error 49 tmpdir, err = ioutil.TempDir("", "fly-build-dir") 50 Expect(err).NotTo(HaveOccurred()) 51 52 buildDir = filepath.Join(tmpdir, "fixture") 53 54 err = os.Mkdir(buildDir, 0755) 55 Expect(err).NotTo(HaveOccurred()) 56 57 taskConfigPath = filepath.Join(buildDir, "task.yml") 58 59 err = ioutil.WriteFile( 60 taskConfigPath, 61 []byte(`--- 62 platform: some-platform 63 64 image_resource: 65 type: registry-image 66 source: 67 repository: ubuntu 68 69 inputs: 70 - name: fixture 71 72 params: 73 FOO: bar 74 BAZ: buzz 75 X: 1 76 EMPTY: 77 78 run: 79 path: find 80 args: [.] 81 `), 82 0644, 83 ) 84 Expect(err).NotTo(HaveOccurred()) 85 86 streaming = make(chan struct{}) 87 events = make(chan atc.Event) 88 89 planFactory := atc.NewPlanFactory(0) 90 91 taskPlan = planFactory.NewPlan(atc.TaskPlan{ 92 Name: "one-off", 93 Config: &atc.TaskConfig{ 94 Platform: "some-platform", 95 ImageResource: &atc.ImageResource{ 96 Type: "registry-image", 97 Source: atc.Source{ 98 "repository": "ubuntu", 99 }, 100 }, 101 Inputs: []atc.TaskInputConfig{ 102 {Name: "fixture"}, 103 }, 104 Params: map[string]string{ 105 "FOO": "bar", 106 "BAZ": "buzz", 107 "X": "1", 108 "EMPTY": "", 109 }, 110 Run: atc.TaskRunConfig{ 111 Path: "find", 112 Args: []string{"."}, 113 }, 114 }, 115 }) 116 117 expectedPlan = planFactory.NewPlan(atc.DoPlan{ 118 planFactory.NewPlan(atc.AggregatePlan{ 119 planFactory.NewPlan(atc.ArtifactInputPlan{ 120 ArtifactID: 125, 121 Name: filepath.Base(buildDir), 122 }), 123 }), 124 taskPlan, 125 }) 126 }) 127 128 AfterEach(func() { 129 os.RemoveAll(tmpdir) 130 }) 131 132 JustBeforeEach(func() { 133 uploading := make(chan struct{}) 134 uploadingBits = uploading 135 136 atcServer.RouteToHandler("POST", "/api/v1/teams/main/artifacts", 137 ghttp.CombineHandlers( 138 func(w http.ResponseWriter, req *http.Request) { 139 close(uploading) 140 141 gr, err := gzip.NewReader(req.Body) 142 Expect(err).NotTo(HaveOccurred()) 143 144 tr := tar.NewReader(gr) 145 146 hdr, err := tr.Next() 147 Expect(err).NotTo(HaveOccurred()) 148 149 Expect(hdr.Name).To(Equal("./")) 150 151 hdr, err = tr.Next() 152 Expect(err).NotTo(HaveOccurred()) 153 154 Expect(hdr.Name).To(MatchRegexp("(./)?task.yml$")) 155 }, 156 ghttp.RespondWith(201, `{"id":125}`), 157 ), 158 ) 159 atcServer.RouteToHandler("POST", "/api/v1/teams/main/builds", 160 ghttp.CombineHandlers( 161 ghttp.VerifyRequest("POST", "/api/v1/teams/main/builds"), 162 VerifyPlan(expectedPlan), 163 func(w http.ResponseWriter, r *http.Request) { 164 http.SetCookie(w, &http.Cookie{ 165 Name: "Some-Cookie", 166 Value: "some-cookie-data", 167 Path: "/", 168 Expires: time.Now().Add(1 * time.Minute), 169 }) 170 }, 171 ghttp.RespondWith(201, `{"id":128}`), 172 ), 173 ) 174 atcServer.RouteToHandler("GET", "/api/v1/builds/128/events", 175 ghttp.CombineHandlers( 176 ghttp.VerifyRequest("GET", "/api/v1/builds/128/events"), 177 func(w http.ResponseWriter, r *http.Request) { 178 flusher := w.(http.Flusher) 179 180 w.Header().Add("Content-Type", "text/event-stream; charset=utf-8") 181 w.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate") 182 w.Header().Add("Connection", "keep-alive") 183 184 w.WriteHeader(http.StatusOK) 185 186 flusher.Flush() 187 188 close(streaming) 189 190 id := 0 191 192 for e := range events { 193 payload, err := json.Marshal(event.Message{Event: e}) 194 Expect(err).NotTo(HaveOccurred()) 195 196 event := sse.Event{ 197 ID: fmt.Sprintf("%d", id), 198 Name: "event", 199 Data: payload, 200 } 201 202 err = event.Write(w) 203 Expect(err).NotTo(HaveOccurred()) 204 205 flusher.Flush() 206 207 id++ 208 } 209 210 err := sse.Event{ 211 Name: "end", 212 }.Write(w) 213 Expect(err).NotTo(HaveOccurred()) 214 }, 215 ), 216 ) 217 atcServer.RouteToHandler("GET", "/api/v1/builds/128/artifacts", 218 ghttp.RespondWithJSONEncoded(200, []atc.WorkerArtifact{workerArtifact}), 219 ) 220 221 }) 222 223 It("creates a build, streams output, uploads the bits, and polls until completion", func() { 224 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath) 225 flyCmd.Dir = buildDir 226 227 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 228 Expect(err).NotTo(HaveOccurred()) 229 230 Eventually(streaming).Should(BeClosed()) 231 232 buildURL, _ := url.Parse(atcServer.URL()) 233 buildURL.Path = path.Join(buildURL.Path, "builds/128") 234 Eventually(sess.Out).Should(gbytes.Say("executing build 128 at %s", buildURL.String())) 235 236 events <- event.Log{Payload: "sup"} 237 238 Eventually(sess.Out).Should(gbytes.Say("sup")) 239 240 close(events) 241 242 <-sess.Exited 243 Expect(sess.ExitCode()).To(Equal(0)) 244 245 Expect(uploadingBits).To(BeClosed()) 246 }) 247 248 Context("when there is a pipeline job with the same input", func() { 249 BeforeEach(func() { 250 taskPlan.Task.VersionedResourceTypes = atc.VersionedResourceTypes{ 251 atc.VersionedResourceType{ 252 ResourceType: atc.ResourceType{ 253 Name: "resource-type", 254 Type: "s3", 255 Source: atc.Source{}, 256 }, 257 }, 258 } 259 260 planFactory := atc.NewPlanFactory(0) 261 262 expectedPlan = planFactory.NewPlan(atc.DoPlan{ 263 planFactory.NewPlan(atc.AggregatePlan{ 264 planFactory.NewPlan(atc.GetPlan{ 265 Name: "fixture", 266 VersionedResourceTypes: atc.VersionedResourceTypes{ 267 atc.VersionedResourceType{ 268 ResourceType: atc.ResourceType{ 269 Name: "resource-type", 270 Type: "s3", 271 Source: atc.Source{}, 272 }, 273 }, 274 }, 275 }), 276 }), 277 taskPlan, 278 }) 279 280 atcServer.RouteToHandler("POST", "/api/v1/teams/main/pipelines/some-pipeline/builds", 281 ghttp.CombineHandlers( 282 ghttp.VerifyRequest("POST", "/api/v1/teams/main/pipelines/some-pipeline/builds"), 283 testhelpers.VerifyPlan(expectedPlan), 284 func(w http.ResponseWriter, r *http.Request) { 285 http.SetCookie(w, &http.Cookie{ 286 Name: "Some-Cookie", 287 Value: "some-cookie-data", 288 Path: "/", 289 Expires: time.Now().Add(1 * time.Minute), 290 }) 291 }, 292 ghttp.RespondWith(201, `{"id":128}`), 293 ), 294 ) 295 atcServer.RouteToHandler("GET", "/api/v1/teams/main/pipelines/some-pipeline/jobs/some-job/inputs", 296 ghttp.RespondWithJSONEncoded(200, []atc.BuildInput{atc.BuildInput{Name: "fixture"}}), 297 ) 298 atcServer.RouteToHandler("GET", "/api/v1/teams/main/pipelines/some-pipeline/resource-types", 299 ghttp.RespondWithJSONEncoded(200, atc.VersionedResourceTypes{ 300 atc.VersionedResourceType{ 301 ResourceType: atc.ResourceType{ 302 Name: "resource-type", 303 Type: "s3", 304 Source: atc.Source{}, 305 }, 306 }, 307 }), 308 ) 309 }) 310 311 It("creates a build, streams output, and polls until completion", func() { 312 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath, "-j", "some-pipeline/some-job") 313 flyCmd.Dir = buildDir 314 315 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 316 Expect(err).NotTo(HaveOccurred()) 317 318 Eventually(streaming).Should(BeClosed()) 319 320 buildURL, _ := url.Parse(atcServer.URL()) 321 buildURL.Path = path.Join(buildURL.Path, "builds/128") 322 Eventually(sess.Out).Should(gbytes.Say("executing build 128 at %s", buildURL.String())) 323 324 events <- event.Log{Payload: "sup"} 325 326 Eventually(sess.Out).Should(gbytes.Say("sup")) 327 328 close(events) 329 330 <-sess.Exited 331 Expect(sess.ExitCode()).To(Equal(0)) 332 }) 333 334 }) 335 336 Context("when the build config is invalid", func() { 337 BeforeEach(func() { 338 // missing platform and run path 339 err := ioutil.WriteFile( 340 filepath.Join(buildDir, "task.yml"), 341 []byte(`--- 342 run: {} 343 `), 344 0644, 345 ) 346 Expect(err).NotTo(HaveOccurred()) 347 }) 348 349 It("prints the failure and exits 1", func() { 350 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath) 351 flyCmd.Dir = buildDir 352 353 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 354 Expect(err).NotTo(HaveOccurred()) 355 356 Eventually(sess.Err).Should(gbytes.Say("missing")) 357 358 <-sess.Exited 359 Expect(sess.ExitCode()).To(Equal(1)) 360 }) 361 }) 362 363 Context("when the build config is valid", func() { 364 Context("when arguments include input that is not a git repo", func() { 365 366 Context("when arguments not include --include-ignored", func() { 367 It("uploading with everything", func() { 368 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath, "-i", "fixture="+buildDir) 369 370 flyCmd.Dir = buildDir 371 372 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 373 Expect(err).NotTo(HaveOccurred()) 374 375 // sync with after create 376 Eventually(streaming).Should(BeClosed()) 377 378 close(events) 379 380 <-sess.Exited 381 Expect(sess.ExitCode()).To(Equal(0)) 382 383 Expect(uploadingBits).To(BeClosed()) 384 }) 385 }) 386 }) 387 388 Context("when arguments include input that is a git repo", func() { 389 390 BeforeEach(func() { 391 gitIgnorePath := filepath.Join(buildDir, ".gitignore") 392 393 err := ioutil.WriteFile(gitIgnorePath, []byte(`*.test`), 0644) 394 Expect(err).NotTo(HaveOccurred()) 395 396 fileToBeIgnoredPath := filepath.Join(buildDir, "dev.test") 397 err = ioutil.WriteFile(fileToBeIgnoredPath, []byte(`test file content`), 0644) 398 Expect(err).NotTo(HaveOccurred()) 399 400 err = os.Mkdir(filepath.Join(buildDir, ".git"), 0755) 401 Expect(err).NotTo(HaveOccurred()) 402 403 err = os.Mkdir(filepath.Join(buildDir, ".git/refs"), 0755) 404 Expect(err).NotTo(HaveOccurred()) 405 406 err = os.Mkdir(filepath.Join(buildDir, ".git/objects"), 0755) 407 Expect(err).NotTo(HaveOccurred()) 408 409 gitHEADPath := filepath.Join(buildDir, ".git/HEAD") 410 err = ioutil.WriteFile(gitHEADPath, []byte(`ref: refs/heads/master`), 0644) 411 Expect(err).NotTo(HaveOccurred()) 412 }) 413 414 Context("when arguments not include --include-ignored", func() { 415 It("by default apply .gitignore", func() { 416 uploading := make(chan struct{}) 417 uploadingBits = uploading 418 atcServer.RouteToHandler("POST", "/api/v1/teams/main/artifacts", 419 ghttp.CombineHandlers( 420 func(w http.ResponseWriter, req *http.Request) { 421 close(uploading) 422 423 gr, err := gzip.NewReader(req.Body) 424 Expect(err).NotTo(HaveOccurred()) 425 426 tr := tar.NewReader(gr) 427 428 var matchFound = false 429 for { 430 hdr, err := tr.Next() 431 if err != nil { 432 break 433 } 434 if strings.Contains(hdr.Name, "dev.test") { 435 matchFound = true 436 break 437 } 438 } 439 440 Expect(matchFound).To(Equal(false)) 441 }, 442 ghttp.RespondWith(201, `{"id":125}`), 443 ), 444 ) 445 446 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath) 447 flyCmd.Dir = buildDir 448 449 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 450 Expect(err).NotTo(HaveOccurred()) 451 452 // sync with after create 453 Eventually(streaming).Should(BeClosed()) 454 455 close(events) 456 457 <-sess.Exited 458 Expect(sess.ExitCode()).To(Equal(0)) 459 460 Expect(uploadingBits).To(BeClosed()) 461 }) 462 }) 463 464 Context("when arguments include --include-ignored", func() { 465 It("uploading with everything", func() { 466 uploading := make(chan struct{}) 467 uploadingBits = uploading 468 atcServer.RouteToHandler("POST", "/api/v1/teams/main/artifacts", 469 ghttp.CombineHandlers( 470 func(w http.ResponseWriter, req *http.Request) { 471 close(uploading) 472 473 Expect(req.FormValue("platform")).To(Equal("some-platform")) 474 475 gr, err := gzip.NewReader(req.Body) 476 Expect(err).NotTo(HaveOccurred()) 477 478 tr := tar.NewReader(gr) 479 480 var matchFound = false 481 for { 482 hdr, err := tr.Next() 483 if err != nil { 484 break 485 } 486 if strings.Contains(hdr.Name, "dev.test") { 487 matchFound = true 488 break 489 } 490 } 491 492 Expect(matchFound).To(Equal(true)) 493 }, 494 ghttp.RespondWith(201, `{"id":125}`), 495 ), 496 ) 497 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath, "--include-ignored") 498 flyCmd.Dir = buildDir 499 500 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 501 Expect(err).NotTo(HaveOccurred()) 502 503 // sync with after create 504 Eventually(streaming).Should(BeClosed()) 505 506 close(events) 507 508 <-sess.Exited 509 Expect(sess.ExitCode()).To(Equal(0)) 510 511 Expect(uploadingBits).To(BeClosed()) 512 }) 513 }) 514 }) 515 }) 516 517 Context("when arguments are passed through", func() { 518 BeforeEach(func() { 519 (*expectedPlan.Do)[1].Task.Config.Run.Args = []string{".", "-name", `foo "bar" baz`} 520 }) 521 522 It("inserts them into the config template", func() { 523 atcServer.AllowUnhandledRequests = true 524 525 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath, "--", "-name", "foo \"bar\" baz") 526 flyCmd.Dir = buildDir 527 528 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 529 Expect(err).NotTo(HaveOccurred()) 530 531 // sync with after create 532 Eventually(streaming).Should(BeClosed()) 533 534 close(events) 535 536 <-sess.Exited 537 Expect(sess.ExitCode()).To(Equal(0)) 538 539 Expect(uploadingBits).To(BeClosed()) 540 }) 541 }) 542 543 Context("when tags are specified", func() { 544 BeforeEach(func() { 545 (*expectedPlan.Do)[1].Task.Tags = []string{"tag-1", "tag-2"} 546 }) 547 548 It("sprinkles them on the task", func() { 549 atcServer.AllowUnhandledRequests = true 550 551 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath, "--tag", "tag-1", "--tag", "tag-2") 552 flyCmd.Dir = buildDir 553 554 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 555 Expect(err).NotTo(HaveOccurred()) 556 557 // sync with after create 558 Eventually(streaming).Should(BeClosed()) 559 560 close(events) 561 562 <-sess.Exited 563 Expect(sess.ExitCode()).To(Equal(0)) 564 565 Expect(uploadingBits).To(BeClosed()) 566 }) 567 }) 568 569 Context("when invalid inputs are passed", func() { 570 It("prints an error", func() { 571 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath, "-i", "fixture=.", "-i", "evan=.") 572 flyCmd.Dir = buildDir 573 574 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 575 Expect(err).NotTo(HaveOccurred()) 576 577 Eventually(sess.Err).Should(gbytes.Say("unknown input `evan`")) 578 579 <-sess.Exited 580 Expect(sess.ExitCode()).To(Equal(1)) 581 }) 582 583 Context("when input is not a folder", func() { 584 It("prints an error", func() { 585 testFile := filepath.Join(buildDir, "test-file.txt") 586 err := ioutil.WriteFile( 587 testFile, 588 []byte(`test file content`), 589 0644, 590 ) 591 Expect(err).NotTo(HaveOccurred()) 592 593 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath, "-i", "fixture=./test-file.txt") 594 flyCmd.Dir = buildDir 595 596 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 597 Expect(err).NotTo(HaveOccurred()) 598 599 Eventually(sess.Err).Should(gbytes.Say("./test-file.txt not a folder")) 600 601 <-sess.Exited 602 Expect(sess.ExitCode()).To(Equal(1)) 603 }) 604 }) 605 606 Context("when invalid inputs are passed and the single valid input is correctly omitted", func() { 607 It("prints an error about invalid inputs instead of missing inputs", func() { 608 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath, "-i", "evan=.") 609 flyCmd.Dir = buildDir 610 611 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 612 Expect(err).NotTo(HaveOccurred()) 613 614 Eventually(sess.Err).Should(gbytes.Say("unknown input `evan`")) 615 616 <-sess.Exited 617 Expect(sess.ExitCode()).To(Equal(1)) 618 }) 619 }) 620 }) 621 622 Context("when the task specifies an optional input", func() { 623 BeforeEach(func() { 624 err := ioutil.WriteFile( 625 filepath.Join(buildDir, "task.yml"), 626 []byte(`--- 627 platform: some-platform 628 629 image_resource: 630 type: registry-image 631 source: 632 repository: ubuntu 633 634 inputs: 635 - name: fixture 636 - name: some-optional-input 637 optional: true 638 639 params: 640 FOO: bar 641 BAZ: buzz 642 X: 1 643 EMPTY: 644 645 run: 646 path: find 647 args: [.] 648 `), 649 0644, 650 ) 651 Expect(err).NotTo(HaveOccurred()) 652 (*expectedPlan.Do)[1].Task.Config.Inputs = []atc.TaskInputConfig{ 653 {Name: "fixture"}, 654 {Name: "some-optional-input", Optional: true}, 655 } 656 }) 657 658 Context("when the required input is specified but the optional input is omitted", func() { 659 It("runs successfully", func() { 660 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath, "-i", "fixture=.") 661 flyCmd.Dir = buildDir 662 663 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 664 Expect(err).NotTo(HaveOccurred()) 665 666 Eventually(streaming).Should(BeClosed()) 667 668 buildURL, _ := url.Parse(atcServer.URL()) 669 buildURL.Path = path.Join(buildURL.Path, "builds/128") 670 Eventually(sess.Out).Should(gbytes.Say("executing build 128 at %s", buildURL.String())) 671 672 events <- event.Log{Payload: "sup"} 673 674 Eventually(sess.Out).Should(gbytes.Say("sup")) 675 676 close(events) 677 678 <-sess.Exited 679 Expect(sess.ExitCode()).To(Equal(0)) 680 681 Expect(uploadingBits).To(BeClosed()) 682 }) 683 }) 684 685 Context("when the required input is not specified on the command line", func() { 686 It("runs infers the required input successfully", func() { 687 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath) 688 flyCmd.Dir = buildDir 689 690 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 691 Expect(err).NotTo(HaveOccurred()) 692 693 Eventually(streaming).Should(BeClosed()) 694 695 buildURL, _ := url.Parse(atcServer.URL()) 696 buildURL.Path = path.Join(buildURL.Path, "builds/128") 697 Eventually(sess.Out).Should(gbytes.Say("executing build 128 at %s", buildURL.String())) 698 699 events <- event.Log{Payload: "sup"} 700 701 Eventually(sess.Out).Should(gbytes.Say("sup")) 702 703 close(events) 704 705 <-sess.Exited 706 Expect(sess.ExitCode()).To(Equal(0)) 707 708 Expect(uploadingBits).To(BeClosed()) 709 }) 710 }) 711 }) 712 713 Context("when the task specifies more than one required input", func() { 714 BeforeEach(func() { 715 err := ioutil.WriteFile( 716 filepath.Join(buildDir, "task.yml"), 717 []byte(`--- 718 platform: some-platform 719 720 image_resource: 721 type: registry-image 722 source: 723 repository: ubuntu 724 725 inputs: 726 - name: fixture 727 - name: something 728 729 params: 730 FOO: bar 731 BAZ: buzz 732 X: 1 733 EMPTY: 734 735 run: 736 path: find 737 args: [.] 738 `), 739 0644, 740 ) 741 Expect(err).NotTo(HaveOccurred()) 742 }) 743 744 Context("When some required inputs are not passed", func() { 745 It("Prints an error", func() { 746 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath, "-i", "something=.") 747 flyCmd.Dir = buildDir 748 749 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 750 Expect(err).NotTo(HaveOccurred()) 751 752 Eventually(sess.Err).Should(gbytes.Say("missing required input `fixture`")) 753 754 <-sess.Exited 755 Expect(sess.ExitCode()).To(Equal(1)) 756 }) 757 758 }) 759 760 Context("When no inputs are passed", func() { 761 It("Prints an error", func() { 762 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath) 763 flyCmd.Dir = buildDir 764 765 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 766 Expect(err).NotTo(HaveOccurred()) 767 768 Eventually(sess.Err).Should(gbytes.Say("missing required input")) 769 770 <-sess.Exited 771 Expect(sess.ExitCode()).To(Equal(1)) 772 }) 773 }) 774 }) 775 776 Context("when running with --privileged", func() { 777 BeforeEach(func() { 778 (*expectedPlan.Do)[1].Task.Privileged = true 779 }) 780 781 It("inserts them into the config template", func() { 782 atcServer.AllowUnhandledRequests = true 783 784 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath, "--privileged") 785 flyCmd.Dir = buildDir 786 787 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 788 Expect(err).NotTo(HaveOccurred()) 789 790 // sync with after create 791 Eventually(streaming).Should(BeClosed()) 792 793 close(events) 794 795 <-sess.Exited 796 Expect(sess.ExitCode()).To(Equal(0)) 797 798 Expect(uploadingBits).To(BeClosed()) 799 }) 800 }) 801 802 Context("when running with bogus flags", func() { 803 It("exits 1", func() { 804 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath, "--bogus-flag") 805 flyCmd.Dir = buildDir 806 807 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 808 Expect(err).NotTo(HaveOccurred()) 809 810 Eventually(sess.Err).Should(gbytes.Say("unknown flag `bogus-flag'")) 811 812 <-sess.Exited 813 Expect(sess.ExitCode()).To(Equal(1)) 814 }) 815 }) 816 817 Context("when running with invalid -j flag", func() { 818 It("exits 1", func() { 819 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath, "-j", "some-pipeline/invalid/some-job") 820 flyCmd.Dir = buildDir 821 822 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 823 Expect(err).NotTo(HaveOccurred()) 824 825 Eventually(sess.Err).Should(gbytes.Say("argument format should be <pipeline>/<job>")) 826 827 <-sess.Exited 828 Expect(sess.ExitCode()).To(Equal(1)) 829 }) 830 }) 831 832 Context("when parameters are specified in the environment", func() { 833 BeforeEach(func() { 834 (*expectedPlan.Do)[1].Task.Config.Params = map[string]string{ 835 "FOO": "newbar", 836 "BAZ": "buzz", 837 "X": "", 838 "EMPTY": "", 839 } 840 }) 841 842 It("overrides the builds parameter values", func() { 843 atcServer.AllowUnhandledRequests = true 844 845 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath) 846 flyCmd.Dir = buildDir 847 flyCmd.Env = append(os.Environ(), "FOO=newbar", "X=") 848 849 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 850 Expect(err).NotTo(HaveOccurred()) 851 852 // sync with after create 853 Eventually(streaming).Should(BeClosed()) 854 855 close(events) 856 857 <-sess.Exited 858 Expect(sess.ExitCode()).To(Equal(0)) 859 860 Expect(uploadingBits).To(BeClosed()) 861 }) 862 }) 863 864 Context("when the build is interrupted", func() { 865 var aborted chan struct{} 866 867 JustBeforeEach(func() { 868 aborted = make(chan struct{}) 869 870 atcServer.AppendHandlers( 871 ghttp.CombineHandlers( 872 ghttp.VerifyRequest("PUT", "/api/v1/builds/128/abort"), 873 func(w http.ResponseWriter, r *http.Request) { 874 close(aborted) 875 }, 876 ), 877 ) 878 }) 879 880 if runtime.GOOS != "windows" { 881 Describe("with SIGINT", func() { 882 It("aborts the build and exits nonzero", func() { 883 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath) 884 flyCmd.Dir = buildDir 885 886 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 887 Expect(err).ToNot(HaveOccurred()) 888 889 Eventually(streaming).Should(BeClosed()) 890 891 Eventually(uploadingBits).Should(BeClosed()) 892 893 sess.Signal(os.Interrupt) 894 895 Eventually(aborted).Should(BeClosed()) 896 897 events <- event.Status{Status: atc.StatusErrored} 898 close(events) 899 900 <-sess.Exited 901 Expect(sess.ExitCode()).To(Equal(2)) 902 }) 903 }) 904 905 Describe("with SIGTERM", func() { 906 It("aborts the build and exits nonzero", func() { 907 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath) 908 flyCmd.Dir = buildDir 909 910 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 911 Expect(err).ToNot(HaveOccurred()) 912 913 Eventually(streaming).Should(BeClosed()) 914 915 Eventually(uploadingBits).Should(BeClosed()) 916 917 sess.Signal(syscall.SIGTERM) 918 919 Eventually(aborted).Should(BeClosed()) 920 921 events <- event.Status{Status: atc.StatusErrored} 922 close(events) 923 924 <-sess.Exited 925 Expect(sess.ExitCode()).To(Equal(2)) 926 }) 927 }) 928 } 929 }) 930 931 Context("when the build succeeds", func() { 932 It("exits 0", func() { 933 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath) 934 flyCmd.Dir = buildDir 935 936 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 937 Expect(err).ToNot(HaveOccurred()) 938 939 Eventually(streaming).Should(BeClosed()) 940 941 events <- event.Status{Status: atc.StatusSucceeded} 942 close(events) 943 944 <-sess.Exited 945 Expect(sess.ExitCode()).To(Equal(0)) 946 947 Expect(uploadingBits).To(BeClosed()) 948 }) 949 }) 950 951 Context("when the build fails", func() { 952 It("exits 1", func() { 953 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath) 954 flyCmd.Dir = buildDir 955 956 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 957 Expect(err).ToNot(HaveOccurred()) 958 959 Eventually(streaming).Should(BeClosed()) 960 961 events <- event.Status{Status: atc.StatusFailed} 962 close(events) 963 964 <-sess.Exited 965 Expect(sess.ExitCode()).To(Equal(1)) 966 967 Expect(uploadingBits).To(BeClosed()) 968 }) 969 }) 970 971 Context("when the build errors", func() { 972 It("exits 2", func() { 973 flyCmd := exec.Command(flyPath, "-t", targetName, "e", "-c", taskConfigPath) 974 flyCmd.Dir = buildDir 975 976 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 977 Expect(err).ToNot(HaveOccurred()) 978 979 Eventually(streaming).Should(BeClosed()) 980 981 events <- event.Status{Status: atc.StatusErrored} 982 close(events) 983 984 <-sess.Exited 985 Expect(sess.ExitCode()).To(Equal(2)) 986 987 Expect(uploadingBits).To(BeClosed()) 988 }) 989 }) 990 })