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