github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/db/build_test.go (about) 1 package db_test 2 3 import ( 4 "context" 5 "crypto/md5" 6 "encoding/hex" 7 "encoding/json" 8 "fmt" 9 "strconv" 10 "time" 11 12 "code.cloudfoundry.org/clock" 13 "code.cloudfoundry.org/lager" 14 sq "github.com/Masterminds/squirrel" 15 "github.com/pf-qiu/concourse/v6/atc" 16 "github.com/pf-qiu/concourse/v6/atc/creds" 17 "github.com/pf-qiu/concourse/v6/atc/creds/dummy" 18 "github.com/pf-qiu/concourse/v6/atc/db" 19 "github.com/pf-qiu/concourse/v6/atc/db/dbtest" 20 "github.com/pf-qiu/concourse/v6/atc/event" 21 "github.com/pf-qiu/concourse/v6/tracing" 22 "github.com/pf-qiu/concourse/v6/vars" 23 . "github.com/onsi/ginkgo" 24 . "github.com/onsi/gomega" 25 gocache "github.com/patrickmn/go-cache" 26 ) 27 28 var _ = Describe("Build", func() { 29 var ( 30 versionsDB db.VersionsDB 31 32 // XXX(dbtests): remove these 33 team db.Team 34 build db.Build 35 job db.Job 36 37 ctx context.Context 38 ) 39 40 BeforeEach(func() { 41 ctx = context.Background() 42 43 versionsDB = db.NewVersionsDB(dbConn, 100, gocache.New(10*time.Second, 10*time.Second)) 44 45 var err error 46 var found bool 47 team, err = teamFactory.CreateTeam(atc.Team{Name: "some-team"}) 48 Expect(err).ToNot(HaveOccurred()) 49 50 pipelineConfig := atc.Config{ 51 Jobs: atc.JobConfigs{ 52 {Name: "some-job"}, 53 }, 54 } 55 56 pipeline, _, err := team.SavePipeline(atc.PipelineRef{Name: "some-build-pipeline"}, pipelineConfig, db.ConfigVersion(1), false) 57 Expect(err).ToNot(HaveOccurred()) 58 59 job, found, err = pipeline.Job("some-job") 60 Expect(err).ToNot(HaveOccurred()) 61 Expect(found).To(BeTrue()) 62 63 build, err = job.CreateBuild() 64 Expect(err).NotTo(HaveOccurred()) 65 }) 66 67 It("has no plan on creation", func() { 68 build, err := team.CreateOneOffBuild() 69 Expect(err).ToNot(HaveOccurred()) 70 Expect(build.HasPlan()).To(BeFalse()) 71 }) 72 73 Describe("LagerData", func() { 74 var build db.Build 75 76 var data lager.Data 77 78 JustBeforeEach(func() { 79 data = build.LagerData() 80 }) 81 82 Context("for a one-off build", func() { 83 BeforeEach(func() { 84 var err error 85 build, err = team.CreateOneOffBuild() 86 Expect(err).ToNot(HaveOccurred()) 87 }) 88 89 It("includes build and team info", func() { 90 Expect(data).To(Equal(lager.Data{ 91 "build_id": build.ID(), 92 "build": build.Name(), 93 "team": team.Name(), 94 })) 95 }) 96 }) 97 98 Context("for a job build", func() { 99 BeforeEach(func() { 100 var err error 101 build, err = defaultJob.CreateBuild() 102 Expect(err).ToNot(HaveOccurred()) 103 }) 104 105 It("includes build, team, pipeline, and job info", func() { 106 Expect(data).To(Equal(lager.Data{ 107 "build_id": build.ID(), 108 "build": build.Name(), 109 "team": build.TeamName(), 110 "pipeline": build.PipelineName(), 111 "job": defaultJob.Name(), 112 })) 113 }) 114 }) 115 116 Context("for a resource build", func() { 117 BeforeEach(func() { 118 var err error 119 var created bool 120 build, created, err = defaultResource.CreateBuild(context.TODO(), false, atc.Plan{}) 121 Expect(err).ToNot(HaveOccurred()) 122 Expect(created).To(BeTrue()) 123 }) 124 125 It("includes build, team, and pipeline", func() { 126 Expect(data).To(Equal(lager.Data{ 127 "build_id": build.ID(), 128 "build": build.Name(), 129 "team": build.TeamName(), 130 "pipeline": build.PipelineName(), 131 "resource": defaultResource.Name(), 132 })) 133 }) 134 }) 135 136 Context("for a resource type build", func() { 137 BeforeEach(func() { 138 var err error 139 var created bool 140 build, created, err = defaultResourceType.CreateBuild(context.TODO(), false, atc.Plan{}) 141 Expect(err).ToNot(HaveOccurred()) 142 Expect(created).To(BeTrue()) 143 }) 144 145 It("includes build, team, and pipeline", func() { 146 Expect(data).To(Equal(lager.Data{ 147 "build_id": build.ID(), 148 "build": build.Name(), 149 "team": build.TeamName(), 150 "pipeline": build.PipelineName(), 151 "resource_type": defaultResourceType.Name(), 152 })) 153 }) 154 }) 155 }) 156 157 Describe("SyslogTag", func() { 158 var build db.Build 159 160 var originID event.OriginID = "some-origin" 161 var tag string 162 163 JustBeforeEach(func() { 164 tag = build.SyslogTag(originID) 165 }) 166 167 Context("for a one-off build", func() { 168 BeforeEach(func() { 169 var err error 170 build, err = team.CreateOneOffBuild() 171 Expect(err).ToNot(HaveOccurred()) 172 }) 173 174 It("includes build and team info", func() { 175 Expect(tag).To(Equal(fmt.Sprintf("%s/%d/%s", team.Name(), build.ID(), originID))) 176 }) 177 }) 178 179 Context("for a job build", func() { 180 BeforeEach(func() { 181 var err error 182 build, err = defaultJob.CreateBuild() 183 Expect(err).ToNot(HaveOccurred()) 184 }) 185 186 It("includes build, team, pipeline, and job info", func() { 187 Expect(tag).To(Equal(fmt.Sprintf("%s/%s/%s/%s/%s", defaultJob.TeamName(), defaultJob.PipelineName(), defaultJob.Name(), build.Name(), originID))) 188 }) 189 }) 190 191 Context("for a resource build", func() { 192 BeforeEach(func() { 193 var err error 194 var created bool 195 build, created, err = defaultResource.CreateBuild(context.TODO(), false, atc.Plan{}) 196 Expect(err).ToNot(HaveOccurred()) 197 Expect(created).To(BeTrue()) 198 }) 199 200 It("includes build, team, and pipeline", func() { 201 Expect(tag).To(Equal(fmt.Sprintf("%s/%s/%s/%d/%s", defaultResource.TeamName(), defaultResource.PipelineName(), defaultResource.Name(), build.ID(), originID))) 202 }) 203 }) 204 205 Context("for a resource type build", func() { 206 BeforeEach(func() { 207 var err error 208 var created bool 209 build, created, err = defaultResourceType.CreateBuild(context.TODO(), false, atc.Plan{}) 210 Expect(err).ToNot(HaveOccurred()) 211 Expect(created).To(BeTrue()) 212 }) 213 214 It("includes build, team, and pipeline", func() { 215 Expect(tag).To(Equal(fmt.Sprintf("%s/%s/%s/%d/%s", defaultResourceType.TeamName(), defaultResourceType.PipelineName(), defaultResourceType.Name(), build.ID(), originID))) 216 }) 217 }) 218 }) 219 220 Describe("TracingAttrs", func() { 221 var build db.Build 222 223 var attrs tracing.Attrs 224 225 JustBeforeEach(func() { 226 attrs = build.TracingAttrs() 227 }) 228 229 Context("for a one-off build", func() { 230 BeforeEach(func() { 231 var err error 232 build, err = team.CreateOneOffBuild() 233 Expect(err).ToNot(HaveOccurred()) 234 }) 235 236 It("includes build and team info", func() { 237 Expect(attrs).To(Equal(tracing.Attrs{ 238 "build_id": strconv.Itoa(build.ID()), 239 "build": build.Name(), 240 "team": team.Name(), 241 })) 242 }) 243 }) 244 245 Context("for a job build", func() { 246 BeforeEach(func() { 247 var err error 248 build, err = defaultJob.CreateBuild() 249 Expect(err).ToNot(HaveOccurred()) 250 }) 251 252 It("includes build, team, pipeline, and job info", func() { 253 Expect(attrs).To(Equal(tracing.Attrs{ 254 "build_id": strconv.Itoa(build.ID()), 255 "build": build.Name(), 256 "team": build.TeamName(), 257 "pipeline": build.PipelineName(), 258 "job": defaultJob.Name(), 259 })) 260 }) 261 }) 262 263 Context("for a resource build", func() { 264 BeforeEach(func() { 265 var err error 266 var created bool 267 build, created, err = defaultResource.CreateBuild(context.TODO(), false, atc.Plan{}) 268 Expect(err).ToNot(HaveOccurred()) 269 Expect(created).To(BeTrue()) 270 }) 271 272 It("includes build, team, and pipeline", func() { 273 Expect(attrs).To(Equal(tracing.Attrs{ 274 "build_id": strconv.Itoa(build.ID()), 275 "build": build.Name(), 276 "team": build.TeamName(), 277 "pipeline": build.PipelineName(), 278 "resource": defaultResource.Name(), 279 })) 280 }) 281 }) 282 283 Context("for a resource type build", func() { 284 BeforeEach(func() { 285 var err error 286 var created bool 287 build, created, err = defaultResourceType.CreateBuild(context.TODO(), false, atc.Plan{}) 288 Expect(err).ToNot(HaveOccurred()) 289 Expect(created).To(BeTrue()) 290 }) 291 292 It("includes build, team, and pipeline", func() { 293 Expect(attrs).To(Equal(tracing.Attrs{ 294 "build_id": strconv.Itoa(build.ID()), 295 "build": build.Name(), 296 "team": build.TeamName(), 297 "pipeline": build.PipelineName(), 298 "resource_type": defaultResourceType.Name(), 299 })) 300 }) 301 }) 302 }) 303 304 Describe("Reload", func() { 305 It("updates the model", func() { 306 started, err := build.Start(atc.Plan{}) 307 Expect(err).NotTo(HaveOccurred()) 308 Expect(started).To(BeTrue()) 309 310 Expect(build.Status()).To(Equal(db.BuildStatusPending)) 311 312 found, err := build.Reload() 313 Expect(err).NotTo(HaveOccurred()) 314 Expect(found).To(BeTrue()) 315 Expect(build.Status()).To(Equal(db.BuildStatusStarted)) 316 }) 317 }) 318 319 Describe("Drain", func() { 320 It("defaults drain to false in the beginning", func() { 321 Expect(build.IsDrained()).To(BeFalse()) 322 }) 323 324 It("has drain set to true after a drain and a reload", func() { 325 err := build.SetDrained(true) 326 Expect(err).NotTo(HaveOccurred()) 327 328 drained := build.IsDrained() 329 Expect(drained).To(BeTrue()) 330 331 _, err = build.Reload() 332 Expect(err).NotTo(HaveOccurred()) 333 drained = build.IsDrained() 334 Expect(drained).To(BeTrue()) 335 }) 336 }) 337 338 Describe("Start", func() { 339 var err error 340 var started bool 341 var plan atc.Plan 342 343 BeforeEach(func() { 344 plan = atc.Plan{ 345 ID: atc.PlanID("56"), 346 Get: &atc.GetPlan{ 347 Type: "some-type", 348 Name: "some-name", 349 Resource: "some-resource", 350 Source: atc.Source{"some": "source"}, 351 Params: atc.Params{"some": "params"}, 352 Version: &atc.Version{"some": "version"}, 353 Tags: atc.Tags{"some-tags"}, 354 VersionedResourceTypes: atc.VersionedResourceTypes{ 355 { 356 ResourceType: atc.ResourceType{ 357 Name: "some-name", 358 Source: atc.Source{"some": "source"}, 359 Type: "some-type", 360 Privileged: true, 361 Tags: atc.Tags{"some-tags"}, 362 }, 363 Version: atc.Version{"some-resource-type": "version"}, 364 }, 365 }, 366 }, 367 } 368 }) 369 370 JustBeforeEach(func() { 371 started, err = build.Start(plan) 372 Expect(err).NotTo(HaveOccurred()) 373 }) 374 375 Context("build has been aborted", func() { 376 BeforeEach(func() { 377 err = build.MarkAsAborted() 378 Expect(err).NotTo(HaveOccurred()) 379 }) 380 381 It("does not start the build", func() { 382 Expect(started).To(BeFalse()) 383 }) 384 385 It("leaves the build in pending state", func() { 386 found, err := build.Reload() 387 Expect(err).NotTo(HaveOccurred()) 388 Expect(found).To(BeTrue()) 389 Expect(build.Status()).To(Equal(db.BuildStatusPending)) 390 }) 391 }) 392 393 Context("build has not been aborted", func() { 394 It("starts the build", func() { 395 Expect(started).To(BeTrue()) 396 }) 397 398 It("creates Start event", func() { 399 found, err := build.Reload() 400 Expect(err).NotTo(HaveOccurred()) 401 Expect(found).To(BeTrue()) 402 Expect(build.Status()).To(Equal(db.BuildStatusStarted)) 403 404 events, err := build.Events(0) 405 Expect(err).NotTo(HaveOccurred()) 406 407 defer db.Close(events) 408 409 Expect(events.Next()).To(Equal(envelope(event.Status{ 410 Status: atc.StatusStarted, 411 Time: build.StartTime().Unix(), 412 }))) 413 }) 414 415 It("updates build status", func() { 416 found, err := build.Reload() 417 Expect(err).NotTo(HaveOccurred()) 418 Expect(found).To(BeTrue()) 419 Expect(build.Status()).To(Equal(db.BuildStatusStarted)) 420 }) 421 422 It("saves the public plan", func() { 423 found, err := build.Reload() 424 Expect(err).NotTo(HaveOccurred()) 425 Expect(found).To(BeTrue()) 426 Expect(build.HasPlan()).To(BeTrue()) 427 Expect(build.PublicPlan()).To(Equal(plan.Public())) 428 }) 429 }) 430 }) 431 432 Describe("Finish", func() { 433 var scenario *dbtest.Scenario 434 var build db.Build 435 var expectedOutputs []db.AlgorithmVersion 436 437 BeforeEach(func() { 438 pipelineConfig := atc.Config{ 439 Jobs: atc.JobConfigs{ 440 { 441 Name: "some-job", 442 PlanSequence: []atc.Step{ 443 { 444 Config: &atc.GetStep{ 445 Name: "input-1", 446 Resource: "some-resource", 447 }, 448 }, 449 { 450 Config: &atc.GetStep{ 451 Name: "input-2", 452 Resource: "some-other-resource", 453 }, 454 }, 455 { 456 Config: &atc.GetStep{ 457 Name: "input-3", 458 Resource: "some-resource", 459 }, 460 }, 461 { 462 Config: &atc.GetStep{ 463 Name: "input-4", 464 Resource: "some-resource", 465 }, 466 }, 467 { 468 Config: &atc.PutStep{ 469 Name: "output-1", 470 Resource: "some-resource", 471 }, 472 }, 473 { 474 Config: &atc.PutStep{ 475 Name: "output-2", 476 Resource: "some-resource", 477 }, 478 }, 479 }, 480 }, 481 { 482 Name: "downstream-job", 483 PlanSequence: []atc.Step{ 484 { 485 Config: &atc.GetStep{ 486 Name: "some-resource", 487 Passed: []string{"some-job"}, 488 }, 489 }, 490 }, 491 }, 492 { 493 Name: "no-request-job", 494 PlanSequence: []atc.Step{ 495 { 496 Config: &atc.GetStep{ 497 Name: "some-resource", 498 Passed: []string{"downstream-job"}, 499 }, 500 }, 501 }, 502 }, 503 }, 504 Resources: atc.ResourceConfigs{ 505 { 506 Name: "some-resource", 507 Type: dbtest.BaseResourceType, 508 Source: atc.Source{"some": "source"}, 509 }, 510 { 511 Name: "some-other-resource", 512 Type: dbtest.BaseResourceType, 513 Source: atc.Source{"some": "other-source"}, 514 }, 515 }, 516 } 517 518 scenario = dbtest.Setup( 519 builder.WithPipeline(pipelineConfig), 520 builder.WithResourceVersions( 521 "some-resource", 522 atc.Version{"ver": "1"}, 523 atc.Version{"ver": "2"}, 524 ), 525 builder.WithResourceVersions( 526 "some-other-resource", 527 atc.Version{"ver": "1"}, 528 atc.Version{"ver": "2"}, 529 atc.Version{"ver": "3"}, 530 ), 531 builder.WithJobBuild(&build, "some-job", dbtest.JobInputs{ 532 { 533 Name: "input-1", 534 Version: atc.Version{"ver": "1"}, 535 }, 536 { 537 Name: "input-2", 538 Version: atc.Version{"ver": "3"}, 539 }, 540 { 541 Name: "input-3", 542 Version: atc.Version{"ver": "2"}, 543 }, 544 { 545 Name: "input-4", 546 Version: atc.Version{"ver": "2"}, 547 }, 548 }, dbtest.JobOutputs{ 549 "output-1": atc.Version{"ver": "2"}, 550 "output-2": atc.Version{"ver": "3"}, 551 }), 552 ) 553 554 Expect(build.Finish(db.BuildStatusSucceeded)).To(Succeed()) 555 556 expectedOutputs = []db.AlgorithmVersion{ 557 { 558 Version: db.ResourceVersion(convertToMD5(atc.Version{"ver": "1"})), 559 ResourceID: scenario.Resource("some-resource").ID(), 560 }, 561 { 562 Version: db.ResourceVersion(convertToMD5(atc.Version{"ver": "3"})), 563 ResourceID: scenario.Resource("some-other-resource").ID(), 564 }, 565 { 566 Version: db.ResourceVersion(convertToMD5(atc.Version{"ver": "2"})), 567 ResourceID: scenario.Resource("some-resource").ID(), 568 }, 569 { 570 Version: db.ResourceVersion(convertToMD5(atc.Version{"ver": "3"})), 571 ResourceID: scenario.Resource("some-resource").ID(), 572 }, 573 } 574 }) 575 576 It("creates Finish event", func() { 577 found, err := build.Reload() 578 Expect(err).NotTo(HaveOccurred()) 579 Expect(found).To(BeTrue()) 580 Expect(build.Status()).To(Equal(db.BuildStatusSucceeded)) 581 582 events, err := build.Events(0) 583 Expect(err).NotTo(HaveOccurred()) 584 585 defer db.Close(events) 586 587 Expect(events.Next()).To(Equal(envelope(event.Status{ 588 Status: atc.StatusSucceeded, 589 Time: build.EndTime().Unix(), 590 }))) 591 }) 592 593 It("updates build status", func() { 594 found, err := build.Reload() 595 Expect(err).NotTo(HaveOccurred()) 596 Expect(found).To(BeTrue()) 597 Expect(build.Status()).To(Equal(db.BuildStatusSucceeded)) 598 }) 599 600 It("clears out the private plan", func() { 601 found, err := build.Reload() 602 Expect(err).NotTo(HaveOccurred()) 603 Expect(found).To(BeTrue()) 604 Expect(build.PrivatePlan()).To(Equal(atc.Plan{})) 605 }) 606 607 It("sets completed to true", func() { 608 Expect(build.IsCompleted()).To(BeFalse()) 609 Expect(build.IsRunning()).To(BeTrue()) 610 611 found, err := build.Reload() 612 Expect(err).NotTo(HaveOccurred()) 613 Expect(found).To(BeTrue()) 614 Expect(build.IsCompleted()).To(BeTrue()) 615 Expect(build.IsRunning()).To(BeFalse()) 616 }) 617 618 It("inserts inputs and outputs into successful build versions", func() { 619 outputs, err := versionsDB.SuccessfulBuildOutputs(ctx, build.ID()) 620 Expect(err).NotTo(HaveOccurred()) 621 Expect(outputs).To(ConsistOf(expectedOutputs)) 622 }) 623 624 Context("rerunning a build", func() { 625 var ( 626 job db.Job 627 628 pdBuild, pdBuild2, rrBuild db.Build 629 err error 630 latestCompletedBuildCol = "latest_completed_build_id" 631 nextBuildCol = "next_build_id" 632 transitionBuildCol = "transition_build_id" 633 ) 634 635 BeforeEach(func() { 636 job = scenario.Job("some-job") 637 }) 638 639 Context("when there is a pending build that is not a rerun", func() { 640 BeforeEach(func() { 641 pdBuild, err = job.CreateBuild() 642 Expect(err).NotTo(HaveOccurred()) 643 }) 644 645 Context("when rerunning the latest completed build", func() { 646 BeforeEach(func() { 647 rrBuild, err = job.RerunBuild(build) 648 Expect(err).NotTo(HaveOccurred()) 649 }) 650 651 Context("when the rerun finishes and status changed", func() { 652 BeforeEach(func() { 653 err = rrBuild.Finish(db.BuildStatusFailed) 654 Expect(err).NotTo(HaveOccurred()) 655 }) 656 657 It("updates job latest finished build id", func() { 658 Expect(getJobBuildID(latestCompletedBuildCol, job.ID())).To(Equal(rrBuild.ID())) 659 }) 660 661 It("updates job next build id to the pending build", func() { 662 Expect(getJobBuildID(nextBuildCol, job.ID())).To(Equal(pdBuild.ID())) 663 }) 664 665 It("updates transition build id to the rerun build", func() { 666 Expect(getJobBuildID(transitionBuildCol, job.ID())).To(Equal(rrBuild.ID())) 667 }) 668 }) 669 670 Context("when there is another pending build that is not a rerun and the first pending build finishes", func() { 671 BeforeEach(func() { 672 pdBuild2, err = job.CreateBuild() 673 Expect(err).NotTo(HaveOccurred()) 674 675 err = pdBuild.Finish(db.BuildStatusSucceeded) 676 Expect(err).NotTo(HaveOccurred()) 677 }) 678 679 It("updates job next build id to be the next non rerun pending build", func() { 680 Expect(getJobBuildID(nextBuildCol, job.ID())).To(Equal(pdBuild2.ID())) 681 }) 682 683 It("updates job latest finished build id", func() { 684 Expect(getJobBuildID(latestCompletedBuildCol, job.ID())).To(Equal(pdBuild.ID())) 685 }) 686 }) 687 }) 688 689 Context("when rerunning the pending build and the pending build finished", func() { 690 BeforeEach(func() { 691 rrBuild, err = job.RerunBuild(pdBuild) 692 Expect(err).NotTo(HaveOccurred()) 693 694 err = pdBuild.Finish(db.BuildStatusSucceeded) 695 Expect(err).NotTo(HaveOccurred()) 696 }) 697 698 It("updates job next build id to the rerun build", func() { 699 Expect(getJobBuildID(nextBuildCol, job.ID())).To(Equal(rrBuild.ID())) 700 }) 701 702 It("updates job latest finished build id", func() { 703 Expect(getJobBuildID(latestCompletedBuildCol, job.ID())).To(Equal(pdBuild.ID())) 704 }) 705 706 Context("when rerunning the rerun build", func() { 707 var rrBuild2 db.Build 708 709 BeforeEach(func() { 710 err = rrBuild.Finish(db.BuildStatusSucceeded) 711 Expect(err).NotTo(HaveOccurred()) 712 713 rrBuild2, err = job.RerunBuild(rrBuild) 714 Expect(err).NotTo(HaveOccurred()) 715 }) 716 717 It("updates job next build id to the rerun build", func() { 718 Expect(getJobBuildID(nextBuildCol, job.ID())).To(Equal(rrBuild2.ID())) 719 }) 720 721 It("updates job latest finished build id", func() { 722 Expect(getJobBuildID(latestCompletedBuildCol, job.ID())).To(Equal(rrBuild.ID())) 723 }) 724 }) 725 }) 726 727 Context("when pending build finished and rerunning a non latest build and it finishes", func() { 728 BeforeEach(func() { 729 err = pdBuild.Finish(db.BuildStatusErrored) 730 Expect(err).NotTo(HaveOccurred()) 731 732 rrBuild, err = job.RerunBuild(build) 733 Expect(err).NotTo(HaveOccurred()) 734 735 err = rrBuild.Finish(db.BuildStatusSucceeded) 736 Expect(err).NotTo(HaveOccurred()) 737 }) 738 739 It("updates job next build id to nul", func() { 740 _, nextBuild, err := job.FinishedAndNextBuild() 741 Expect(err).NotTo(HaveOccurred()) 742 Expect(nextBuild).To(BeNil()) 743 }) 744 745 It("does not updates job latest finished build id", func() { 746 Expect(getJobBuildID(latestCompletedBuildCol, job.ID())).To(Equal(pdBuild.ID())) 747 }) 748 749 It("does not updates transition build id", func() { 750 Expect(getJobBuildID(transitionBuildCol, job.ID())).To(Equal(pdBuild.ID())) 751 }) 752 }) 753 }) 754 }) 755 756 Context("when requesting schedule", func() { 757 It("request schedule on the downstream job", func() { 758 job := scenario.Job("some-job") 759 downstreamJob := scenario.Job("downstream-job") 760 761 newBuild, err := job.CreateBuild() 762 Expect(err).NotTo(HaveOccurred()) 763 764 requestedSchedule := downstreamJob.ScheduleRequestedTime() 765 766 err = newBuild.Finish(db.BuildStatusSucceeded) 767 Expect(err).NotTo(HaveOccurred()) 768 769 found, err := downstreamJob.Reload() 770 Expect(err).ToNot(HaveOccurred()) 771 Expect(found).To(BeTrue()) 772 773 Expect(downstreamJob.ScheduleRequestedTime()).Should(BeTemporally(">", requestedSchedule)) 774 }) 775 776 It("do not request schedule on jobs that are not directly downstream", func() { 777 job := scenario.Job("some-job") 778 noRequestJob := scenario.Job("no-request-job") 779 780 newBuild, err := job.CreateBuild() 781 Expect(err).NotTo(HaveOccurred()) 782 783 requestedSchedule := noRequestJob.ScheduleRequestedTime() 784 785 err = newBuild.Finish(db.BuildStatusSucceeded) 786 Expect(err).NotTo(HaveOccurred()) 787 788 found, err := noRequestJob.Reload() 789 Expect(err).ToNot(HaveOccurred()) 790 Expect(found).To(BeTrue()) 791 792 Expect(noRequestJob.ScheduleRequestedTime()).Should(BeTemporally("==", requestedSchedule)) 793 }) 794 }) 795 796 Context("archiving pipelines", func() { 797 var childPipeline db.Pipeline 798 799 BeforeEach(func() { 800 By("creating a child pipeline") 801 build, _ := defaultJob.CreateBuild() 802 childPipeline, _, _ = build.SavePipeline(atc.PipelineRef{Name: "child1-pipeline"}, defaultTeam.ID(), defaultPipelineConfig, db.ConfigVersion(0), false) 803 build.Finish(db.BuildStatusSucceeded) 804 805 childPipeline.Reload() 806 Expect(childPipeline.Archived()).To(BeFalse()) 807 }) 808 809 Context("build is successful", func() { 810 It("archives pipelines no longer set by the job", func() { 811 By("no longer setting the child pipeline") 812 build2, _ := defaultJob.CreateBuild() 813 build2.Finish(db.BuildStatusSucceeded) 814 815 childPipeline.Reload() 816 Expect(childPipeline.Archived()).To(BeTrue()) 817 }) 818 819 Context("chain of pipelines setting each other... like a russian doll set...", func() { 820 It("archives all descendent pipelines", func() { 821 childPipelines := []db.Pipeline{childPipeline} 822 823 By("creating a chain of pipelines, previous pipeline setting the next pipeline") 824 for i := 0; i < 5; i++ { 825 job, _, _ := childPipeline.Job("some-job") 826 build, _ := job.CreateBuild() 827 childPipeline, _, _ = build.SavePipeline(atc.PipelineRef{Name: "child-pipeline-" + strconv.Itoa(i)}, defaultTeam.ID(), defaultPipelineConfig, db.ConfigVersion(0), false) 828 build.Finish(db.BuildStatusSucceeded) 829 childPipelines = append(childPipelines, childPipeline) 830 } 831 832 By("parent pipeline no longer sets child pipeline in most recent build") 833 build, _ := defaultJob.CreateBuild() 834 build.Finish(db.BuildStatusSucceeded) 835 836 for _, pipeline := range childPipelines { 837 pipeline.Reload() 838 Expect(pipeline.Archived()).To(BeTrue()) 839 } 840 841 }) 842 }) 843 844 Context("when the pipeline is not set by build", func() { 845 It("never gets archived", func() { 846 build, _ := defaultJob.CreateBuild() 847 teamPipeline, _, _ := defaultTeam.SavePipeline(atc.PipelineRef{Name: "team-pipeline"}, defaultPipelineConfig, db.ConfigVersion(0), false) 848 build.Finish(db.BuildStatusSucceeded) 849 850 teamPipeline.Reload() 851 Expect(teamPipeline.Archived()).To(BeFalse()) 852 }) 853 }) 854 }) 855 Context("build is not successful", func() { 856 It("does not archive pipelines", func() { 857 By("no longer setting the child pipeline") 858 build2, _ := defaultJob.CreateBuild() 859 build2.Finish(db.BuildStatusFailed) 860 861 childPipeline.Reload() 862 Expect(childPipeline.Archived()).To(BeFalse()) 863 }) 864 }) 865 }) 866 }) 867 868 Describe("Variables", func() { 869 var ( 870 globalSecrets creds.Secrets 871 varSourcePool creds.VarSourcePool 872 ) 873 874 BeforeEach(func() { 875 globalSecrets = &dummy.Secrets{StaticVariables: vars.StaticVariables{"foo": "bar"}} 876 877 credentialManagement := creds.CredentialManagementConfig{ 878 RetryConfig: creds.SecretRetryConfig{ 879 Attempts: 5, 880 Interval: time.Second, 881 }, 882 CacheConfig: creds.SecretCacheConfig{ 883 Enabled: true, 884 Duration: time.Minute, 885 DurationNotFound: time.Minute, 886 PurgeInterval: time.Minute * 10, 887 }, 888 } 889 varSourcePool = creds.NewVarSourcePool(logger, credentialManagement, 1*time.Minute, 1*time.Second, clock.NewClock()) 890 }) 891 892 Context("when the build is a one-off build", func() { 893 var build db.Build 894 895 BeforeEach(func() { 896 var err error 897 build, err = defaultTeam.CreateOneOffBuild() 898 Expect(err).ToNot(HaveOccurred()) 899 }) 900 901 It("fetches from the global secrets", func() { 902 v, err := build.Variables(logger, globalSecrets, varSourcePool) 903 Expect(err).ToNot(HaveOccurred()) 904 905 val, found, err := v.Get(vars.Reference{Path: "foo"}) 906 Expect(err).ToNot(HaveOccurred()) 907 Expect(found).To(BeTrue()) 908 Expect(val).To(Equal("bar")) 909 }) 910 }) 911 912 Context("when the build is a job build", func() { 913 var build db.Build 914 915 BeforeEach(func() { 916 config := defaultPipelineConfig 917 config.VarSources = append(config.VarSources, atc.VarSourceConfig{ 918 Name: "some-source", 919 Type: "dummy", 920 Config: map[string]interface{}{ 921 "vars": map[string]interface{}{"baz": "caz"}, 922 }, 923 }) 924 925 pipeline, _, err := defaultTeam.SavePipeline(defaultPipelineRef, config, defaultPipeline.ConfigVersion(), false) 926 Expect(err).ToNot(HaveOccurred()) 927 928 job, found, err := pipeline.Job(defaultJob.Name()) 929 Expect(err).ToNot(HaveOccurred()) 930 Expect(found).To(BeTrue()) 931 932 build, err = job.CreateBuild() 933 Expect(err).ToNot(HaveOccurred()) 934 }) 935 936 It("fetches from the global secrets", func() { 937 v, err := build.Variables(logger, globalSecrets, varSourcePool) 938 Expect(err).ToNot(HaveOccurred()) 939 940 val, found, err := v.Get(vars.Reference{Path: "foo"}) 941 Expect(err).ToNot(HaveOccurred()) 942 Expect(found).To(BeTrue()) 943 Expect(val).To(Equal("bar")) 944 }) 945 946 It("fetches from the var sources", func() { 947 v, err := build.Variables(logger, globalSecrets, varSourcePool) 948 Expect(err).ToNot(HaveOccurred()) 949 950 val, found, err := v.Get(vars.Reference{Source: "some-source", Path: "baz"}) 951 Expect(err).ToNot(HaveOccurred()) 952 Expect(found).To(BeTrue()) 953 Expect(val).To(Equal("caz")) 954 }) 955 }) 956 }) 957 958 Describe("Abort", func() { 959 JustBeforeEach(func() { 960 err := build.MarkAsAborted() 961 Expect(err).NotTo(HaveOccurred()) 962 963 found, err := build.Reload() 964 Expect(err).NotTo(HaveOccurred()) 965 Expect(found).To(BeTrue()) 966 }) 967 968 It("updates aborted to true", func() { 969 Expect(build.IsAborted()).To(BeTrue()) 970 }) 971 972 Context("request job rescheudle", func() { 973 JustBeforeEach(func() { 974 found, err := job.Reload() 975 Expect(err).NotTo(HaveOccurred()) 976 Expect(found).To(BeTrue()) 977 }) 978 979 Context("when build is in pending state", func() { 980 BeforeEach(func() { 981 time.Sleep(1 * time.Second) 982 }) 983 984 It("requests the job to reschedule immediately", func() { 985 Expect(job.ScheduleRequestedTime()).Should(BeTemporally("~", time.Now(), time.Second)) 986 }) 987 }) 988 989 Context("when build is not in pending state", func() { 990 var firstRequestTime time.Time 991 992 BeforeEach(func() { 993 firstRequestTime = time.Now() 994 995 time.Sleep(1 * time.Second) 996 997 err := build.Finish(db.BuildStatusFailed) 998 Expect(err).NotTo(HaveOccurred()) 999 1000 found, err := build.Reload() 1001 Expect(err).NotTo(HaveOccurred()) 1002 Expect(found).To(BeTrue()) 1003 }) 1004 1005 It("does not request reschedule", func() { 1006 Expect(job.ScheduleRequestedTime()).Should(BeTemporally("~", firstRequestTime, time.Second)) 1007 }) 1008 }) 1009 }) 1010 }) 1011 1012 Describe("Events", func() { 1013 It("saves and emits status events", func() { 1014 By("allowing you to subscribe when no events have yet occurred") 1015 events, err := build.Events(0) 1016 Expect(err).NotTo(HaveOccurred()) 1017 1018 defer db.Close(events) 1019 1020 By("emitting a status event when started") 1021 started, err := build.Start(atc.Plan{}) 1022 Expect(err).NotTo(HaveOccurred()) 1023 Expect(started).To(BeTrue()) 1024 1025 found, err := build.Reload() 1026 Expect(err).NotTo(HaveOccurred()) 1027 Expect(found).To(BeTrue()) 1028 1029 Expect(events.Next()).To(Equal(envelope(event.Status{ 1030 Status: atc.StatusStarted, 1031 Time: build.StartTime().Unix(), 1032 }))) 1033 1034 By("emitting a status event when finished") 1035 err = build.Finish(db.BuildStatusSucceeded) 1036 Expect(err).NotTo(HaveOccurred()) 1037 1038 found, err = build.Reload() 1039 Expect(err).NotTo(HaveOccurred()) 1040 Expect(found).To(BeTrue()) 1041 1042 Expect(events.Next()).To(Equal(envelope(event.Status{ 1043 Status: atc.StatusSucceeded, 1044 Time: build.EndTime().Unix(), 1045 }))) 1046 1047 By("ending the stream when finished") 1048 _, err = events.Next() 1049 Expect(err).To(Equal(db.ErrEndOfBuildEventStream)) 1050 }) 1051 1052 It("emits pre-bigint migration events", func() { 1053 started, err := build.Start(atc.Plan{}) 1054 Expect(err).NotTo(HaveOccurred()) 1055 Expect(started).To(BeTrue()) 1056 1057 found, err := build.Reload() 1058 Expect(err).NotTo(HaveOccurred()) 1059 Expect(found).To(BeTrue()) 1060 1061 _, err = dbConn.Exec(`UPDATE build_events SET build_id_old = build_id, build_id = NULL WHERE build_id = $1`, build.ID()) 1062 Expect(err).NotTo(HaveOccurred()) 1063 1064 events, err := build.Events(0) 1065 Expect(err).NotTo(HaveOccurred()) 1066 1067 defer db.Close(events) 1068 Expect(events.Next()).To(Equal(envelope(event.Status{ 1069 Status: atc.StatusStarted, 1070 Time: build.StartTime().Unix(), 1071 }))) 1072 }) 1073 }) 1074 1075 Describe("SaveEvent", func() { 1076 It("saves and propagates events correctly", func() { 1077 By("allowing you to subscribe when no events have yet occurred") 1078 events, err := build.Events(0) 1079 Expect(err).NotTo(HaveOccurred()) 1080 1081 defer db.Close(events) 1082 1083 By("saving them in order") 1084 err = build.SaveEvent(event.Log{ 1085 Payload: "some ", 1086 }) 1087 Expect(err).NotTo(HaveOccurred()) 1088 1089 Expect(events.Next()).To(Equal(envelope(event.Log{ 1090 Payload: "some ", 1091 }))) 1092 1093 err = build.SaveEvent(event.Log{ 1094 Payload: "log", 1095 }) 1096 Expect(err).NotTo(HaveOccurred()) 1097 1098 Expect(events.Next()).To(Equal(envelope(event.Log{ 1099 Payload: "log", 1100 }))) 1101 1102 By("allowing you to subscribe from an offset") 1103 eventsFrom1, err := build.Events(1) 1104 Expect(err).NotTo(HaveOccurred()) 1105 1106 defer db.Close(eventsFrom1) 1107 1108 Expect(eventsFrom1.Next()).To(Equal(envelope(event.Log{ 1109 Payload: "log", 1110 }))) 1111 1112 By("notifying those waiting on events as soon as they're saved") 1113 nextEvent := make(chan event.Envelope) 1114 nextErr := make(chan error) 1115 1116 go func() { 1117 event, err := events.Next() 1118 if err != nil { 1119 nextErr <- err 1120 } else { 1121 nextEvent <- event 1122 } 1123 }() 1124 1125 Consistently(nextEvent).ShouldNot(Receive()) 1126 Consistently(nextErr).ShouldNot(Receive()) 1127 1128 err = build.SaveEvent(event.Log{ 1129 Payload: "log 2", 1130 }) 1131 Expect(err).NotTo(HaveOccurred()) 1132 1133 Eventually(nextEvent).Should(Receive(Equal(envelope(event.Log{ 1134 Payload: "log 2", 1135 })))) 1136 1137 By("returning ErrBuildEventStreamClosed for Next calls after Close") 1138 events3, err := build.Events(0) 1139 Expect(err).NotTo(HaveOccurred()) 1140 1141 err = events3.Close() 1142 Expect(err).NotTo(HaveOccurred()) 1143 1144 Eventually(func() error { 1145 _, err := events3.Next() 1146 return err 1147 }).Should(Equal(db.ErrBuildEventStreamClosed)) 1148 }) 1149 }) 1150 1151 Describe("SaveOutput", func() { 1152 var pipelineConfig atc.Config 1153 1154 var scenario *dbtest.Scenario 1155 var build db.Build 1156 1157 var outputVersion atc.Version 1158 1159 BeforeEach(func() { 1160 atc.EnableGlobalResources = true 1161 1162 pipelineConfig = atc.Config{ 1163 Jobs: atc.JobConfigs{ 1164 { 1165 Name: "some-job", 1166 PlanSequence: []atc.Step{ 1167 { 1168 Config: &atc.GetStep{ 1169 Name: "some-resource", 1170 }, 1171 }, 1172 }, 1173 }, 1174 { 1175 Name: "some-other-job", 1176 PlanSequence: []atc.Step{ 1177 { 1178 Config: &atc.GetStep{ 1179 Name: "some-other-resource", 1180 }, 1181 }, 1182 }, 1183 }, 1184 }, 1185 Resources: atc.ResourceConfigs{ 1186 { 1187 Name: "some-resource", 1188 Type: dbtest.BaseResourceType, 1189 Source: atc.Source{"some": "source"}, 1190 }, 1191 { 1192 Name: "some-other-resource", 1193 Type: dbtest.BaseResourceType, 1194 Source: atc.Source{"some": "other-source"}, 1195 }, 1196 }, 1197 } 1198 1199 scenario = dbtest.Setup( 1200 builder.WithPipeline(pipelineConfig), 1201 builder.WithResourceVersions("some-resource", atc.Version{"some": "version"}), 1202 builder.WithResourceVersions("some-other-resource", atc.Version{"some": "other-version"}), 1203 builder.WithJobBuild(&build, "some-job", dbtest.JobInputs{ 1204 { 1205 Name: "some-resource", 1206 Version: atc.Version{"some": "version"}, 1207 }, 1208 }, dbtest.JobOutputs{}), 1209 ) 1210 1211 outputVersion = atc.Version{"some": "new-version"} 1212 }) 1213 1214 JustBeforeEach(func() { 1215 err := build.SaveOutput( 1216 dbtest.BaseResourceType, 1217 atc.Source{"some": "source"}, 1218 atc.VersionedResourceTypes{}, 1219 outputVersion, 1220 []db.ResourceConfigMetadataField{ 1221 { 1222 Name: "meta", 1223 Value: "data", 1224 }, 1225 }, 1226 "output-name", 1227 "some-resource", 1228 ) 1229 Expect(err).ToNot(HaveOccurred()) 1230 }) 1231 1232 AfterEach(func() { 1233 atc.EnableGlobalResources = false 1234 }) 1235 1236 It("should set the resource's config scope", func() { 1237 resource, found, err := scenario.Pipeline.Resource("some-resource") 1238 Expect(err).ToNot(HaveOccurred()) 1239 Expect(found).To(BeTrue()) 1240 Expect(resource.ResourceConfigScopeID()).ToNot(BeZero()) 1241 }) 1242 1243 Context("when the version does not exist", func() { 1244 It("can save a build's output", func() { 1245 rcv := scenario.ResourceVersion("some-resource", outputVersion) 1246 Expect(rcv.Version()).To(Equal(db.Version(outputVersion))) 1247 1248 _, buildOutputs, err := build.Resources() 1249 Expect(err).ToNot(HaveOccurred()) 1250 Expect(len(buildOutputs)).To(Equal(1)) 1251 Expect(buildOutputs[0].Name).To(Equal("output-name")) 1252 Expect(buildOutputs[0].Version).To(Equal(outputVersion)) 1253 }) 1254 1255 Context("with a job in a separate team downstream of the same resource config", func() { 1256 var otherScenario *dbtest.Scenario 1257 1258 var beforeTime, otherJobBeforeTime time.Time 1259 var otherTeamBeforeTime, otherTeamOtherJobBeforeTime time.Time 1260 1261 BeforeEach(func() { 1262 otherScenario = dbtest.Setup( 1263 builder.WithTeam("other-team"), 1264 builder.WithPipeline(pipelineConfig), 1265 builder.WithResourceVersions("some-resource", atc.Version{"some": "version"}), 1266 builder.WithResourceVersions("some-other-resource", atc.Version{"some": "other-version"}), 1267 ) 1268 1269 beforeTime = scenario.Job("some-job").ScheduleRequestedTime() 1270 otherTeamBeforeTime = otherScenario.Job("some-job").ScheduleRequestedTime() 1271 1272 otherJobBeforeTime = scenario.Job("some-other-job").ScheduleRequestedTime() 1273 otherTeamOtherJobBeforeTime = otherScenario.Job("some-other-job").ScheduleRequestedTime() 1274 }) 1275 1276 It("requests schedule on jobs which use the same config", func() { 1277 Expect(scenario.Job("some-job").ScheduleRequestedTime()).To(BeTemporally(">", beforeTime)) 1278 Expect(otherScenario.Job("some-job").ScheduleRequestedTime()).To(BeTemporally(">", otherTeamBeforeTime)) 1279 1280 Expect(scenario.Job("some-other-job").ScheduleRequestedTime()).To(BeTemporally("==", otherJobBeforeTime)) 1281 Expect(otherScenario.Job("some-other-job").ScheduleRequestedTime()).To(BeTemporally("==", otherTeamOtherJobBeforeTime)) 1282 }) 1283 }) 1284 }) 1285 1286 Context("when the version already exists", func() { 1287 var rcv db.ResourceConfigVersion 1288 1289 BeforeEach(func() { 1290 scenario.Run( 1291 builder.WithResourceVersions("some-resource", outputVersion), 1292 ) 1293 1294 rcv = scenario.ResourceVersion("some-resource", outputVersion) 1295 }) 1296 1297 It("does not increment the check order", func() { 1298 newRCV := scenario.ResourceVersion("some-resource", outputVersion) 1299 Expect(newRCV.CheckOrder()).To(Equal(rcv.CheckOrder())) 1300 }) 1301 1302 Context("with a job in a separate team downstream of the same resource config", func() { 1303 var otherScenario *dbtest.Scenario 1304 1305 var beforeTime, otherJobBeforeTime time.Time 1306 var otherTeamBeforeTime, otherTeamOtherJobBeforeTime time.Time 1307 1308 BeforeEach(func() { 1309 otherScenario = dbtest.Setup( 1310 builder.WithTeam("other-team"), 1311 builder.WithPipeline(pipelineConfig), 1312 builder.WithResourceVersions("some-resource", atc.Version{"some": "version"}), 1313 builder.WithResourceVersions("some-other-resource", atc.Version{"some": "other-version"}), 1314 ) 1315 1316 beforeTime = scenario.Job("some-job").ScheduleRequestedTime() 1317 otherTeamBeforeTime = otherScenario.Job("some-job").ScheduleRequestedTime() 1318 1319 otherJobBeforeTime = scenario.Job("some-other-job").ScheduleRequestedTime() 1320 otherTeamOtherJobBeforeTime = otherScenario.Job("some-other-job").ScheduleRequestedTime() 1321 }) 1322 1323 It("does not request schedule on jobs which use the same config", func() { 1324 Expect(scenario.Job("some-job").ScheduleRequestedTime()).To(BeTemporally("==", beforeTime)) 1325 Expect(otherScenario.Job("some-job").ScheduleRequestedTime()).To(BeTemporally("==", otherTeamBeforeTime)) 1326 1327 Expect(scenario.Job("some-other-job").ScheduleRequestedTime()).To(BeTemporally("==", otherJobBeforeTime)) 1328 Expect(otherScenario.Job("some-other-job").ScheduleRequestedTime()).To(BeTemporally("==", otherTeamOtherJobBeforeTime)) 1329 }) 1330 }) 1331 }) 1332 }) 1333 1334 Describe("Resources", func() { 1335 var ( 1336 scenario *dbtest.Scenario 1337 build db.Build 1338 inputResource db.Resource 1339 ) 1340 1341 BeforeEach(func() { 1342 pipelineConfig := atc.Config{ 1343 Jobs: atc.JobConfigs{ 1344 { 1345 Name: "some-job", 1346 PlanSequence: []atc.Step{ 1347 { 1348 Config: &atc.GetStep{ 1349 Name: "some-input", 1350 Resource: "some-resource", 1351 }, 1352 }, 1353 { 1354 Config: &atc.PutStep{ 1355 Name: "some-resource", 1356 }, 1357 }, 1358 { 1359 Config: &atc.PutStep{ 1360 Name: "some-other-resource", 1361 }, 1362 }, 1363 }, 1364 }, 1365 }, 1366 Resources: atc.ResourceConfigs{ 1367 { 1368 Name: "some-resource", 1369 Type: dbtest.BaseResourceType, 1370 Source: atc.Source{"some": "source-1"}, 1371 }, 1372 { 1373 Name: "some-other-resource", 1374 Type: dbtest.BaseResourceType, 1375 Source: atc.Source{"some": "source-2"}, 1376 }, 1377 { 1378 Name: "some-unused-resource", 1379 Type: dbtest.BaseResourceType, 1380 Source: atc.Source{"some": "source-3"}, 1381 }, 1382 }, 1383 } 1384 1385 scenario = dbtest.Setup( 1386 builder.WithPipeline(pipelineConfig), 1387 builder.WithResourceVersions( 1388 "some-resource", 1389 atc.Version{"ver": "1"}, 1390 atc.Version{"ver": "2"}, 1391 ), 1392 builder.WithJobBuild(&build, "some-job", dbtest.JobInputs{ 1393 { 1394 Name: "some-input", 1395 Version: atc.Version{"ver": "1"}, 1396 FirstOccurrence: true, 1397 }, 1398 }, dbtest.JobOutputs{ 1399 "some-resource": atc.Version{"ver": "2"}, 1400 "some-other-resource": atc.Version{"ver": "not-checked"}, 1401 }), 1402 ) 1403 1404 inputResource = scenario.Resource("some-resource") 1405 }) 1406 1407 It("returns build inputs and outputs", func() { 1408 inputs, outputs, err := build.Resources() 1409 Expect(err).NotTo(HaveOccurred()) 1410 1411 Expect(inputs).To(ConsistOf([]db.BuildInput{ 1412 { 1413 Name: "some-input", 1414 Version: atc.Version{"ver": "1"}, 1415 ResourceID: inputResource.ID(), 1416 FirstOccurrence: true, 1417 }, 1418 })) 1419 1420 Expect(outputs).To(ConsistOf([]db.BuildOutput{ 1421 { 1422 Name: "some-resource", 1423 Version: atc.Version{"ver": "2"}, 1424 }, 1425 { 1426 Name: "some-other-resource", 1427 Version: atc.Version{"ver": "not-checked"}, 1428 }, 1429 })) 1430 }) 1431 1432 Context("when the first occurrence is empty", func() { 1433 BeforeEach(func() { 1434 res, err := psql.Update("build_resource_config_version_inputs"). 1435 Set("first_occurrence", nil). 1436 Where(sq.Eq{ 1437 "build_id": build.ID(), 1438 "resource_id": inputResource.ID(), 1439 "version_md5": convertToMD5(atc.Version{"ver": "1"}), 1440 }). 1441 RunWith(dbConn). 1442 Exec() 1443 Expect(err).NotTo(HaveOccurred()) 1444 rows, err := res.RowsAffected() 1445 Expect(err).NotTo(HaveOccurred()) 1446 Expect(rows).To(Equal(int64(1))) 1447 }) 1448 1449 It("determines the first occurrence to be true", func() { 1450 inputs, _, err := build.Resources() 1451 Expect(err).NotTo(HaveOccurred()) 1452 Expect(inputs).To(ConsistOf([]db.BuildInput{ 1453 { 1454 Name: "some-input", 1455 Version: atc.Version{"ver": "1"}, 1456 ResourceID: inputResource.ID(), 1457 FirstOccurrence: true, 1458 }, 1459 })) 1460 }) 1461 1462 Context("when the a build with those inputs already exist", func() { 1463 var newBuild db.Build 1464 1465 BeforeEach(func() { 1466 scenario.Run( 1467 builder.WithJobBuild(&newBuild, "some-job", dbtest.JobInputs{ 1468 { 1469 Name: "some-input", 1470 Version: atc.Version{"ver": "1"}, 1471 FirstOccurrence: true, 1472 }, 1473 }, dbtest.JobOutputs{ 1474 "some-resource": atc.Version{"ver": "2"}, 1475 "some-other-resource": atc.Version{"ver": "not-checked"}, 1476 }), 1477 ) 1478 1479 res, err := psql.Update("build_resource_config_version_inputs"). 1480 Set("first_occurrence", nil). 1481 Where(sq.Eq{ 1482 "build_id": newBuild.ID(), 1483 "resource_id": inputResource.ID(), 1484 "version_md5": convertToMD5(atc.Version{"ver": "1"}), 1485 }). 1486 RunWith(dbConn). 1487 Exec() 1488 Expect(err).NotTo(HaveOccurred()) 1489 1490 rows, err := res.RowsAffected() 1491 Expect(err).NotTo(HaveOccurred()) 1492 Expect(rows).To(Equal(int64(1))) 1493 }) 1494 1495 It("determines the first occurrence to be false", func() { 1496 inputs, _, err := newBuild.Resources() 1497 Expect(err).NotTo(HaveOccurred()) 1498 Expect(inputs).To(ConsistOf([]db.BuildInput{ 1499 { 1500 Name: "some-input", 1501 Version: atc.Version{"ver": "1"}, 1502 ResourceID: inputResource.ID(), 1503 FirstOccurrence: false, 1504 }, 1505 })) 1506 }) 1507 }) 1508 }) 1509 }) 1510 1511 Describe("Pipeline", func() { 1512 var ( 1513 build db.Build 1514 foundPipeline db.Pipeline 1515 createdPipeline db.Pipeline 1516 found bool 1517 ) 1518 1519 JustBeforeEach(func() { 1520 var err error 1521 foundPipeline, found, err = build.Pipeline() 1522 Expect(err).ToNot(HaveOccurred()) 1523 }) 1524 1525 Context("when a job build", func() { 1526 BeforeEach(func() { 1527 var err error 1528 createdPipeline, _, err = team.SavePipeline(atc.PipelineRef{Name: "some-pipeline"}, atc.Config{ 1529 Jobs: atc.JobConfigs{ 1530 { 1531 Name: "some-job", 1532 }, 1533 }, 1534 }, db.ConfigVersion(1), false) 1535 Expect(err).ToNot(HaveOccurred()) 1536 1537 job, found, err := createdPipeline.Job("some-job") 1538 Expect(err).ToNot(HaveOccurred()) 1539 Expect(found).To(BeTrue()) 1540 1541 build, err = job.CreateBuild() 1542 Expect(err).ToNot(HaveOccurred()) 1543 }) 1544 1545 It("returns the correct pipeline", func() { 1546 Expect(found).To(BeTrue()) 1547 Expect(foundPipeline.Name()).To(Equal(createdPipeline.Name())) 1548 }) 1549 }) 1550 }) 1551 1552 Describe("Preparation", func() { 1553 var ( 1554 scenario *dbtest.Scenario 1555 job db.Job 1556 1557 expectedBuildPrep db.BuildPreparation 1558 ) 1559 1560 BeforeEach(func() { 1561 scenario = dbtest.Setup( 1562 builder.WithPipeline(atc.Config{ 1563 Resources: atc.ResourceConfigs{ 1564 { 1565 Name: "some-resource", 1566 Type: dbtest.BaseResourceType, 1567 Source: atc.Source{ 1568 "source-config": "some-value", 1569 }, 1570 }, 1571 }, 1572 Jobs: atc.JobConfigs{ 1573 { 1574 Name: "some-job", 1575 RawMaxInFlight: 1, 1576 PlanSequence: []atc.Step{ 1577 { 1578 Config: &atc.GetStep{ 1579 Name: "some-input", 1580 Resource: "some-resource", 1581 }, 1582 }, 1583 }, 1584 }, 1585 }, 1586 }), 1587 builder.WithResourceVersions("some-resource", atc.Version{"version": "some-version"}), 1588 builder.WithPendingJobBuild(&build, "some-job"), 1589 ) 1590 1591 job = scenario.Job("some-job") 1592 1593 expectedBuildPrep = db.BuildPreparation{ 1594 BuildID: build.ID(), 1595 PausedPipeline: db.BuildPreparationStatusNotBlocking, 1596 PausedJob: db.BuildPreparationStatusNotBlocking, 1597 MaxRunningBuilds: db.BuildPreparationStatusNotBlocking, 1598 Inputs: map[string]db.BuildPreparationStatus{}, 1599 InputsSatisfied: db.BuildPreparationStatusNotBlocking, 1600 MissingInputReasons: db.MissingInputReasons{}, 1601 } 1602 }) 1603 1604 Context("when inputs are satisfied", func() { 1605 BeforeEach(func() { 1606 scenario.Run( 1607 builder.WithNextInputMapping("some-job", dbtest.JobInputs{ 1608 { 1609 Name: "some-input", 1610 Version: atc.Version{"version": "some-version"}, 1611 }, 1612 }), 1613 ) 1614 }) 1615 1616 Context("when resource check finished after build created", func() { 1617 BeforeEach(func() { 1618 scenario.Run( 1619 // don't save any versions, just bump the last check timestamp 1620 builder.WithResourceVersions("some-resource"), 1621 ) 1622 1623 expectedBuildPrep.Inputs = map[string]db.BuildPreparationStatus{ 1624 "some-input": db.BuildPreparationStatusNotBlocking, 1625 } 1626 }) 1627 1628 Context("when the build is started", func() { 1629 BeforeEach(func() { 1630 started, err := build.Start(atc.Plan{}) 1631 Expect(started).To(BeTrue()) 1632 Expect(err).NotTo(HaveOccurred()) 1633 1634 stillExists, err := build.Reload() 1635 Expect(stillExists).To(BeTrue()) 1636 Expect(err).NotTo(HaveOccurred()) 1637 1638 expectedBuildPrep.Inputs = map[string]db.BuildPreparationStatus{} 1639 }) 1640 1641 It("returns build preparation", func() { 1642 buildPrep, found, err := build.Preparation() 1643 Expect(err).NotTo(HaveOccurred()) 1644 Expect(found).To(BeTrue()) 1645 Expect(buildPrep).To(Equal(expectedBuildPrep)) 1646 }) 1647 }) 1648 1649 Context("when pipeline is paused", func() { 1650 BeforeEach(func() { 1651 err := scenario.Pipeline.Pause() 1652 Expect(err).NotTo(HaveOccurred()) 1653 1654 expectedBuildPrep.PausedPipeline = db.BuildPreparationStatusBlocking 1655 }) 1656 1657 It("returns build preparation with paused pipeline", func() { 1658 buildPrep, found, err := build.Preparation() 1659 Expect(err).NotTo(HaveOccurred()) 1660 Expect(found).To(BeTrue()) 1661 Expect(buildPrep).To(Equal(expectedBuildPrep)) 1662 }) 1663 }) 1664 1665 Context("when job is paused", func() { 1666 BeforeEach(func() { 1667 err := scenario.Job("some-job").Pause() 1668 Expect(err).NotTo(HaveOccurred()) 1669 1670 expectedBuildPrep.PausedJob = db.BuildPreparationStatusBlocking 1671 }) 1672 1673 It("returns build preparation with paused pipeline", func() { 1674 buildPrep, found, err := build.Preparation() 1675 Expect(err).NotTo(HaveOccurred()) 1676 Expect(found).To(BeTrue()) 1677 Expect(buildPrep).To(Equal(expectedBuildPrep)) 1678 }) 1679 }) 1680 1681 Context("when max running builds is reached", func() { 1682 var secondBuild db.Build 1683 1684 BeforeEach(func() { 1685 scenario.Run( 1686 builder.WithPendingJobBuild(&secondBuild, "some-job"), 1687 // don't save any versions, just bump the last check timestamp 1688 builder.WithResourceVersions("some-resource"), 1689 ) 1690 1691 scheduled, err := job.ScheduleBuild(build) 1692 Expect(err).ToNot(HaveOccurred()) 1693 Expect(scheduled).To(BeTrue()) 1694 1695 scheduled, err = job.ScheduleBuild(secondBuild) 1696 Expect(err).ToNot(HaveOccurred()) 1697 Expect(scheduled).To(BeFalse()) 1698 1699 expectedBuildPrep.BuildID = secondBuild.ID() 1700 expectedBuildPrep.MaxRunningBuilds = db.BuildPreparationStatusBlocking 1701 }) 1702 1703 It("returns build preparation with max in flight reached", func() { 1704 buildPrep, found, err := secondBuild.Preparation() 1705 Expect(err).NotTo(HaveOccurred()) 1706 Expect(found).To(BeTrue()) 1707 Expect(buildPrep).To(Equal(expectedBuildPrep)) 1708 }) 1709 1710 Context("when max running builds is de-reached", func() { 1711 BeforeEach(func() { 1712 err := build.Finish(db.BuildStatusSucceeded) 1713 Expect(err).NotTo(HaveOccurred()) 1714 1715 scheduled, err := job.ScheduleBuild(secondBuild) 1716 Expect(err).ToNot(HaveOccurred()) 1717 Expect(scheduled).To(BeTrue()) 1718 1719 expectedBuildPrep.MaxRunningBuilds = db.BuildPreparationStatusNotBlocking 1720 }) 1721 1722 It("returns build preparation with max in flight not reached", func() { 1723 buildPrep, found, err := secondBuild.Preparation() 1724 Expect(err).NotTo(HaveOccurred()) 1725 Expect(found).To(BeTrue()) 1726 Expect(buildPrep).To(Equal(expectedBuildPrep)) 1727 }) 1728 }) 1729 }) 1730 }) 1731 1732 Context("when no resource check finished after build created", func() { 1733 BeforeEach(func() { 1734 expectedBuildPrep.Inputs = map[string]db.BuildPreparationStatus{ 1735 "some-input": db.BuildPreparationStatusBlocking, 1736 } 1737 expectedBuildPrep.InputsSatisfied = db.BuildPreparationStatusBlocking 1738 expectedBuildPrep.MissingInputReasons = db.MissingInputReasons{ 1739 "some-input": db.NoResourceCheckFinished, 1740 } 1741 }) 1742 1743 It("returns build preparation with missing input reason", func() { 1744 buildPrep, found, err := build.Preparation() 1745 Expect(err).NotTo(HaveOccurred()) 1746 Expect(found).To(BeTrue()) 1747 Expect(buildPrep).To(Equal(expectedBuildPrep)) 1748 }) 1749 }) 1750 }) 1751 1752 Context("when inputs are not satisfied", func() { 1753 BeforeEach(func() { 1754 expectedBuildPrep.InputsSatisfied = db.BuildPreparationStatusBlocking 1755 expectedBuildPrep.MissingInputReasons = map[string]string{"some-input": db.MissingBuildInput} 1756 expectedBuildPrep.Inputs = map[string]db.BuildPreparationStatus{"some-input": db.BuildPreparationStatusBlocking} 1757 }) 1758 1759 It("returns blocking inputs satisfied", func() { 1760 buildPrep, found, err := build.Preparation() 1761 Expect(err).NotTo(HaveOccurred()) 1762 Expect(found).To(BeTrue()) 1763 Expect(buildPrep).To(Equal(expectedBuildPrep)) 1764 }) 1765 }) 1766 1767 Context("when some inputs are errored", func() { 1768 BeforeEach(func() { 1769 scenario.Run( 1770 builder.WithNextInputMapping("some-job", dbtest.JobInputs{ 1771 { 1772 Name: "some-input", 1773 ResolveError: "resolve error", 1774 }, 1775 }), 1776 ) 1777 1778 expectedBuildPrep.Inputs = map[string]db.BuildPreparationStatus{ 1779 "some-input": db.BuildPreparationStatusBlocking, 1780 } 1781 expectedBuildPrep.InputsSatisfied = db.BuildPreparationStatusBlocking 1782 expectedBuildPrep.MissingInputReasons = db.MissingInputReasons{ 1783 "some-input": "resolve error", 1784 } 1785 }) 1786 1787 It("returns blocking inputs satisfied", func() { 1788 buildPrep, found, err := build.Preparation() 1789 Expect(err).NotTo(HaveOccurred()) 1790 Expect(found).To(BeTrue()) 1791 Expect(buildPrep).To(Equal(expectedBuildPrep)) 1792 }) 1793 }) 1794 1795 Context("when some inputs are missing", func() { 1796 BeforeEach(func() { 1797 scenario.Run( 1798 builder.WithNextInputMapping("some-job", dbtest.JobInputs{ 1799 { 1800 Name: "some-input", 1801 Version: atc.Version{"some": "version"}, 1802 }, 1803 }), 1804 1805 // checked after build creation 1806 builder.WithResourceVersions("some-resource"), 1807 1808 // add another input 1809 builder.WithPipeline(atc.Config{ 1810 Resources: atc.ResourceConfigs{ 1811 { 1812 Name: "some-resource", 1813 Type: dbtest.BaseResourceType, 1814 Source: atc.Source{ 1815 "source-config": "some-value", 1816 }, 1817 }, 1818 }, 1819 Jobs: atc.JobConfigs{ 1820 { 1821 Name: "some-job", 1822 RawMaxInFlight: 1, 1823 PlanSequence: []atc.Step{ 1824 { 1825 Config: &atc.GetStep{ 1826 Name: "some-input", 1827 Resource: "some-resource", 1828 }, 1829 }, 1830 { 1831 Config: &atc.GetStep{ 1832 Name: "some-other-input", 1833 Resource: "some-resource", 1834 }, 1835 }, 1836 }, 1837 }, 1838 }, 1839 }), 1840 ) 1841 1842 expectedBuildPrep.Inputs = map[string]db.BuildPreparationStatus{ 1843 "some-input": db.BuildPreparationStatusNotBlocking, 1844 "some-other-input": db.BuildPreparationStatusBlocking, 1845 } 1846 expectedBuildPrep.InputsSatisfied = db.BuildPreparationStatusBlocking 1847 expectedBuildPrep.MissingInputReasons = db.MissingInputReasons{ 1848 "some-other-input": "input is not included in resolved candidates", 1849 } 1850 }) 1851 1852 It("returns blocking inputs satisfied", func() { 1853 buildPrep, found, err := build.Preparation() 1854 Expect(err).NotTo(HaveOccurred()) 1855 Expect(found).To(BeTrue()) 1856 Expect(buildPrep).To(Equal(expectedBuildPrep)) 1857 }) 1858 }) 1859 }) 1860 1861 Describe("AdoptInputsAndPipes", func() { 1862 var scenario *dbtest.Scenario 1863 1864 BeforeEach(func() { 1865 scenario = dbtest.Setup( 1866 builder.WithPipeline(atc.Config{ 1867 Jobs: atc.JobConfigs{ 1868 { 1869 Name: "upstream-job", 1870 PlanSequence: []atc.Step{ 1871 { 1872 Config: &atc.GetStep{ 1873 Name: "some-input", 1874 Resource: "some-resource", 1875 }, 1876 }, 1877 }, 1878 }, 1879 { 1880 Name: "downstream-job", 1881 PlanSequence: []atc.Step{ 1882 { 1883 Config: &atc.GetStep{ 1884 Name: "some-input", 1885 Resource: "some-resource", 1886 Passed: []string{"upstream-job"}, 1887 }, 1888 }, 1889 { 1890 Config: &atc.GetStep{ 1891 Name: "some-other-input", 1892 Resource: "some-other-resource", 1893 }, 1894 }, 1895 }, 1896 }, 1897 }, 1898 Resources: atc.ResourceConfigs{ 1899 { 1900 Name: "some-resource", 1901 Type: dbtest.BaseResourceType, 1902 Source: atc.Source{"some": "source"}, 1903 }, 1904 { 1905 Name: "some-other-resource", 1906 Type: dbtest.BaseResourceType, 1907 Source: atc.Source{"some": "other-source"}, 1908 }, 1909 }, 1910 }), 1911 builder.WithResourceVersions("some-resource", 1912 atc.Version{"version": "v1"}, 1913 atc.Version{"version": "v2"}, 1914 atc.Version{"version": "v3"}, 1915 ), 1916 builder.WithResourceVersions("some-other-resource", 1917 atc.Version{"version": "v1"}, 1918 ), 1919 ) 1920 }) 1921 1922 Describe("adopting inputs for an upstream job", func() { 1923 var upstreamBuild db.Build 1924 1925 BeforeEach(func() { 1926 scenario.Run( 1927 builder.WithPendingJobBuild(&upstreamBuild, "upstream-job"), 1928 ) 1929 }) 1930 1931 Context("for valid versions", func() { 1932 BeforeEach(func() { 1933 scenario.Run( 1934 builder.WithNextInputMapping("upstream-job", dbtest.JobInputs{ 1935 { 1936 Name: "some-input", 1937 Version: atc.Version{"version": "v3"}, 1938 }, 1939 }), 1940 ) 1941 }) 1942 1943 It("adopts the next inputs and pipes", func() { 1944 inputs, adopted, err := upstreamBuild.AdoptInputsAndPipes() 1945 Expect(err).ToNot(HaveOccurred()) 1946 Expect(adopted).To(BeTrue()) 1947 Expect(inputs).To(ConsistOf([]db.BuildInput{ 1948 { 1949 Name: "some-input", 1950 ResourceID: scenario.Resource("some-resource").ID(), 1951 Version: atc.Version{"version": "v3"}, 1952 FirstOccurrence: false, 1953 }, 1954 })) 1955 1956 Expect(upstreamBuild.InputsReady()).To(BeFalse()) 1957 found, err := upstreamBuild.Reload() 1958 Expect(err).ToNot(HaveOccurred()) 1959 Expect(found).To(BeTrue()) 1960 Expect(upstreamBuild.InputsReady()).To(BeTrue()) 1961 }) 1962 1963 Context("followed by a downstream job", func() { 1964 var downstreamBuild db.Build 1965 1966 BeforeEach(func() { 1967 _, adopted, err := upstreamBuild.AdoptInputsAndPipes() 1968 Expect(err).ToNot(HaveOccurred()) 1969 Expect(adopted).To(BeTrue()) 1970 1971 Expect(upstreamBuild.Finish(db.BuildStatusSucceeded)).To(Succeed()) 1972 1973 scenario.Run( 1974 builder.WithPendingJobBuild(&downstreamBuild, "downstream-job"), 1975 builder.WithNextInputMapping("downstream-job", dbtest.JobInputs{ 1976 { 1977 Name: "some-input", 1978 Version: atc.Version{"version": "v3"}, 1979 PassedBuilds: []db.Build{upstreamBuild}, 1980 }, 1981 { 1982 Name: "some-other-input", 1983 Version: atc.Version{"version": "v1"}, 1984 FirstOccurrence: true, 1985 }, 1986 }), 1987 ) 1988 }) 1989 1990 It("adopts the next inputs and pipes referencing the upstream build", func() { 1991 inputs, adopted, err := downstreamBuild.AdoptInputsAndPipes() 1992 Expect(err).ToNot(HaveOccurred()) 1993 Expect(adopted).To(BeTrue()) 1994 Expect(inputs).To(ConsistOf([]db.BuildInput{ 1995 { 1996 Name: "some-input", 1997 ResourceID: scenario.Resource("some-resource").ID(), 1998 Version: atc.Version{"version": "v3"}, 1999 FirstOccurrence: false, 2000 }, 2001 { 2002 Name: "some-other-input", 2003 ResourceID: scenario.Resource("some-other-resource").ID(), 2004 Version: atc.Version{"version": "v1"}, 2005 FirstOccurrence: true, 2006 }, 2007 })) 2008 2009 Expect(downstreamBuild.InputsReady()).To(BeFalse()) 2010 found, err := downstreamBuild.Reload() 2011 Expect(err).ToNot(HaveOccurred()) 2012 Expect(found).To(BeTrue()) 2013 Expect(downstreamBuild.InputsReady()).To(BeTrue()) 2014 2015 buildPipes, err := versionsDB.LatestBuildPipes(ctx, downstreamBuild.ID()) 2016 Expect(err).ToNot(HaveOccurred()) 2017 Expect(buildPipes[scenario.Job("upstream-job").ID()]).To(Equal(db.BuildCursor{ 2018 ID: upstreamBuild.ID(), 2019 })) 2020 }) 2021 }) 2022 }) 2023 2024 Context("for bogus versions", func() { 2025 BeforeEach(func() { 2026 scenario.Run( 2027 builder.WithNextInputMapping("upstream-job", dbtest.JobInputs{ 2028 { 2029 Name: "some-input", 2030 Version: atc.Version{"version": "bogus"}, 2031 }, 2032 }), 2033 ) 2034 }) 2035 2036 It("set resolve error of that input", func() { 2037 buildInputs, adopted, err := upstreamBuild.AdoptInputsAndPipes() 2038 Expect(err).ToNot(HaveOccurred()) 2039 Expect(adopted).To(BeFalse()) 2040 Expect(buildInputs).To(BeNil()) 2041 2042 nextBuildInputs, err := scenario.Job("upstream-job").GetNextBuildInputs() 2043 Expect(err).ToNot(HaveOccurred()) 2044 Expect(len(nextBuildInputs)).To(Equal(1)) 2045 Expect(nextBuildInputs[0].ResolveError).To(Equal("chosen version of input some-input not available")) 2046 }) 2047 }) 2048 2049 Context("when inputs are not determined", func() { 2050 BeforeEach(func() { 2051 scenario.Run( 2052 builder.WithNextInputMapping("upstream-job", dbtest.JobInputs{ 2053 { 2054 Name: "some-input", 2055 Version: atc.Version{"version": "bogus"}, 2056 ResolveError: "errored", 2057 }, 2058 }), 2059 ) 2060 }) 2061 2062 It("does not adopt next inputs and pipes", func() { 2063 buildInputs, adopted, err := upstreamBuild.AdoptInputsAndPipes() 2064 Expect(err).ToNot(HaveOccurred()) 2065 Expect(adopted).To(BeFalse()) 2066 Expect(buildInputs).To(BeNil()) 2067 2068 buildPipes, err := versionsDB.LatestBuildPipes(ctx, upstreamBuild.ID()) 2069 Expect(err).ToNot(HaveOccurred()) 2070 Expect(buildPipes).To(HaveLen(0)) 2071 2072 Expect(upstreamBuild.InputsReady()).To(BeFalse()) 2073 found, err := upstreamBuild.Reload() 2074 Expect(err).ToNot(HaveOccurred()) 2075 Expect(found).To(BeTrue()) 2076 Expect(upstreamBuild.InputsReady()).To(BeFalse()) 2077 }) 2078 }) 2079 }) 2080 }) 2081 2082 Describe("AdoptRerunInputsAndPipes", func() { 2083 var scenario *dbtest.Scenario 2084 var pipelineConfig atc.Config 2085 2086 var upstreamBuild, downstreamBuild, retriggerBuild db.Build 2087 var buildInputs, expectedBuildInputs []db.BuildInput 2088 var adoptFound bool 2089 2090 BeforeEach(func() { 2091 pipelineConfig = atc.Config{ 2092 Jobs: atc.JobConfigs{ 2093 { 2094 Name: "upstream-job", 2095 PlanSequence: []atc.Step{ 2096 { 2097 Config: &atc.GetStep{ 2098 Name: "some-input", 2099 Resource: "some-resource", 2100 }, 2101 }, 2102 }, 2103 }, 2104 { 2105 Name: "downstream-job", 2106 PlanSequence: []atc.Step{ 2107 { 2108 Config: &atc.GetStep{ 2109 Name: "some-input", 2110 Resource: "some-resource", 2111 Passed: []string{"upstream-job"}, 2112 }, 2113 }, 2114 { 2115 Config: &atc.GetStep{ 2116 Name: "some-other-input", 2117 Resource: "some-other-resource", 2118 }, 2119 }, 2120 }, 2121 }, 2122 }, 2123 Resources: atc.ResourceConfigs{ 2124 { 2125 Name: "some-resource", 2126 Type: dbtest.BaseResourceType, 2127 Source: atc.Source{"some": "source"}, 2128 }, 2129 { 2130 Name: "some-other-resource", 2131 Type: dbtest.BaseResourceType, 2132 Source: atc.Source{"some": "other-source"}, 2133 }, 2134 }, 2135 } 2136 2137 scenario = dbtest.Setup( 2138 builder.WithPipeline(pipelineConfig), 2139 builder.WithResourceVersions("some-resource", 2140 atc.Version{"version": "v1"}, 2141 atc.Version{"version": "v2"}, 2142 atc.Version{"version": "v3"}, 2143 ), 2144 builder.WithResourceVersions("some-other-resource", 2145 atc.Version{"version": "v1"}, 2146 ), 2147 builder.WithJobBuild(&upstreamBuild, "upstream-job", dbtest.JobInputs{ 2148 { 2149 Name: "some-input", 2150 Version: atc.Version{"version": "v3"}, 2151 }, 2152 }, dbtest.JobOutputs{}), 2153 builder.WithPendingJobBuild(&downstreamBuild, "downstream-job"), 2154 ) 2155 2156 var err error 2157 retriggerBuild, err = job.RerunBuild(downstreamBuild) 2158 Expect(err).ToNot(HaveOccurred()) 2159 }) 2160 2161 JustBeforeEach(func() { 2162 var err error 2163 buildInputs, adoptFound, err = retriggerBuild.AdoptRerunInputsAndPipes() 2164 Expect(err).ToNot(HaveOccurred()) 2165 }) 2166 2167 Context("when the build to retrigger has inputs and pipes", func() { 2168 BeforeEach(func() { 2169 scenario.Run( 2170 builder.WithNextInputMapping("downstream-job", dbtest.JobInputs{ 2171 { 2172 Name: "some-input", 2173 Version: atc.Version{"version": "v3"}, 2174 PassedBuilds: []db.Build{upstreamBuild}, 2175 FirstOccurrence: true, 2176 }, 2177 { 2178 Name: "some-other-input", 2179 Version: atc.Version{"version": "v1"}, 2180 FirstOccurrence: true, 2181 }, 2182 }), 2183 ) 2184 2185 _, found, err := downstreamBuild.AdoptInputsAndPipes() 2186 Expect(err).ToNot(HaveOccurred()) 2187 Expect(found).To(BeTrue()) 2188 2189 expectedBuildInputs = []db.BuildInput{ 2190 { 2191 Name: "some-input", 2192 ResourceID: scenario.Resource("some-resource").ID(), 2193 Version: atc.Version{"version": "v3"}, 2194 FirstOccurrence: false, 2195 }, 2196 { 2197 Name: "some-other-input", 2198 ResourceID: scenario.Resource("some-other-resource").ID(), 2199 Version: atc.Version{"version": "v1"}, 2200 FirstOccurrence: false, 2201 }, 2202 } 2203 }) 2204 2205 It("build inputs and pipes of the build to retrigger off of as it's own build inputs but sets first occurrence to false", func() { 2206 Expect(adoptFound).To(BeTrue()) 2207 2208 Expect(buildInputs).To(ConsistOf(expectedBuildInputs)) 2209 2210 buildPipes, err := versionsDB.LatestBuildPipes(ctx, retriggerBuild.ID()) 2211 Expect(err).ToNot(HaveOccurred()) 2212 Expect(buildPipes).To(HaveLen(1)) 2213 Expect(buildPipes[scenario.Job("upstream-job").ID()]).To(Equal(db.BuildCursor{ 2214 ID: upstreamBuild.ID(), 2215 })) 2216 2217 reloaded, err := retriggerBuild.Reload() 2218 Expect(err).ToNot(HaveOccurred()) 2219 Expect(reloaded).To(BeTrue()) 2220 Expect(retriggerBuild.InputsReady()).To(BeTrue()) 2221 }) 2222 2223 Context("when the version becomes unavailable", func() { 2224 BeforeEach(func() { 2225 pipelineConfig.Resources[0].Source = atc.Source{"some": "new-source"} 2226 2227 scenario.Run( 2228 builder.WithPipeline(pipelineConfig), 2229 builder.WithResourceVersions("some-resource", atc.Version{"some": "new-version"}), 2230 ) 2231 }) 2232 2233 It("fails to adopt", func() { 2234 Expect(adoptFound).To(BeFalse()) 2235 }) 2236 2237 It("aborts the build", func() { 2238 reloaded, err := retriggerBuild.Reload() 2239 Expect(err).ToNot(HaveOccurred()) 2240 Expect(reloaded).To(BeTrue()) 2241 Expect(retriggerBuild.IsAborted()).To(BeTrue()) 2242 }) 2243 }) 2244 }) 2245 2246 Context("when the build to retrigger off of does not have inputs or pipes", func() { 2247 It("does not move build inputs and pipes", func() { 2248 Expect(adoptFound).To(BeFalse()) 2249 2250 Expect(buildInputs).To(BeNil()) 2251 2252 buildPipes, err := versionsDB.LatestBuildPipes(ctx, build.ID()) 2253 Expect(err).ToNot(HaveOccurred()) 2254 Expect(buildPipes).To(HaveLen(0)) 2255 2256 reloaded, err := retriggerBuild.Reload() 2257 Expect(err).ToNot(HaveOccurred()) 2258 Expect(reloaded).To(BeTrue()) 2259 Expect(retriggerBuild.InputsReady()).To(BeFalse()) 2260 }) 2261 }) 2262 }) 2263 2264 Describe("ResourcesChecked", func() { 2265 var scenario *dbtest.Scenario 2266 2267 var build db.Build 2268 var checked bool 2269 2270 BeforeEach(func() { 2271 pipelineConfig := atc.Config{ 2272 Jobs: atc.JobConfigs{ 2273 { 2274 Name: "some-job", 2275 PlanSequence: []atc.Step{ 2276 { 2277 Config: &atc.GetStep{ 2278 Name: "some-resource", 2279 }, 2280 }, 2281 { 2282 Config: &atc.GetStep{ 2283 Name: "some-other-resource", 2284 }, 2285 }, 2286 }, 2287 }, 2288 }, 2289 Resources: atc.ResourceConfigs{ 2290 { 2291 Name: "some-resource", 2292 Type: dbtest.BaseResourceType, 2293 Source: atc.Source{"some": "source"}, 2294 }, 2295 { 2296 Name: "some-other-resource", 2297 Type: dbtest.BaseResourceType, 2298 Source: atc.Source{"some": "other-source"}, 2299 }, 2300 }, 2301 } 2302 2303 scenario = dbtest.Setup( 2304 builder.WithPipeline(pipelineConfig), 2305 builder.WithResourceVersions("some-resource", atc.Version{"some": "version"}), 2306 builder.WithResourceVersions("some-other-resource", atc.Version{"some": "other-version"}), 2307 builder.WithPendingJobBuild(&build, "some-job"), 2308 ) 2309 }) 2310 2311 JustBeforeEach(func() { 2312 var err error 2313 checked, err = build.ResourcesChecked() 2314 Expect(err).ToNot(HaveOccurred()) 2315 }) 2316 2317 Context("when all the resources in the build has been checked", func() { 2318 BeforeEach(func() { 2319 scenario.Run( 2320 builder.WithResourceVersions("some-resource"), 2321 builder.WithResourceVersions("some-other-resource"), 2322 ) 2323 }) 2324 2325 It("returns true", func() { 2326 Expect(checked).To(BeTrue()) 2327 }) 2328 }) 2329 2330 Context("when a the resource in the build has not been checked", func() { 2331 It("returns false", func() { 2332 Expect(checked).To(BeFalse()) 2333 }) 2334 }) 2335 2336 Context("when a pinned resource in the build has not been checked", func() { 2337 BeforeEach(func() { 2338 scenario.Run( 2339 builder.WithResourceVersions("some-resource"), 2340 ) 2341 2342 rcv := scenario.ResourceVersion("some-other-resource", atc.Version{"some": "other-version"}) 2343 2344 found, err := scenario.Resource("some-other-resource").PinVersion(rcv.ID()) 2345 Expect(err).ToNot(HaveOccurred()) 2346 Expect(found).To(BeTrue()) 2347 }) 2348 2349 It("returns true", func() { 2350 Expect(checked).To(BeTrue()) 2351 }) 2352 }) 2353 }) 2354 2355 Describe("SavePipeline", func() { 2356 It("saves the parent job and build ids", func() { 2357 By("creating a build") 2358 build, err := defaultJob.CreateBuild() 2359 Expect(err).ToNot(HaveOccurred()) 2360 2361 By("saving a pipeline with the build") 2362 pipeline, _, err := build.SavePipeline(atc.PipelineRef{Name: "other-pipeline"}, build.TeamID(), atc.Config{ 2363 Jobs: atc.JobConfigs{ 2364 { 2365 Name: "some-job", 2366 }, 2367 }, 2368 Resources: atc.ResourceConfigs{ 2369 { 2370 Name: "some-resource", 2371 Type: "some-base-resource-type", 2372 Source: atc.Source{ 2373 "some": "source", 2374 }, 2375 }, 2376 }, 2377 ResourceTypes: atc.ResourceTypes{ 2378 { 2379 Name: "some-type", 2380 Type: "some-base-resource-type", 2381 Source: atc.Source{ 2382 "some-type": "source", 2383 }, 2384 }, 2385 }, 2386 }, db.ConfigVersion(0), false) 2387 Expect(err).ToNot(HaveOccurred()) 2388 Expect(pipeline.ParentJobID()).To(Equal(build.JobID())) 2389 Expect(pipeline.ParentBuildID()).To(Equal(build.ID())) 2390 }) 2391 2392 It("only saves the pipeline if it is the latest build", func() { 2393 By("creating two builds") 2394 buildOne, err := defaultJob.CreateBuild() 2395 Expect(err).ToNot(HaveOccurred()) 2396 buildTwo, err := defaultJob.CreateBuild() 2397 Expect(err).ToNot(HaveOccurred()) 2398 2399 By("saving a pipeline with the second build") 2400 pipeline, _, err := buildTwo.SavePipeline(atc.PipelineRef{Name: "other-pipeline"}, buildTwo.TeamID(), atc.Config{ 2401 Jobs: atc.JobConfigs{ 2402 { 2403 Name: "some-job", 2404 }, 2405 }, 2406 Resources: atc.ResourceConfigs{ 2407 { 2408 Name: "some-resource", 2409 Type: "some-base-resource-type", 2410 Source: atc.Source{ 2411 "some": "source", 2412 }, 2413 }, 2414 }, 2415 ResourceTypes: atc.ResourceTypes{ 2416 { 2417 Name: "some-type", 2418 Type: "some-base-resource-type", 2419 Source: atc.Source{ 2420 "some-type": "source", 2421 }, 2422 }, 2423 }, 2424 }, db.ConfigVersion(0), false) 2425 Expect(err).ToNot(HaveOccurred()) 2426 Expect(pipeline.ParentJobID()).To(Equal(buildTwo.JobID())) 2427 Expect(pipeline.ParentBuildID()).To(Equal(buildTwo.ID())) 2428 2429 By("saving a pipeline with the first build") 2430 _, _, err = buildOne.SavePipeline(atc.PipelineRef{Name: "other-pipeline"}, buildOne.TeamID(), atc.Config{ 2431 Jobs: atc.JobConfigs{ 2432 { 2433 Name: "some-job", 2434 }, 2435 }, 2436 Resources: atc.ResourceConfigs{ 2437 { 2438 Name: "some-resource", 2439 Type: "some-base-resource-type", 2440 Source: atc.Source{ 2441 "some": "source", 2442 }, 2443 }, 2444 }, 2445 ResourceTypes: atc.ResourceTypes{ 2446 { 2447 Name: "some-type", 2448 Type: "some-base-resource-type", 2449 Source: atc.Source{ 2450 "some-type": "source", 2451 }, 2452 }, 2453 }, 2454 }, pipeline.ConfigVersion(), false) 2455 Expect(err).To(Equal(db.ErrSetByNewerBuild)) 2456 }) 2457 2458 Context("a pipeline is previously saved by team.SavePipeline", func() { 2459 It("the parent job and build ID are updated", func() { 2460 By("creating a build") 2461 build, err := defaultJob.CreateBuild() 2462 Expect(err).ToNot(HaveOccurred()) 2463 2464 By("re-saving the default pipeline with the build") 2465 pipeline, _, err := build.SavePipeline(defaultPipelineRef, build.TeamID(), defaultPipelineConfig, db.ConfigVersion(1), false) 2466 Expect(err).ToNot(HaveOccurred()) 2467 Expect(pipeline.ParentJobID()).To(Equal(build.JobID())) 2468 Expect(pipeline.ParentBuildID()).To(Equal(build.ID())) 2469 }) 2470 }) 2471 }) 2472 }) 2473 2474 func envelope(ev atc.Event) event.Envelope { 2475 payload, err := json.Marshal(ev) 2476 Expect(err).ToNot(HaveOccurred()) 2477 2478 data := json.RawMessage(payload) 2479 2480 return event.Envelope{ 2481 Event: ev.EventType(), 2482 Version: ev.Version(), 2483 Data: &data, 2484 } 2485 } 2486 2487 func convertToMD5(version atc.Version) string { 2488 versionJSON, err := json.Marshal(version) 2489 Expect(err).ToNot(HaveOccurred()) 2490 2491 hasher := md5.New() 2492 hasher.Write([]byte(versionJSON)) 2493 return hex.EncodeToString(hasher.Sum(nil)) 2494 } 2495 2496 func getJobBuildID(col string, jobID int) int { 2497 var result int 2498 2499 err := psql.Select(col). 2500 From("jobs"). 2501 Where(sq.Eq{"id": jobID}). 2502 RunWith(dbConn). 2503 QueryRow(). 2504 Scan(&result) 2505 Expect(err).ToNot(HaveOccurred()) 2506 2507 return result 2508 }