github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/fly/integration/set_pipeline_test.go (about) 1 package integration_test 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "net/http" 8 "os" 9 "os/exec" 10 "regexp" 11 12 . "github.com/onsi/ginkgo" 13 . "github.com/onsi/gomega" 14 15 "code.cloudfoundry.org/urljoiner" 16 "github.com/mgutz/ansi" 17 "github.com/onsi/gomega/gbytes" 18 "github.com/onsi/gomega/gexec" 19 "github.com/onsi/gomega/ghttp" 20 "github.com/tedsuo/rata" 21 "sigs.k8s.io/yaml" 22 23 "github.com/pf-qiu/concourse/v6/atc" 24 ) 25 26 var _ = Describe("Fly CLI", func() { 27 Describe("set-pipeline", func() { 28 var ( 29 config atc.Config 30 ) 31 32 yes := func(stdin io.Writer) { 33 fmt.Fprintf(stdin, "y\n") 34 } 35 36 no := func(stdin io.Writer) { 37 fmt.Fprintf(stdin, "n\n") 38 } 39 40 expectSaveConfig := func(config atc.Config) { 41 path, err := atc.Routes.CreatePathForRoute(atc.SaveConfig, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) 42 Expect(err).NotTo(HaveOccurred()) 43 44 atcServer.RouteToHandler("PUT", path, 45 ghttp.CombineHandlers( 46 ghttp.VerifyHeaderKV(atc.ConfigVersionHeader, "42"), 47 func(w http.ResponseWriter, r *http.Request) { 48 bodyConfig := getConfig(r) 49 50 receivedConfig := atc.Config{} 51 err = yaml.Unmarshal(bodyConfig, &receivedConfig) 52 Expect(err).NotTo(HaveOccurred()) 53 54 Expect(receivedConfig).To(Equal(config)) 55 56 w.WriteHeader(http.StatusOK) 57 w.Write([]byte(`{}`)) 58 }, 59 ), 60 ) 61 62 path_get, err := atc.Routes.CreatePathForRoute(atc.GetPipeline, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) 63 Expect(err).NotTo(HaveOccurred()) 64 65 atcServer.RouteToHandler("GET", path_get, 66 ghttp.RespondWithJSONEncoded(http.StatusOK, atc.Pipeline{Name: "awesome-pipeline", Paused: false, TeamName: "main"}), 67 ) 68 } 69 70 BeforeEach(func() { 71 config = atc.Config{ 72 Groups: atc.GroupConfigs{ 73 { 74 Name: "some-group", 75 Jobs: []string{"job-1", "job-2"}, 76 Resources: []string{"resource-1", "resource-2"}, 77 }, 78 { 79 Name: "some-other-group", 80 Jobs: []string{"job-3", "job-4"}, 81 Resources: []string{"resource-6", "resource-4"}, 82 }, 83 }, 84 85 Resources: atc.ResourceConfigs{ 86 { 87 Name: "some-resource", 88 Type: "some-type", 89 Source: atc.Source{ 90 "source-config": "some-value", 91 }, 92 }, 93 { 94 Name: "some-other-resource", 95 Type: "some-other-type", 96 Source: atc.Source{ 97 "source-config": "some-value", 98 }, 99 }, 100 { 101 Name: "some-resource-with-int-field", 102 Type: "some-type", 103 Source: atc.Source{ 104 "source-config": 5, 105 }, 106 }, 107 }, 108 109 ResourceTypes: atc.ResourceTypes{ 110 { 111 Name: "some-resource-type", 112 Type: "some-type", 113 Source: atc.Source{ 114 "source-config": "some-value", 115 }, 116 }, 117 { 118 Name: "some-other-resource-type", 119 Type: "some-other-type", 120 Source: atc.Source{ 121 "source-config": "some-value", 122 }, 123 }, 124 }, 125 126 Jobs: atc.JobConfigs{ 127 { 128 Name: "some-job", 129 Public: true, 130 Serial: true, 131 }, 132 { 133 Name: "some-unchanged-job", 134 }, 135 { 136 Name: "some-other-job", 137 }, 138 { 139 Name: "pinned-resource-job", 140 PlanSequence: []atc.Step{ 141 { 142 Config: &atc.GetStep{ 143 Name: "some-resource", 144 Version: &atc.VersionConfig{ 145 Pinned: atc.Version{ 146 "ref": "some-ref", 147 }, 148 }, 149 }, 150 }, 151 }, 152 }, 153 }, 154 } 155 }) 156 157 Describe("templating", func() { 158 BeforeEach(func() { 159 config = atc.Config{ 160 Groups: atc.GroupConfigs{}, 161 162 Resources: atc.ResourceConfigs{ 163 { 164 Name: "some-resource", 165 Type: "template-type", 166 Source: atc.Source{ 167 "source-config": "some-value", 168 }, 169 }, 170 { 171 Name: "some-other-resource", 172 Type: "some-other-type", 173 Source: atc.Source{ 174 "secret_key": "verysecret", 175 }, 176 }, 177 }, 178 179 Jobs: atc.JobConfigs{}, 180 } 181 182 path, err := atc.Routes.CreatePathForRoute(atc.GetConfig, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) 183 Expect(err).NotTo(HaveOccurred()) 184 185 atcServer.AppendHandlers( 186 ghttp.CombineHandlers( 187 ghttp.VerifyRequest("GET", path), 188 ghttp.RespondWithJSONEncoded(http.StatusOK, atc.ConfigResponse{Config: config}, http.Header{atc.ConfigVersionHeader: {"42"}}), 189 ), 190 ) 191 }) 192 193 Context("when configuring container limits in task", func() { 194 It("succeeds", func() { 195 flyCmd := exec.Command( 196 flyPath, "-t", targetName, 197 "set-pipeline", 198 "--pipeline", "awesome-pipeline", 199 "-c", "fixtures/testConfigContainerLimits.yml", 200 ) 201 stdin, err := flyCmd.StdinPipe() 202 Expect(err).NotTo(HaveOccurred()) 203 204 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 205 Expect(err).NotTo(HaveOccurred()) 206 207 Eventually(sess).Should(gbytes.Say(`cpu: 1024`)) 208 Eventually(sess).Should(gbytes.Say(`memory: 2147483648`)) 209 Eventually(sess).Should(gbytes.Say(`apply configuration\? \[yN\]: `)) 210 no(stdin) 211 212 <-sess.Exited 213 Expect(sess.ExitCode()).To(Equal(0)) 214 }) 215 }) 216 217 Context("when configuring with old-style templated value that fails", func() { 218 It("shows helpful error messages", func() { 219 flyCmd := exec.Command( 220 flyPath, "-t", targetName, 221 "set-pipeline", 222 "--pipeline", "awesome-pipeline", 223 "-c", "fixtures/testConfig.yml", 224 ) 225 226 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 227 Expect(err).NotTo(HaveOccurred()) 228 229 Eventually(sess.Err).Should(gbytes.Say(`2 errors occurred:`)) 230 Eventually(sess.Err).Should(gbytes.Say(`\* unbound variable in template: 'resource-type'`)) 231 Eventually(sess.Err).Should(gbytes.Say(`\* unbound variable in template: 'resource-key'`)) 232 233 <-sess.Exited 234 Expect(sess.ExitCode()).NotTo(Equal(0)) 235 }) 236 }) 237 238 Context("when configuring with old-style templated value succeeds", func() { 239 BeforeEach(func() { 240 expectSaveConfig(atc.Config{ 241 Groups: atc.GroupConfigs{}, 242 243 Resources: atc.ResourceConfigs{ 244 { 245 Name: "some-resource", 246 Type: "template-type", 247 Source: atc.Source{ 248 "source-config": "some-value", 249 }, 250 }, 251 { 252 Name: "some-other-resource", 253 Type: "some-other-type", 254 Source: atc.Source{ 255 "secret_key": "overridden-secret", 256 }, 257 }, 258 }, 259 260 Jobs: atc.JobConfigs{}, 261 }) 262 }) 263 264 It("parses the config file and sends it to the ATC", func() { 265 Expect(func() { 266 flyCmd := exec.Command( 267 flyPath, "-t", targetName, 268 "set-pipeline", 269 "--pipeline", "awesome-pipeline", 270 "-c", "fixtures/testConfig.yml", 271 "--var", "resource-key=overridden-secret", 272 "--load-vars-from", "fixtures/vars.yml", 273 ) 274 275 stdin, err := flyCmd.StdinPipe() 276 Expect(err).NotTo(HaveOccurred()) 277 278 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 279 Expect(err).NotTo(HaveOccurred()) 280 281 Eventually(sess).Should(gbytes.Say(`apply configuration\? \[yN\]: `)) 282 yes(stdin) 283 Eventually(sess).Should(gbytes.Say("configuration updated")) 284 285 <-sess.Exited 286 Expect(sess.ExitCode()).To(Equal(0)) 287 }).To(Change(func() int { 288 return len(atcServer.ReceivedRequests()) 289 }).By(4)) 290 }) 291 292 Context("when a non-stringy var is specified with -v", func() { 293 BeforeEach(func() { 294 config = atc.Config{ 295 Groups: atc.GroupConfigs{}, 296 297 Resources: atc.ResourceConfigs{ 298 { 299 Name: "some-resource", 300 Type: "template-type", 301 Source: atc.Source{ 302 "source-config": "some-value", 303 }, 304 }, 305 { 306 Name: "some-other-resource", 307 Type: "some-other-type", 308 Source: atc.Source{ 309 "secret_key": `{"complicated": "secret"}`, 310 }, 311 }, 312 }, 313 314 Jobs: atc.JobConfigs{}, 315 } 316 317 path, err := atc.Routes.CreatePathForRoute(atc.SaveConfig, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) 318 Expect(err).NotTo(HaveOccurred()) 319 320 atcServer.RouteToHandler("PUT", path, 321 ghttp.CombineHandlers( 322 ghttp.VerifyHeaderKV(atc.ConfigVersionHeader, "42"), 323 func(w http.ResponseWriter, r *http.Request) { 324 bodyConfig := getConfig(r) 325 326 receivedConfig := atc.Config{} 327 err = yaml.Unmarshal(bodyConfig, &receivedConfig) 328 Expect(err).NotTo(HaveOccurred()) 329 330 Expect(receivedConfig).To(Equal(config)) 331 332 w.WriteHeader(http.StatusOK) 333 w.Write([]byte(`{}`)) 334 }, 335 ), 336 ) 337 }) 338 339 It("succeeds", func() { 340 Expect(func() { 341 flyCmd := exec.Command( 342 flyPath, "-t", targetName, 343 "set-pipeline", 344 "-n", 345 "--pipeline", "awesome-pipeline", 346 "-c", "fixtures/testConfig.yml", 347 "--var", `resource-key={"complicated": "secret"}`, 348 "--load-vars-from", "fixtures/vars.yml", 349 ) 350 351 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 352 Expect(err).NotTo(HaveOccurred()) 353 <-sess.Exited 354 Expect(sess.ExitCode()).To(Equal(0)) 355 }).To(Change(func() int { 356 return len(atcServer.ReceivedRequests()) 357 }).By(4)) 358 }) 359 }) 360 361 Context("when the --non-interactive is passed", func() { 362 It("parses the config file and sends it to the ATC without interaction", func() { 363 Expect(func() { 364 flyCmd := exec.Command( 365 flyPath, "-t", targetName, 366 "set-pipeline", 367 "--pipeline", "awesome-pipeline", 368 "-c", "fixtures/testConfig.yml", 369 "--var", "resource-key=overridden-secret", 370 "--load-vars-from", "fixtures/vars.yml", 371 "--non-interactive", 372 ) 373 374 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 375 Expect(err).NotTo(HaveOccurred()) 376 377 Eventually(sess).Should(gbytes.Say("configuration updated")) 378 379 <-sess.Exited 380 Expect(sess.ExitCode()).To(Equal(0)) 381 382 }).To(Change(func() int { 383 return len(atcServer.ReceivedRequests()) 384 }).By(4)) 385 }) 386 }) 387 }) 388 389 Context("when a var is specified with -v", func() { 390 BeforeEach(func() { 391 expectSaveConfig(atc.Config{ 392 Resources: atc.ResourceConfigs{ 393 { 394 Name: "some-resource", 395 Type: "some-type", 396 Tags: atc.Tags{"val-1", "val-2"}, 397 Source: atc.Source{ 398 "private_key": `-----BEGIN SOME KEY----- 399 this is super secure 400 -----END SOME KEY----- 401 `, 402 "config-a": "some-param-a", 403 "config-b": "some-param-b-via-v", 404 "bool": true, 405 "number": 1.23, 406 }, 407 }, 408 }, 409 410 Jobs: atc.JobConfigs{ 411 { 412 Name: "some-job", 413 PlanSequence: []atc.Step{ 414 { 415 Config: &atc.GetStep{ 416 Name: "some-resource", 417 }, 418 }, 419 }, 420 }, 421 }, 422 }) 423 }) 424 425 It("succeeds", func() { 426 Expect(func() { 427 flyCmd := exec.Command( 428 flyPath, "-t", targetName, 429 "set-pipeline", 430 "-n", 431 "--pipeline", "awesome-pipeline", 432 "-c", "fixtures/vars-pipeline.yml", 433 "-l", "fixtures/vars-pipeline-params-a.yml", 434 "-l", "fixtures/vars-pipeline-params-types.yml", 435 "-v", "param-b=some-param-b-via-v", 436 ) 437 438 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 439 Expect(err).NotTo(HaveOccurred()) 440 <-sess.Exited 441 Expect(sess.ExitCode()).To(Equal(0)) 442 }).To(Change(func() int { 443 return len(atcServer.ReceivedRequests()) 444 }).By(4)) 445 }) 446 }) 447 448 Context("when vars are overridden with -v, some with special types", func() { 449 BeforeEach(func() { 450 expectSaveConfig(atc.Config{ 451 Resources: atc.ResourceConfigs{ 452 { 453 Name: "some-resource", 454 Type: "some-type", 455 Tags: atc.Tags{"val-1", "val-2"}, 456 Source: atc.Source{ 457 "private_key": `-----BEGIN SOME KEY----- 458 this is super secure 459 -----END SOME KEY----- 460 `, 461 "config-a": "some-param-a", 462 "config-b": "some\nmultiline\nbusiness\n", 463 "bool": false, 464 "number": 3.14, 465 }, 466 }, 467 }, 468 469 Jobs: atc.JobConfigs{ 470 { 471 Name: "some-job", 472 PlanSequence: []atc.Step{ 473 { 474 Config: &atc.GetStep{ 475 Name: "some-resource", 476 }, 477 }, 478 }, 479 }, 480 }, 481 }) 482 }) 483 484 It("succeeds", func() { 485 Expect(func() { 486 flyCmd := exec.Command( 487 flyPath, "-t", targetName, 488 "set-pipeline", 489 "-n", 490 "--pipeline", "awesome-pipeline", 491 "-c", "fixtures/vars-pipeline.yml", 492 "-l", "fixtures/vars-pipeline-params-a.yml", 493 "-l", "fixtures/vars-pipeline-params-b.yml", 494 "-l", "fixtures/vars-pipeline-params-types.yml", 495 "-v", "param-b=some\nmultiline\nbusiness\n", 496 "-y", "bool-param=false", 497 "-y", "number-param=3.14", 498 ) 499 500 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 501 Expect(err).NotTo(HaveOccurred()) 502 <-sess.Exited 503 Expect(sess.ExitCode()).To(Equal(0)) 504 }).To(Change(func() int { 505 return len(atcServer.ReceivedRequests()) 506 }).By(4)) 507 }) 508 }) 509 510 Context("when var flags use dot notation", func() { 511 BeforeEach(func() { 512 expectSaveConfig(atc.Config{ 513 Resources: atc.ResourceConfigs{ 514 { 515 Name: "some-resource", 516 Type: "some-type", 517 Source: atc.Source{ 518 "a": "foo", 519 "b": "bar", 520 "other": "baz", 521 }, 522 }, 523 }, 524 525 Jobs: atc.JobConfigs{ 526 { 527 Name: "some-job", 528 PlanSequence: []atc.Step{ 529 { 530 Config: &atc.GetStep{ 531 Name: "some-resource", 532 }, 533 }, 534 }, 535 }, 536 }, 537 }) 538 }) 539 540 It("succeeds", func() { 541 Expect(func() { 542 flyCmd := exec.Command( 543 flyPath, "-t", targetName, 544 "set-pipeline", 545 "-n", 546 "--pipeline", "awesome-pipeline", 547 "-c", "fixtures/nested-vars-pipeline.yml", 548 "-v", "source.a=foo", 549 "-v", "source.b=bar", 550 "-v", `"source.a"=baz`, 551 ) 552 553 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 554 Expect(err).NotTo(HaveOccurred()) 555 <-sess.Exited 556 Expect(sess.ExitCode()).To(Equal(0)) 557 }).To(Change(func() int { 558 return len(atcServer.ReceivedRequests()) 559 }).By(4)) 560 }) 561 }) 562 563 Context("when a var is not specified", func() { 564 BeforeEach(func() { 565 config = atc.Config{ 566 Resources: atc.ResourceConfigs{ 567 { 568 Name: "some-resource", 569 Type: "some-type", 570 Tags: atc.Tags{"val-1", "val-2"}, 571 Source: atc.Source{ 572 "private_key": `-----BEGIN SOME KEY----- 573 this is super secure 574 -----END SOME KEY----- 575 `, 576 "config-a": "some-param-a", 577 "config-b": "((param-b))", 578 "bool": true, 579 "number": 1.23, 580 }, 581 }, 582 }, 583 584 Jobs: atc.JobConfigs{ 585 { 586 Name: "some-job", 587 PlanSequence: []atc.Step{ 588 { 589 Config: &atc.GetStep{ 590 Name: "some-resource", 591 }, 592 }, 593 }, 594 }, 595 }, 596 } 597 expectSaveConfig(config) 598 }) 599 600 It("succeeds, sending the remaining vars uninterpolated", func() { 601 Expect(func() { 602 flyCmd := exec.Command( 603 flyPath, "-t", targetName, 604 "set-pipeline", 605 "-n", 606 "--pipeline", "awesome-pipeline", 607 "-c", "fixtures/vars-pipeline.yml", 608 "-l", "fixtures/vars-pipeline-params-a.yml", 609 "-l", "fixtures/vars-pipeline-params-types.yml", 610 ) 611 612 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 613 Expect(err).NotTo(HaveOccurred()) 614 <-sess.Exited 615 Expect(sess.ExitCode()).To(Equal(0)) 616 }).To(Change(func() int { 617 return len(atcServer.ReceivedRequests()) 618 }).By(4)) 619 }) 620 621 Context("when the --check-creds option is used", func() { 622 Context("when the variable exists in the credentials manager", func() { 623 It("should succeed and send the vars uninterpolated", func() { 624 Expect(func() { 625 flyCmd := exec.Command( 626 flyPath, "-t", targetName, 627 "set-pipeline", 628 "-n", 629 "--pipeline", "awesome-pipeline", 630 "-c", "fixtures/vars-pipeline.yml", 631 "-l", "fixtures/vars-pipeline-params-a.yml", 632 "-l", "fixtures/vars-pipeline-params-types.yml", 633 "--check-creds", 634 ) 635 636 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 637 Expect(err).NotTo(HaveOccurred()) 638 <-sess.Exited 639 Expect(sess.ExitCode()).To(Equal(0)) 640 }).To(Change(func() int { 641 return len(atcServer.ReceivedRequests()) 642 }).By(4)) 643 }) 644 }) 645 646 Context("when the variable does not exist in the credentials manager", func() { 647 BeforeEach(func() { 648 path, err := atc.Routes.CreatePathForRoute(atc.SaveConfig, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) 649 Expect(err).NotTo(HaveOccurred()) 650 651 configResponse := atc.SaveConfigResponse{Errors: []string{"some-error"}} 652 atcServer.RouteToHandler("PUT", path, 653 ghttp.CombineHandlers( 654 ghttp.VerifyHeaderKV(atc.ConfigVersionHeader, "42"), 655 func(w http.ResponseWriter, r *http.Request) { 656 bodyConfig := getConfig(r) 657 658 receivedConfig := atc.Config{} 659 err = yaml.Unmarshal(bodyConfig, &receivedConfig) 660 Expect(err).NotTo(HaveOccurred()) 661 662 Expect(receivedConfig).To(Equal(config)) 663 }, 664 ghttp.RespondWithJSONEncoded(http.StatusBadRequest, configResponse, http.Header{atc.ConfigVersionHeader: {"42"}}), 665 ), 666 ) 667 }) 668 669 It("should error and return the missing field", func() { 670 Expect(func() { 671 flyCmd := exec.Command( 672 flyPath, "-t", targetName, 673 "set-pipeline", 674 "-n", 675 "--pipeline", "awesome-pipeline", 676 "-c", "fixtures/vars-pipeline.yml", 677 "-l", "fixtures/vars-pipeline-params-a.yml", 678 "-l", "fixtures/vars-pipeline-params-types.yml", 679 "--check-creds", 680 ) 681 682 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 683 Expect(err).NotTo(HaveOccurred()) 684 685 Eventually(sess.Err).Should(gbytes.Say(`error: invalid pipeline config:`)) 686 Eventually(sess.Err).Should(gbytes.Say(`some-error`)) 687 688 <-sess.Exited 689 Expect(sess.ExitCode()).NotTo(Equal(0)) 690 }).To(Change(func() int { 691 return len(atcServer.ReceivedRequests()) 692 }).By(3)) 693 }) 694 }) 695 }) 696 697 }) 698 }) 699 700 Describe("setting", func() { 701 var ( 702 changedConfig atc.Config 703 704 payload []byte 705 configFile *os.File 706 ) 707 708 BeforeEach(func() { 709 var err error 710 711 configFile, err = ioutil.TempFile("", "fly-config-file") 712 Expect(err).NotTo(HaveOccurred()) 713 714 changedConfig = config 715 716 path, err := atc.Routes.CreatePathForRoute(atc.GetConfig, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) 717 Expect(err).NotTo(HaveOccurred()) 718 719 atcServer.RouteToHandler("GET", path, 720 ghttp.RespondWithJSONEncoded(http.StatusOK, atc.ConfigResponse{Config: config}, http.Header{atc.ConfigVersionHeader: {"42"}}), 721 ) 722 723 path_get, err := atc.Routes.CreatePathForRoute(atc.GetPipeline, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) 724 Expect(err).NotTo(HaveOccurred()) 725 726 atcServer.RouteToHandler("GET", path_get, 727 ghttp.RespondWithJSONEncoded(http.StatusOK, atc.Pipeline{Name: "awesome-pipeline", Paused: false, TeamName: "main"}), 728 ) 729 }) 730 731 JustBeforeEach(func() { 732 var err error 733 734 payload, err = yaml.Marshal(changedConfig) 735 Expect(err).NotTo(HaveOccurred()) 736 737 _, err = configFile.Write(payload) 738 Expect(err).NotTo(HaveOccurred()) 739 740 err = configFile.Close() 741 Expect(err).NotTo(HaveOccurred()) 742 }) 743 744 AfterEach(func() { 745 err := os.RemoveAll(configFile.Name()) 746 Expect(err).NotTo(HaveOccurred()) 747 }) 748 749 Context("when not specifying a pipeline name", func() { 750 It("fails and says you should give a pipeline name", func() { 751 flyCmd := exec.Command(flyPath, "-t", targetName, "set-pipeline", "-c", configFile.Name()) 752 753 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 754 Expect(err).NotTo(HaveOccurred()) 755 756 <-sess.Exited 757 Expect(sess.ExitCode()).To(Equal(1)) 758 759 Expect(sess.Err).To(gbytes.Say("error: the required flag `" + osFlag("p", "pipeline") + "' was not specified")) 760 }) 761 }) 762 763 Context("when specifying a pipeline name with a '/' character in it", func() { 764 It("fails and says '/' characters are not allowed", func() { 765 flyCmd := exec.Command(flyPath, "-t", targetName, "set-pipeline", "-p", "forbidden/pipelinename", "-c", configFile.Name()) 766 767 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 768 Expect(err).NotTo(HaveOccurred()) 769 770 <-sess.Exited 771 Expect(sess.ExitCode()).To(Equal(1)) 772 773 Expect(sess.Err).To(gbytes.Say("error: pipeline name cannot contain '/'")) 774 }) 775 }) 776 777 Context("when not specifying a config file", func() { 778 It("fails and says you should give a config file", func() { 779 flyCmd := exec.Command(flyPath, "-t", targetName, "set-pipeline", "-p", "awesome-pipeline") 780 781 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 782 Expect(err).NotTo(HaveOccurred()) 783 784 <-sess.Exited 785 Expect(sess.ExitCode()).To(Equal(1)) 786 787 Expect(sess.Err).To(gbytes.Say("error: the required flag `" + osFlag("c", "config") + "' was not specified")) 788 }) 789 }) 790 791 Context("when configuring with groups re-ordered", func() { 792 BeforeEach(func() { 793 changedConfig.Groups = atc.GroupConfigs{ 794 { 795 Name: "some-other-group", 796 Jobs: []string{"job-3", "job-4"}, 797 Resources: []string{"resource-6", "resource-4"}, 798 }, 799 { 800 Name: "some-group", 801 Jobs: []string{"job-1", "job-2"}, 802 Resources: []string{"resource-1", "resource-2"}, 803 }, 804 } 805 806 path, err := atc.Routes.CreatePathForRoute(atc.SaveConfig, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) 807 Expect(err).NotTo(HaveOccurred()) 808 809 atcServer.RouteToHandler("PUT", path, 810 ghttp.CombineHandlers( 811 ghttp.VerifyHeaderKV(atc.ConfigVersionHeader, "42"), 812 func(w http.ResponseWriter, r *http.Request) { 813 config := getConfig(r) 814 Expect(config).To(MatchYAML(payload)) 815 }, 816 ghttp.RespondWith(http.StatusOK, "{}"), 817 ), 818 ) 819 }) 820 821 It("parses the config file and sends it to the ATC", func() { 822 Expect(func() { 823 flyCmd := exec.Command(flyPath, "-t", targetName, "set-pipeline", "-p", "awesome-pipeline", "-c", configFile.Name()) 824 825 stdin, err := flyCmd.StdinPipe() 826 Expect(err).NotTo(HaveOccurred()) 827 828 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 829 Expect(err).NotTo(HaveOccurred()) 830 831 Eventually(sess).Should(gbytes.Say("group some-group has changed")) 832 833 Eventually(sess).Should(gbytes.Say("group some-other-group has changed")) 834 835 Eventually(sess).Should(gbytes.Say(`apply configuration\? \[yN\]: `)) 836 yes(stdin) 837 838 Eventually(sess).Should(gbytes.Say("configuration updated")) 839 840 <-sess.Exited 841 Expect(sess.ExitCode()).To(Equal(0)) 842 843 }).To(Change(func() int { 844 return len(atcServer.ReceivedRequests()) 845 }).By(4)) 846 }) 847 }) 848 849 Context("when configuring succeeds", func() { 850 BeforeEach(func() { 851 newGroup := changedConfig.Groups[1] 852 newGroup.Name = "some-new-group" 853 changedConfig.Groups[0].Jobs = append(changedConfig.Groups[0].Jobs, "some-new-job") 854 changedConfig.Groups = append(changedConfig.Groups[:1], newGroup) 855 856 newResource := changedConfig.Resources[1] 857 newResource.Name = "some-new-resource" 858 859 newResources := make(atc.ResourceConfigs, len(changedConfig.Resources)) 860 copy(newResources, changedConfig.Resources) 861 newResources[0].Type = "some-new-type" 862 newResources[1] = newResource 863 newResources[2].Source = atc.Source{"source-config": 5.0} 864 865 changedConfig.Resources = newResources 866 867 newResourceType := changedConfig.ResourceTypes[1] 868 newResourceType.Name = "some-new-resource-type" 869 870 newResourceTypes := make(atc.ResourceTypes, len(changedConfig.ResourceTypes)) 871 copy(newResourceTypes, changedConfig.ResourceTypes) 872 newResourceTypes[0].Type = "some-new-type" 873 newResourceTypes[1] = newResourceType 874 875 changedConfig.ResourceTypes = newResourceTypes 876 877 newJob := changedConfig.Jobs[2] 878 newJob.Name = "some-new-job" 879 changedConfig.Jobs[0].Serial = false 880 changedConfig.Jobs = append(changedConfig.Jobs[:2], newJob) 881 882 path, err := atc.Routes.CreatePathForRoute(atc.SaveConfig, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) 883 Expect(err).NotTo(HaveOccurred()) 884 885 atcServer.RouteToHandler("PUT", path, 886 ghttp.CombineHandlers( 887 ghttp.VerifyHeaderKV(atc.ConfigVersionHeader, "42"), 888 func(w http.ResponseWriter, r *http.Request) { 889 config := getConfig(r) 890 Expect(config).To(MatchYAML(payload)) 891 }, 892 ghttp.RespondWith(http.StatusOK, "{}"), 893 ), 894 ) 895 }) 896 897 It("parses the config file and sends it to the ATC", func() { 898 Expect(func() { 899 flyCmd := exec.Command(flyPath, "-t", targetName, "set-pipeline", "-p", "awesome-pipeline", "-c", configFile.Name()) 900 901 stdin, err := flyCmd.StdinPipe() 902 Expect(err).NotTo(HaveOccurred()) 903 904 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 905 Expect(err).NotTo(HaveOccurred()) 906 907 Eventually(sess).Should(gbytes.Say("group some-group has changed")) 908 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("- some-new-job", "green"))) 909 910 Eventually(sess).Should(gbytes.Say("group some-other-group has been removed")) 911 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("name: some-other-group", "red"))) 912 913 Eventually(sess).Should(gbytes.Say("group some-new-group has been added")) 914 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("name: some-new-group", "green"))) 915 916 Eventually(sess).Should(gbytes.Say("resource some-resource has changed")) 917 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("type: some-type", "red"))) 918 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("type: some-new-type", "green"))) 919 920 Eventually(sess).Should(gbytes.Say("resource some-other-resource has been removed")) 921 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("name: some-other-resource", "red"))) 922 923 Eventually(sess).Should(gbytes.Say("resource some-new-resource has been added")) 924 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("name: some-new-resource", "green"))) 925 926 Eventually(sess).Should(gbytes.Say("resource type some-resource-type has changed")) 927 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("type: some-type", "red"))) 928 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("type: some-new-type", "green"))) 929 930 Eventually(sess).Should(gbytes.Say("resource type some-other-resource-type has been removed")) 931 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("name: some-other-resource-type", "red"))) 932 933 Eventually(sess).Should(gbytes.Say("resource type some-new-resource-type has been added")) 934 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("name: some-new-resource-type", "green"))) 935 936 Eventually(sess).Should(gbytes.Say("job some-job has changed")) 937 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("serial: true", "red"))) 938 939 Eventually(sess).Should(gbytes.Say("job some-other-job has been removed")) 940 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("name: some-other-job", "red"))) 941 942 Eventually(sess).Should(gbytes.Say("job some-new-job has been added")) 943 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("name: some-new-job", "green"))) 944 945 Eventually(sess).Should(gbytes.Say("pipeline name:")) 946 Eventually(sess).Should(gbytes.Say("awesome-pipeline")) 947 Consistently(sess).ShouldNot(gbytes.Say("pipeline instance vars:")) 948 949 Eventually(sess).Should(gbytes.Say(`apply configuration\? \[yN\]: `)) 950 yes(stdin) 951 952 Eventually(sess).Should(gbytes.Say("configuration updated")) 953 954 <-sess.Exited 955 Expect(sess.ExitCode()).To(Equal(0)) 956 957 Expect(sess.Out.Contents()).ToNot(ContainSubstring("some-resource-with-int-field")) 958 959 Expect(sess.Out.Contents()).ToNot(ContainSubstring("some-unchanged-job")) 960 961 }).To(Change(func() int { 962 return len(atcServer.ReceivedRequests()) 963 }).By(4)) 964 }) 965 966 It("bails if the user rejects the diff", func() { 967 Expect(func() { 968 flyCmd := exec.Command(flyPath, "-t", targetName, "set-pipeline", "-p", "awesome-pipeline", "-c", configFile.Name()) 969 970 stdin, err := flyCmd.StdinPipe() 971 Expect(err).NotTo(HaveOccurred()) 972 973 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 974 Expect(err).NotTo(HaveOccurred()) 975 976 Eventually(sess).Should(gbytes.Say(`apply configuration\? \[yN\]: `)) 977 no(stdin) 978 979 <-sess.Exited 980 Expect(sess.ExitCode()).To(Equal(0)) 981 }).To(Change(func() int { 982 return len(atcServer.ReceivedRequests()) 983 }).By(2)) 984 }) 985 986 It("parses the config from stdin and sends it to the ATC", func() { 987 Expect(func() { 988 flyCmd := exec.Command(flyPath, "-t", targetName, "set-pipeline", "-p", "awesome-pipeline", "-c", "-") 989 990 stdin, err := flyCmd.StdinPipe() 991 Expect(err).NotTo(HaveOccurred()) 992 993 file, err := os.Open(configFile.Name()) 994 Expect(err).NotTo(HaveOccurred()) 995 _, err = io.Copy(stdin, file) 996 Expect(err).NotTo(HaveOccurred()) 997 file.Close() 998 stdin.Close() 999 1000 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 1001 Expect(err).NotTo(HaveOccurred()) 1002 1003 Eventually(sess).Should(gbytes.Say("group some-group has changed")) 1004 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("- some-new-job", "green"))) 1005 1006 Eventually(sess).Should(gbytes.Say("group some-other-group has been removed")) 1007 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("name: some-other-group", "red"))) 1008 1009 Eventually(sess).Should(gbytes.Say("group some-new-group has been added")) 1010 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("name: some-new-group", "green"))) 1011 1012 Eventually(sess).Should(gbytes.Say("resource some-resource has changed")) 1013 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("type: some-type", "red"))) 1014 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("type: some-new-type", "green"))) 1015 1016 Eventually(sess).Should(gbytes.Say("resource some-other-resource has been removed")) 1017 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("name: some-other-resource", "red"))) 1018 1019 Eventually(sess).Should(gbytes.Say("resource some-new-resource has been added")) 1020 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("name: some-new-resource", "green"))) 1021 1022 Eventually(sess).Should(gbytes.Say("resource type some-resource-type has changed")) 1023 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("type: some-type", "red"))) 1024 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("type: some-new-type", "green"))) 1025 1026 Eventually(sess).Should(gbytes.Say("resource type some-other-resource-type has been removed")) 1027 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("name: some-other-resource-type", "red"))) 1028 1029 Eventually(sess).Should(gbytes.Say("resource type some-new-resource-type has been added")) 1030 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("name: some-new-resource-type", "green"))) 1031 1032 Eventually(sess).Should(gbytes.Say("job some-job has changed")) 1033 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("serial: true", "red"))) 1034 1035 Eventually(sess).Should(gbytes.Say("job some-other-job has been removed")) 1036 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("name: some-other-job", "red"))) 1037 1038 Eventually(sess).Should(gbytes.Say("job some-new-job has been added")) 1039 Eventually(sess.Out.Contents).Should(ContainSubstring(ansi.Color("name: some-new-job", "green"))) 1040 1041 // When read pipeline configure from stdin, it should do non-interactive mode. 1042 Consistently(sess).ShouldNot(gbytes.Say(`apply configuration\? \[yN\]: `)) 1043 1044 Eventually(sess).Should(gbytes.Say("configuration updated")) 1045 1046 <-sess.Exited 1047 Expect(sess.ExitCode()).To(Equal(0)) 1048 1049 Expect(sess.Out.Contents()).ToNot(ContainSubstring("some-resource-with-int-field")) 1050 1051 Expect(sess.Out.Contents()).ToNot(ContainSubstring("some-unchanged-job")) 1052 }).To(Change(func() int { 1053 return len(atcServer.ReceivedRequests()) 1054 }).By(4)) 1055 }) 1056 1057 Context("when setting an instanced pipeline", func() { 1058 It("prints the instance vars as YAML", func() { 1059 flyCmd := exec.Command(flyPath, "-t", targetName, "set-pipeline", "-p", "awesome-pipeline", "-i", "version=1.2.3", "-c", configFile.Name()) 1060 1061 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 1062 Expect(err).NotTo(HaveOccurred()) 1063 1064 Eventually(sess).Should(gbytes.Say("pipeline name:")) 1065 Eventually(sess).Should(gbytes.Say("awesome-pipeline")) 1066 1067 Eventually(sess).Should(gbytes.Say("pipeline instance vars:")) 1068 Eventually(sess).Should(gbytes.Say(" version: 1.2.3")) 1069 }) 1070 }) 1071 }) 1072 1073 Context("when setting new pipeline with non-default team", func() { 1074 BeforeEach(func() { 1075 atcServer.AppendHandlers( 1076 ghttp.CombineHandlers( 1077 ghttp.VerifyRequest("GET", "/api/v1/teams/other-team"), 1078 ghttp.RespondWithJSONEncoded(http.StatusOK, atc.Team{ 1079 Name: "other-team", 1080 }), 1081 ), 1082 ghttp.CombineHandlers( 1083 ghttp.VerifyRequest("GET", "/api/v1/teams/other-team/pipelines/awesome-pipeline/config"), 1084 ghttp.RespondWithJSONEncoded(http.StatusOK, atc.Team{ 1085 Name: "other-team", 1086 }), 1087 ), 1088 ghttp.CombineHandlers( 1089 ghttp.VerifyRequest("PUT", "/api/v1/teams/other-team/pipelines/awesome-pipeline/config"), 1090 ghttp.RespondWithJSONEncoded(http.StatusOK, atc.Team{ 1091 Name: "other-team", 1092 }), 1093 ), 1094 ghttp.CombineHandlers( 1095 ghttp.VerifyRequest("GET", "/api/v1/teams/other-team/pipelines/awesome-pipeline"), 1096 ghttp.RespondWithJSONEncoded(http.StatusOK, atc.Pipeline{ 1097 Name: "awesome-pipeline", 1098 }), 1099 ), 1100 ) 1101 }) 1102 1103 It("successfully sets new pipeline to non-default team", func() { 1104 Expect(func() { 1105 flyCmd := exec.Command(flyPath, "-t", targetName, "set-pipeline", "-p", "awesome-pipeline", "-c", configFile.Name(), "--team", "other-team") 1106 1107 stdin, err := flyCmd.StdinPipe() 1108 Expect(err).NotTo(HaveOccurred()) 1109 1110 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 1111 Expect(err).NotTo(HaveOccurred()) 1112 1113 Eventually(sess).Should(gbytes.Say(`apply configuration\? \[yN\]: `)) 1114 yes(stdin) 1115 1116 Eventually(sess).Should(gbytes.Say("configuration updated")) 1117 1118 <-sess.Exited 1119 Expect(sess.ExitCode()).To(Equal(0)) 1120 1121 }).To(Change(func() int { 1122 return len(atcServer.ReceivedRequests()) 1123 }).By(5)) 1124 }) 1125 1126 It("bails if the user rejects the configuration", func() { 1127 Expect(func() { 1128 flyCmd := exec.Command(flyPath, "-t", targetName, "set-pipeline", "-p", "awesome-pipeline", "-c", configFile.Name(), "--team", "other-team") 1129 1130 stdin, err := flyCmd.StdinPipe() 1131 Expect(err).NotTo(HaveOccurred()) 1132 1133 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 1134 Expect(err).NotTo(HaveOccurred()) 1135 1136 Eventually(sess).Should(gbytes.Say(`apply configuration\? \[yN\]: `)) 1137 no(stdin) 1138 Eventually(sess).Should(gbytes.Say("bailing out")) 1139 1140 <-sess.Exited 1141 Expect(sess.ExitCode()).To(Equal(0)) 1142 }).To(Change(func() int { 1143 return len(atcServer.ReceivedRequests()) 1144 }).By(3)) 1145 }) 1146 }) 1147 1148 Context("when configuring fails", func() { 1149 BeforeEach(func() { 1150 path, err := atc.Routes.CreatePathForRoute(atc.SaveConfig, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) 1151 Expect(err).NotTo(HaveOccurred()) 1152 1153 atcServer.RouteToHandler("PUT", path, 1154 ghttp.RespondWith(http.StatusInternalServerError, "nope"), 1155 ) 1156 config.Resources[0].Name = "updated-name" 1157 }) 1158 1159 It("prints the error to stderr and exits 1", func() { 1160 flyCmd := exec.Command(flyPath, "-t", targetName, "set-pipeline", "-c", configFile.Name(), "-p", "awesome-pipeline") 1161 1162 stdin, err := flyCmd.StdinPipe() 1163 Expect(err).NotTo(HaveOccurred()) 1164 1165 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 1166 Expect(err).NotTo(HaveOccurred()) 1167 1168 Eventually(sess).Should(gbytes.Say(`apply configuration\? \[yN\]: `)) 1169 yes(stdin) 1170 1171 Eventually(sess.Err).Should(gbytes.Say("500 Internal Server Error")) 1172 Eventually(sess.Err).Should(gbytes.Say("nope")) 1173 1174 <-sess.Exited 1175 Expect(sess.ExitCode()).To(Equal(1)) 1176 }) 1177 }) 1178 1179 Context("when the pipeline is paused", func() { 1180 AssertSuccessWithPausedPipelineHelp := func(expectCreationMessage bool) { 1181 It("succeeds and prints an error message to help the user", func() { 1182 Expect(func() { 1183 flyCmd := exec.Command(flyPath, "-t", targetName, "set-pipeline", "-p", "awesome-pipeline", "-c", configFile.Name()) 1184 1185 stdin, err := flyCmd.StdinPipe() 1186 Expect(err).NotTo(HaveOccurred()) 1187 1188 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 1189 Expect(err).NotTo(HaveOccurred()) 1190 1191 Eventually(sess).Should(gbytes.Say(`apply configuration\? \[yN\]: `)) 1192 yes(stdin) 1193 1194 if expectCreationMessage { 1195 pipelineURL := urljoiner.Join(atcServer.URL(), "teams/main/pipelines", "awesome-pipeline") 1196 1197 Eventually(sess).Should(gbytes.Say("pipeline created!")) 1198 Eventually(sess).Should(gbytes.Say(fmt.Sprintf("you can view your pipeline here: %s", pipelineURL))) 1199 } 1200 1201 Eventually(sess).Should(gbytes.Say("the pipeline is currently paused. to unpause, either:")) 1202 Eventually(sess).Should(gbytes.Say(" - run the unpause-pipeline command:")) 1203 Eventually(sess).Should(gbytes.Say(" %s -t %s unpause-pipeline -p awesome-pipeline", regexp.QuoteMeta(flyPath), targetName)) 1204 Eventually(sess).Should(gbytes.Say(" - click play next to the pipeline in the web ui")) 1205 1206 <-sess.Exited 1207 Expect(sess.ExitCode()).To(Equal(0)) 1208 }).To(Change(func() int { 1209 return len(atcServer.ReceivedRequests()) 1210 }).By(4)) 1211 }) 1212 } 1213 1214 Context("when updating an existing pipeline", func() { 1215 BeforeEach(func() { 1216 path, err := atc.Routes.CreatePathForRoute(atc.SaveConfig, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) 1217 Expect(err).NotTo(HaveOccurred()) 1218 1219 path_get, err := atc.Routes.CreatePathForRoute(atc.GetPipeline, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) 1220 Expect(err).NotTo(HaveOccurred()) 1221 1222 atcServer.RouteToHandler("PUT", path, ghttp.CombineHandlers( 1223 ghttp.VerifyHeaderKV(atc.ConfigVersionHeader, "42"), 1224 func(w http.ResponseWriter, r *http.Request) { 1225 config := getConfig(r) 1226 Expect(config).To(MatchYAML(payload)) 1227 }, 1228 ghttp.RespondWith(http.StatusOK, "{}"), 1229 )) 1230 1231 atcServer.RouteToHandler("GET", path_get, ghttp.RespondWithJSONEncoded(http.StatusOK, 1232 atc.Pipeline{Name: "awesome-pipeline", Paused: true, TeamName: "main"})) 1233 1234 config.Resources[0].Name = "updated-name" 1235 }) 1236 1237 AssertSuccessWithPausedPipelineHelp(false) 1238 }) 1239 1240 Context("when the pipeline is being created for the first time", func() { 1241 BeforeEach(func() { 1242 path, err := atc.Routes.CreatePathForRoute(atc.SaveConfig, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) 1243 Expect(err).NotTo(HaveOccurred()) 1244 1245 path_get, err := atc.Routes.CreatePathForRoute(atc.GetPipeline, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) 1246 Expect(err).NotTo(HaveOccurred()) 1247 1248 atcServer.RouteToHandler("PUT", path, ghttp.CombineHandlers( 1249 ghttp.VerifyHeaderKV(atc.ConfigVersionHeader, "42"), 1250 func(w http.ResponseWriter, r *http.Request) { 1251 config := getConfig(r) 1252 Expect(config).To(MatchYAML(payload)) 1253 }, 1254 ghttp.RespondWith(http.StatusCreated, "{}"), 1255 )) 1256 1257 atcServer.RouteToHandler("GET", path_get, ghttp.RespondWithJSONEncoded(http.StatusOK, 1258 atc.Pipeline{Name: "awesome-pipeline", Paused: true, TeamName: "main"})) 1259 1260 config.Resources[0].Name = "updated-name" 1261 }) 1262 1263 AssertSuccessWithPausedPipelineHelp(true) 1264 }) 1265 }) 1266 1267 Context("when the server returns warnings", func() { 1268 BeforeEach(func() { 1269 path, err := atc.Routes.CreatePathForRoute(atc.SaveConfig, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) 1270 Expect(err).NotTo(HaveOccurred()) 1271 1272 atcServer.RouteToHandler("PUT", path, ghttp.CombineHandlers( 1273 ghttp.VerifyHeaderKV(atc.ConfigVersionHeader, "42"), 1274 func(w http.ResponseWriter, r *http.Request) { 1275 config := getConfig(r) 1276 Expect(config).To(MatchYAML(payload)) 1277 }, 1278 ghttp.RespondWith(http.StatusCreated, `{"warnings":[ 1279 {"type":"deprecation","message":"warning-1"}, 1280 {"type":"deprecation","message":"warning-2"} 1281 ]}`), 1282 )) 1283 config.Resources[0].Name = "updated-name" 1284 }) 1285 1286 It("succeeds and prints warnings", func() { 1287 Expect(func() { 1288 flyCmd := exec.Command(flyPath, "-t", targetName, "set-pipeline", "-p", "awesome-pipeline", "-c", configFile.Name()) 1289 1290 stdin, err := flyCmd.StdinPipe() 1291 Expect(err).NotTo(HaveOccurred()) 1292 1293 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 1294 Expect(err).NotTo(HaveOccurred()) 1295 1296 Eventually(sess).Should(gbytes.Say(`apply configuration\? \[yN\]: `)) 1297 yes(stdin) 1298 1299 Eventually(sess.Err).Should(gbytes.Say("DEPRECATION WARNING:")) 1300 Eventually(sess.Err).Should(gbytes.Say(" - warning-1")) 1301 Eventually(sess.Err).Should(gbytes.Say(" - warning-2")) 1302 Eventually(sess).Should(gbytes.Say("pipeline created!")) 1303 1304 <-sess.Exited 1305 Expect(sess.ExitCode()).To(Equal(0)) 1306 }).To(Change(func() int { 1307 return len(atcServer.ReceivedRequests()) 1308 }).By(4)) 1309 }) 1310 }) 1311 1312 Context("when there are no pipeline changes", func() { 1313 It("does not ask for user interaction to apply changes", func() { 1314 flyCmd := exec.Command(flyPath, "-t", targetName, "set-pipeline", "-p", "awesome-pipeline", "-c", configFile.Name()) 1315 1316 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 1317 Expect(err).NotTo(HaveOccurred()) 1318 1319 Consistently(sess).ShouldNot(gbytes.Say("pipeline name:")) 1320 Consistently(sess).ShouldNot(gbytes.Say(`apply configuration\? \[yN\]: `)) 1321 1322 Eventually(sess).Should(gbytes.Say("no changes to apply")) 1323 1324 <-sess.Exited 1325 Expect(sess.ExitCode()).To(Equal(0)) 1326 1327 }) 1328 }) 1329 1330 Context("when the server rejects the request", func() { 1331 BeforeEach(func() { 1332 path, err := atc.Routes.CreatePathForRoute(atc.SaveConfig, rata.Params{"pipeline_name": "awesome-pipeline", "team_name": "main"}) 1333 Expect(err).NotTo(HaveOccurred()) 1334 1335 atcServer.RouteToHandler("PUT", path, func(w http.ResponseWriter, r *http.Request) { 1336 atcServer.CloseClientConnections() 1337 }) 1338 config.Resources[0].Name = "updated-name" 1339 }) 1340 1341 It("prints the error to stderr and exits 1", func() { 1342 flyCmd := exec.Command(flyPath, "-t", targetName, "set-pipeline", "-c", configFile.Name(), "-p", "awesome-pipeline") 1343 1344 stdin, err := flyCmd.StdinPipe() 1345 Expect(err).NotTo(HaveOccurred()) 1346 1347 sess, err := gexec.Start(flyCmd, GinkgoWriter, GinkgoWriter) 1348 Expect(err).NotTo(HaveOccurred()) 1349 1350 Eventually(sess).Should(gbytes.Say(`apply configuration\? \[yN\]: `)) 1351 yes(stdin) 1352 1353 Eventually(sess.Err).Should(gbytes.Say("EOF")) 1354 1355 <-sess.Exited 1356 Expect(sess.ExitCode()).To(Equal(1)) 1357 }) 1358 }) 1359 }) 1360 }) 1361 }) 1362 1363 func getConfig(r *http.Request) []byte { 1364 defer r.Body.Close() 1365 payload, err := ioutil.ReadAll(r.Body) 1366 Expect(err).NotTo(HaveOccurred()) 1367 1368 return payload 1369 }