github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/db/job_test.go (about) 1 package db_test 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/pf-qiu/concourse/v6/atc" 9 "github.com/pf-qiu/concourse/v6/atc/db" 10 "github.com/pf-qiu/concourse/v6/atc/db/dbtest" 11 "github.com/pf-qiu/concourse/v6/tracing" 12 . "github.com/onsi/ginkgo" 13 . "github.com/onsi/gomega" 14 "go.opentelemetry.io/otel/api/trace/tracetest" 15 ) 16 17 var _ = Describe("Job", func() { 18 var ( 19 job db.Job 20 pipeline db.Pipeline 21 team db.Team 22 ) 23 24 BeforeEach(func() { 25 var err error 26 team, err = teamFactory.CreateTeam(atc.Team{Name: "some-team"}) 27 Expect(err).ToNot(HaveOccurred()) 28 29 var created bool 30 pipeline, created, err = team.SavePipeline(atc.PipelineRef{Name: "fake-pipeline"}, atc.Config{ 31 Jobs: atc.JobConfigs{ 32 { 33 Name: "some-job", 34 35 Public: true, 36 37 PlanSequence: []atc.Step{ 38 { 39 Config: &atc.PutStep{ 40 Name: "some-resource", 41 Params: atc.Params{ 42 "some-param": "some-value", 43 }, 44 }, 45 }, 46 { 47 Config: &atc.GetStep{ 48 Name: "some-input", 49 Resource: "some-resource", 50 Params: atc.Params{ 51 "some-param": "some-value", 52 }, 53 Passed: []string{"job-1", "job-2"}, 54 Trigger: true, 55 }, 56 }, 57 { 58 Config: &atc.TaskStep{ 59 Name: "some-task", 60 Privileged: true, 61 ConfigPath: "some/config/path.yml", 62 Config: &atc.TaskConfig{ 63 RootfsURI: "some-image", 64 }, 65 }, 66 }, 67 }, 68 }, 69 { 70 Name: "some-other-job", 71 }, 72 { 73 Name: "some-private-job", 74 Public: false, 75 }, 76 { 77 Name: "other-serial-group-job", 78 }, 79 { 80 Name: "different-serial-group-job", 81 }, 82 { 83 Name: "job-1", 84 }, 85 { 86 Name: "job-2", 87 }, 88 { 89 Name: "non-triggerable-job", 90 DisableManualTrigger: true, 91 }, 92 }, 93 Resources: atc.ResourceConfigs{ 94 { 95 Name: "some-resource", 96 Type: "some-type", 97 }, 98 { 99 Name: "some-other-resource", 100 Type: "some-type", 101 }, 102 }, 103 }, db.ConfigVersion(0), false) 104 Expect(err).ToNot(HaveOccurred()) 105 Expect(created).To(BeTrue()) 106 107 var found bool 108 job, found, err = pipeline.Job("some-job") 109 Expect(err).ToNot(HaveOccurred()) 110 Expect(found).To(BeTrue()) 111 }) 112 113 Describe("Public", func() { 114 Context("when the config has public set to true", func() { 115 It("returns true", func() { 116 Expect(job.Public()).To(BeTrue()) 117 }) 118 }) 119 120 Context("when the config has public set to false", func() { 121 It("returns false", func() { 122 privateJob, found, err := pipeline.Job("some-private-job") 123 Expect(err).ToNot(HaveOccurred()) 124 Expect(found).To(BeTrue()) 125 Expect(privateJob.Public()).To(BeFalse()) 126 }) 127 }) 128 129 Context("when the config does not have public set", func() { 130 It("returns false", func() { 131 otherJob, found, err := pipeline.Job("some-other-job") 132 Expect(err).ToNot(HaveOccurred()) 133 Expect(found).To(BeTrue()) 134 Expect(otherJob.Public()).To(BeFalse()) 135 }) 136 }) 137 }) 138 139 Describe("DisableManualTrigger", func() { 140 Context("when the config has disable_manual_trigger set to true", func() { 141 It("returns true", func() { 142 nonTriggerableJob, found, err := pipeline.Job("non-triggerable-job") 143 Expect(err).ToNot(HaveOccurred()) 144 Expect(found).To(BeTrue()) 145 Expect(nonTriggerableJob.DisableManualTrigger()).To(BeTrue()) 146 }) 147 }) 148 149 Context("when the config does not have disable_manual_trigger set", func() { 150 It("returns false", func() { 151 otherJob, found, err := pipeline.Job("some-other-job") 152 Expect(err).ToNot(HaveOccurred()) 153 Expect(found).To(BeTrue()) 154 Expect(otherJob.DisableManualTrigger()).To(BeFalse()) 155 }) 156 }) 157 }) 158 159 Describe("Pause and Unpause", func() { 160 var initialRequestedTime time.Time 161 It("starts out as unpaused", func() { 162 Expect(job.Paused()).To(BeFalse()) 163 }) 164 165 Context("when pausing job", func() { 166 BeforeEach(func() { 167 initialRequestedTime = job.ScheduleRequestedTime() 168 169 err := job.Pause() 170 Expect(err).ToNot(HaveOccurred()) 171 172 found, err := job.Reload() 173 Expect(err).ToNot(HaveOccurred()) 174 Expect(found).To(BeTrue()) 175 }) 176 177 It("job is succesfully paused", func() { 178 Expect(job.Paused()).To(BeTrue()) 179 }) 180 181 It("does not request schedule on job", func() { 182 Expect(job.ScheduleRequestedTime()).Should(BeTemporally("==", initialRequestedTime)) 183 }) 184 }) 185 186 Context("when unpausing job", func() { 187 BeforeEach(func() { 188 initialRequestedTime = job.ScheduleRequestedTime() 189 190 err := job.Unpause() 191 Expect(err).ToNot(HaveOccurred()) 192 193 found, err := job.Reload() 194 Expect(err).ToNot(HaveOccurred()) 195 Expect(found).To(BeTrue()) 196 }) 197 198 It("job is successfully unpaused", func() { 199 Expect(job.Paused()).To(BeFalse()) 200 }) 201 202 It("requests schedule on job", func() { 203 Expect(job.ScheduleRequestedTime()).Should(BeTemporally(">", initialRequestedTime)) 204 }) 205 }) 206 207 }) 208 209 Describe("FinishedAndNextBuild", func() { 210 var otherPipeline db.Pipeline 211 var otherJob db.Job 212 213 BeforeEach(func() { 214 var created bool 215 var err error 216 otherPipeline, created, err = team.SavePipeline(atc.PipelineRef{Name: "other-pipeline"}, atc.Config{ 217 Jobs: atc.JobConfigs{ 218 {Name: "some-job"}, 219 }, 220 }, db.ConfigVersion(0), false) 221 Expect(err).ToNot(HaveOccurred()) 222 Expect(created).To(BeTrue()) 223 224 var found bool 225 otherJob, found, err = otherPipeline.Job("some-job") 226 Expect(err).ToNot(HaveOccurred()) 227 Expect(found).To(BeTrue()) 228 }) 229 230 It("can report a job's latest running and finished builds", func() { 231 finished, next, err := job.FinishedAndNextBuild() 232 Expect(err).NotTo(HaveOccurred()) 233 234 Expect(next).To(BeNil()) 235 Expect(finished).To(BeNil()) 236 237 finishedBuild, err := job.CreateBuild() 238 Expect(err).NotTo(HaveOccurred()) 239 240 err = finishedBuild.Finish(db.BuildStatusSucceeded) 241 Expect(err).NotTo(HaveOccurred()) 242 243 otherFinishedBuild, err := otherJob.CreateBuild() 244 Expect(err).NotTo(HaveOccurred()) 245 246 err = otherFinishedBuild.Finish(db.BuildStatusSucceeded) 247 Expect(err).NotTo(HaveOccurred()) 248 249 finished, next, err = job.FinishedAndNextBuild() 250 Expect(err).NotTo(HaveOccurred()) 251 252 Expect(next).To(BeNil()) 253 Expect(finished.ID()).To(Equal(finishedBuild.ID())) 254 255 nextBuild, err := job.CreateBuild() 256 Expect(err).NotTo(HaveOccurred()) 257 258 started, err := nextBuild.Start(atc.Plan{}) 259 Expect(err).NotTo(HaveOccurred()) 260 Expect(started).To(BeTrue()) 261 262 otherNextBuild, err := otherJob.CreateBuild() 263 Expect(err).NotTo(HaveOccurred()) 264 265 otherStarted, err := otherNextBuild.Start(atc.Plan{}) 266 Expect(err).NotTo(HaveOccurred()) 267 Expect(otherStarted).To(BeTrue()) 268 269 finished, next, err = job.FinishedAndNextBuild() 270 Expect(err).NotTo(HaveOccurred()) 271 272 Expect(next.ID()).To(Equal(nextBuild.ID())) 273 Expect(finished.ID()).To(Equal(finishedBuild.ID())) 274 275 anotherRunningBuild, err := job.CreateBuild() 276 Expect(err).NotTo(HaveOccurred()) 277 278 finished, next, err = job.FinishedAndNextBuild() 279 Expect(err).NotTo(HaveOccurred()) 280 281 Expect(next.ID()).To(Equal(nextBuild.ID())) // not anotherRunningBuild 282 Expect(finished.ID()).To(Equal(finishedBuild.ID())) 283 284 started, err = anotherRunningBuild.Start(atc.Plan{}) 285 Expect(err).NotTo(HaveOccurred()) 286 Expect(started).To(BeTrue()) 287 288 finished, next, err = job.FinishedAndNextBuild() 289 Expect(err).NotTo(HaveOccurred()) 290 291 Expect(next.ID()).To(Equal(nextBuild.ID())) // not anotherRunningBuild 292 Expect(finished.ID()).To(Equal(finishedBuild.ID())) 293 294 err = nextBuild.Finish(db.BuildStatusSucceeded) 295 Expect(err).NotTo(HaveOccurred()) 296 297 finished, next, err = job.FinishedAndNextBuild() 298 Expect(err).NotTo(HaveOccurred()) 299 300 Expect(next.ID()).To(Equal(anotherRunningBuild.ID())) 301 Expect(finished.ID()).To(Equal(nextBuild.ID())) 302 }) 303 }) 304 305 Describe("UpdateFirstLoggedBuildID", func() { 306 It("updates FirstLoggedBuildID on a job", func() { 307 By("starting out as 0") 308 Expect(job.FirstLoggedBuildID()).To(BeZero()) 309 310 By("increasing it to 57") 311 err := job.UpdateFirstLoggedBuildID(57) 312 Expect(err).NotTo(HaveOccurred()) 313 314 found, err := job.Reload() 315 Expect(err).NotTo(HaveOccurred()) 316 Expect(found).To(BeTrue()) 317 Expect(job.FirstLoggedBuildID()).To(Equal(57)) 318 319 By("not erroring when it's called with the same number") 320 err = job.UpdateFirstLoggedBuildID(57) 321 Expect(err).NotTo(HaveOccurred()) 322 323 By("erroring when the number decreases") 324 err = job.UpdateFirstLoggedBuildID(56) 325 Expect(err).To(Equal(db.FirstLoggedBuildIDDecreasedError{ 326 Job: "some-job", 327 OldID: 57, 328 NewID: 56, 329 })) 330 }) 331 }) 332 333 Describe("Builds", func() { 334 var ( 335 builds [10]db.Build 336 someJob db.Job 337 someOtherJob db.Job 338 ) 339 340 BeforeEach(func() { 341 for i := 0; i < 10; i++ { 342 var found bool 343 var err error 344 someJob, found, err = pipeline.Job("some-job") 345 Expect(err).NotTo(HaveOccurred()) 346 Expect(found).To(BeTrue()) 347 348 someOtherJob, found, err = pipeline.Job("some-other-job") 349 Expect(err).NotTo(HaveOccurred()) 350 Expect(found).To(BeTrue()) 351 352 build, err := someJob.CreateBuild() 353 Expect(err).NotTo(HaveOccurred()) 354 355 _, err = someOtherJob.CreateBuild() 356 Expect(err).NotTo(HaveOccurred()) 357 358 builds[i] = build 359 } 360 }) 361 362 Context("when there are no builds to be found", func() { 363 It("returns the builds, with previous/next pages", func() { 364 buildsPage, pagination, err := someOtherJob.Builds(db.Page{}) 365 Expect(err).ToNot(HaveOccurred()) 366 Expect(buildsPage).To(Equal([]db.Build{})) 367 Expect(pagination).To(Equal(db.Pagination{})) 368 }) 369 }) 370 371 Context("with no from/to", func() { 372 It("returns the first page, with the given limit, and a next page", func() { 373 buildsPage, pagination, err := someJob.Builds(db.Page{Limit: 2}) 374 Expect(err).ToNot(HaveOccurred()) 375 Expect(buildsPage).To(Equal([]db.Build{builds[9], builds[8]})) 376 Expect(pagination.Newer).To(BeNil()) 377 Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(builds[7].ID()), Limit: 2})) 378 }) 379 }) 380 381 Context("with a to that places it in the middle of the builds", func() { 382 It("returns the builds, with previous/next pages", func() { 383 buildsPage, pagination, err := someJob.Builds(db.Page{To: db.NewIntPtr(builds[6].ID()), Limit: 2}) 384 Expect(err).ToNot(HaveOccurred()) 385 Expect(buildsPage).To(Equal([]db.Build{builds[6], builds[5]})) 386 Expect(pagination.Newer).To(Equal(&db.Page{From: db.NewIntPtr(builds[7].ID()), Limit: 2})) 387 Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(builds[4].ID()), Limit: 2})) 388 }) 389 }) 390 391 Context("with a to that places it at the end of the builds", func() { 392 It("returns the builds, with previous/next pages", func() { 393 buildsPage, pagination, err := someJob.Builds(db.Page{To: db.NewIntPtr(builds[1].ID()), Limit: 2}) 394 Expect(err).ToNot(HaveOccurred()) 395 Expect(buildsPage).To(Equal([]db.Build{builds[1], builds[0]})) 396 Expect(pagination.Newer).To(Equal(&db.Page{From: db.NewIntPtr(builds[2].ID()), Limit: 2})) 397 Expect(pagination.Older).To(BeNil()) 398 }) 399 }) 400 401 Context("with a from that places it in the middle of the builds", func() { 402 It("returns the builds, with previous/next pages", func() { 403 buildsPage, pagination, err := someJob.Builds(db.Page{From: db.NewIntPtr(builds[6].ID()), Limit: 2}) 404 Expect(err).ToNot(HaveOccurred()) 405 Expect(buildsPage).To(Equal([]db.Build{builds[7], builds[6]})) 406 Expect(pagination.Newer).To(Equal(&db.Page{From: db.NewIntPtr(builds[8].ID()), Limit: 2})) 407 Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(builds[5].ID()), Limit: 2})) 408 }) 409 }) 410 411 Context("with a from that places it at the beginning of the builds", func() { 412 It("returns the builds, with previous/next pages", func() { 413 buildsPage, pagination, err := someJob.Builds(db.Page{From: db.NewIntPtr(builds[8].ID()), Limit: 2}) 414 Expect(err).ToNot(HaveOccurred()) 415 Expect(buildsPage).To(Equal([]db.Build{builds[9], builds[8]})) 416 Expect(pagination.Newer).To(BeNil()) 417 Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(builds[7].ID()), Limit: 2})) 418 }) 419 }) 420 }) 421 422 Describe("BuildsWithTime", func() { 423 424 var ( 425 pipeline db.Pipeline 426 builds = make([]db.Build, 4) 427 job db.Job 428 ) 429 430 BeforeEach(func() { 431 var ( 432 err error 433 found bool 434 ) 435 436 config := atc.Config{ 437 Jobs: atc.JobConfigs{ 438 { 439 Name: "some-job", 440 }, 441 { 442 Name: "some-other-job", 443 }, 444 }, 445 } 446 pipeline, _, err = team.SavePipeline(atc.PipelineRef{Name: "some-pipeline"}, config, db.ConfigVersion(1), false) 447 Expect(err).ToNot(HaveOccurred()) 448 449 job, found, err = pipeline.Job("some-job") 450 Expect(err).ToNot(HaveOccurred()) 451 Expect(found).To(BeTrue()) 452 453 for i := range builds { 454 builds[i], err = job.CreateBuild() 455 Expect(err).ToNot(HaveOccurred()) 456 457 buildStart := time.Date(2020, 11, i+1, 0, 0, 0, 0, time.UTC) 458 _, err = dbConn.Exec("UPDATE builds SET start_time = to_timestamp($1) WHERE id = $2", buildStart.Unix(), builds[i].ID()) 459 Expect(err).NotTo(HaveOccurred()) 460 461 builds[i], found, err = job.Build(builds[i].Name()) 462 Expect(err).ToNot(HaveOccurred()) 463 Expect(found).To(BeTrue()) 464 } 465 }) 466 467 Context("when not providing boundaries", func() { 468 Context("without a limit specified", func() { 469 It("returns no builds", func() { 470 returnedBuilds, _, err := job.BuildsWithTime(db.Page{}) 471 Expect(err).NotTo(HaveOccurred()) 472 473 Expect(returnedBuilds).To(BeEmpty()) 474 }) 475 }) 476 477 Context("when a limit specified", func() { 478 It("returns a subset of the builds", func() { 479 returnedBuilds, _, err := job.BuildsWithTime(db.Page{ 480 Limit: 2, 481 }) 482 Expect(err).NotTo(HaveOccurred()) 483 Expect(returnedBuilds).To(ConsistOf(builds[3], builds[2])) 484 }) 485 }) 486 }) 487 488 Context("when providing boundaries", func() { 489 Context("only to", func() { 490 It("returns only those before to", func() { 491 returnedBuilds, _, err := job.BuildsWithTime(db.Page{ 492 To: db.NewIntPtr(int(builds[2].StartTime().Unix())), 493 Limit: 50, 494 }) 495 496 Expect(err).NotTo(HaveOccurred()) 497 Expect(returnedBuilds).To(ConsistOf(builds[0], builds[1], builds[2])) 498 }) 499 }) 500 501 Context("only from", func() { 502 It("returns only those after from", func() { 503 returnedBuilds, _, err := job.BuildsWithTime(db.Page{ 504 From: db.NewIntPtr(int(builds[1].StartTime().Unix())), 505 Limit: 50, 506 }) 507 508 Expect(err).NotTo(HaveOccurred()) 509 Expect(returnedBuilds).To(ConsistOf(builds[1], builds[2], builds[3])) 510 }) 511 }) 512 513 Context("from and to", func() { 514 It("returns only elements in the range", func() { 515 returnedBuilds, _, err := job.BuildsWithTime(db.Page{ 516 From: db.NewIntPtr(int(builds[1].StartTime().Unix())), 517 To: db.NewIntPtr(int(builds[2].StartTime().Unix())), 518 Limit: 50, 519 }) 520 Expect(err).NotTo(HaveOccurred()) 521 Expect(returnedBuilds).To(ConsistOf(builds[1], builds[2])) 522 }) 523 }) 524 }) 525 }) 526 527 Describe("Build", func() { 528 var firstBuild db.Build 529 530 Context("when a build exists", func() { 531 BeforeEach(func() { 532 var err error 533 firstBuild, err = job.CreateBuild() 534 Expect(err).NotTo(HaveOccurred()) 535 }) 536 537 It("finds the latest build", func() { 538 secondBuild, err := job.CreateBuild() 539 Expect(err).NotTo(HaveOccurred()) 540 541 build, found, err := job.Build("latest") 542 Expect(err).NotTo(HaveOccurred()) 543 Expect(found).To(BeTrue()) 544 Expect(build.ID()).To(Equal(secondBuild.ID())) 545 Expect(build.Status()).To(Equal(secondBuild.Status())) 546 }) 547 548 It("finds the build", func() { 549 build, found, err := job.Build(firstBuild.Name()) 550 Expect(err).NotTo(HaveOccurred()) 551 Expect(found).To(BeTrue()) 552 Expect(build.ID()).To(Equal(firstBuild.ID())) 553 Expect(build.Status()).To(Equal(firstBuild.Status())) 554 }) 555 }) 556 557 Context("when the build does not exist", func() { 558 It("does not error", func() { 559 build, found, err := job.Build("bogus-build") 560 Expect(err).NotTo(HaveOccurred()) 561 Expect(found).To(BeFalse()) 562 Expect(build).To(BeNil()) 563 }) 564 565 It("does not error finding the latest", func() { 566 build, found, err := job.Build("latest") 567 Expect(err).NotTo(HaveOccurred()) 568 Expect(found).To(BeFalse()) 569 Expect(build).To(BeNil()) 570 }) 571 }) 572 573 Context("creating a build", func() { 574 It("requests schedule on the job", func() { 575 requestedSchedule := job.ScheduleRequestedTime() 576 577 _, err := job.CreateBuild() 578 Expect(err).NotTo(HaveOccurred()) 579 580 found, err := job.Reload() 581 Expect(err).NotTo(HaveOccurred()) 582 Expect(found).To(BeTrue()) 583 584 Expect(job.ScheduleRequestedTime()).Should(BeTemporally(">", requestedSchedule)) 585 }) 586 }) 587 }) 588 589 Describe("RerunBuild", func() { 590 var firstBuild db.Build 591 var rerunErr error 592 var rerunBuild db.Build 593 var buildToRerun db.Build 594 595 JustBeforeEach(func() { 596 rerunBuild, rerunErr = job.RerunBuild(buildToRerun) 597 }) 598 599 Context("when the first build exists", func() { 600 BeforeEach(func() { 601 var err error 602 firstBuild, err = job.CreateBuild() 603 Expect(err).NotTo(HaveOccurred()) 604 605 buildToRerun = firstBuild 606 }) 607 608 It("finds the build", func() { 609 Expect(rerunErr).ToNot(HaveOccurred()) 610 Expect(rerunBuild.Name()).To(Equal(fmt.Sprintf("%s.1", firstBuild.Name()))) 611 Expect(rerunBuild.RerunNumber()).To(Equal(1)) 612 613 build, found, err := job.Build(rerunBuild.Name()) 614 Expect(err).NotTo(HaveOccurred()) 615 Expect(found).To(BeTrue()) 616 Expect(build.ID()).To(Equal(rerunBuild.ID())) 617 Expect(build.Status()).To(Equal(rerunBuild.Status())) 618 }) 619 620 It("requests schedule on the job", func() { 621 requestedSchedule := job.ScheduleRequestedTime() 622 623 _, err := job.RerunBuild(buildToRerun) 624 Expect(err).NotTo(HaveOccurred()) 625 626 found, err := job.Reload() 627 Expect(err).NotTo(HaveOccurred()) 628 Expect(found).To(BeTrue()) 629 630 Expect(job.ScheduleRequestedTime()).Should(BeTemporally(">", requestedSchedule)) 631 }) 632 633 Context("when there is an existing rerun build", func() { 634 var rerun1 db.Build 635 636 BeforeEach(func() { 637 var err error 638 rerun1, err = job.RerunBuild(buildToRerun) 639 Expect(err).ToNot(HaveOccurred()) 640 Expect(rerun1.Name()).To(Equal(fmt.Sprintf("%s.1", firstBuild.Name()))) 641 Expect(rerun1.RerunNumber()).To(Equal(1)) 642 }) 643 644 It("increments the rerun build number", func() { 645 Expect(rerunErr).ToNot(HaveOccurred()) 646 Expect(rerunBuild.Name()).To(Equal(fmt.Sprintf("%s.2", firstBuild.Name()))) 647 Expect(rerunBuild.RerunNumber()).To(Equal(rerun1.RerunNumber() + 1)) 648 }) 649 }) 650 651 Context("when we try to rerun a rerun build", func() { 652 var rerun1 db.Build 653 654 BeforeEach(func() { 655 var err error 656 rerun1, err = job.RerunBuild(buildToRerun) 657 Expect(err).ToNot(HaveOccurred()) 658 Expect(rerun1.Name()).To(Equal(fmt.Sprintf("%s.1", firstBuild.Name()))) 659 Expect(rerun1.RerunNumber()).To(Equal(1)) 660 661 buildToRerun = rerun1 662 }) 663 664 It("keeps the name of original build and increments the rerun build number", func() { 665 Expect(rerunErr).ToNot(HaveOccurred()) 666 Expect(rerunBuild.Name()).To(Equal(fmt.Sprintf("%s.2", firstBuild.Name()))) 667 Expect(rerunBuild.RerunNumber()).To(Equal(rerun1.RerunNumber() + 1)) 668 }) 669 }) 670 }) 671 }) 672 673 Describe("ScheduleBuild", func() { 674 var ( 675 schedulingBuild db.Build 676 scheduleFound, reloadFound bool 677 schedulingErr error 678 ) 679 680 saveMaxInFlightPipeline := func() { 681 BeforeEach(func() { 682 var err error 683 pipeline, _, err = team.SavePipeline(atc.PipelineRef{Name: "fake-pipeline"}, atc.Config{ 684 Jobs: atc.JobConfigs{ 685 { 686 Name: "some-job", 687 688 Public: true, 689 690 RawMaxInFlight: 2, 691 692 PlanSequence: []atc.Step{ 693 { 694 Config: &atc.PutStep{ 695 Name: "some-resource", 696 Params: atc.Params{ 697 "some-param": "some-value", 698 }, 699 }, 700 }, 701 { 702 Config: &atc.GetStep{ 703 Name: "some-input", 704 Resource: "some-resource", 705 Params: atc.Params{ 706 "some-param": "some-value", 707 }, 708 Passed: []string{"job-1", "job-2"}, 709 Trigger: true, 710 }, 711 }, 712 { 713 Config: &atc.TaskStep{ 714 Name: "some-task", 715 Privileged: true, 716 ConfigPath: "some/config/path.yml", 717 Config: &atc.TaskConfig{ 718 RootfsURI: "some-image", 719 }, 720 }, 721 }, 722 }, 723 }, 724 { 725 Name: "some-other-job", 726 }, 727 { 728 Name: "some-private-job", 729 Public: false, 730 }, 731 { 732 Name: "other-serial-group-job", 733 }, 734 { 735 Name: "different-serial-group-job", 736 }, 737 { 738 Name: "job-1", 739 }, 740 { 741 Name: "job-2", 742 }, 743 }, 744 Resources: atc.ResourceConfigs{ 745 { 746 Name: "some-resource", 747 Type: "some-type", 748 }, 749 { 750 Name: "some-other-resource", 751 Type: "some-type", 752 }, 753 }, 754 }, pipeline.ConfigVersion(), false) 755 Expect(err).ToNot(HaveOccurred()) 756 }) 757 } 758 759 saveSerialGroupsPipeline := func() { 760 BeforeEach(func() { 761 var err error 762 pipeline, _, err = team.SavePipeline(atc.PipelineRef{Name: "fake-pipeline"}, atc.Config{ 763 Jobs: atc.JobConfigs{ 764 { 765 Name: "some-job", 766 767 Public: true, 768 769 Serial: true, 770 771 SerialGroups: []string{"serial-group"}, 772 773 RawMaxInFlight: 2, 774 775 PlanSequence: []atc.Step{ 776 { 777 Config: &atc.PutStep{ 778 Name: "some-resource", 779 Params: atc.Params{ 780 "some-param": "some-value", 781 }, 782 }, 783 }, 784 { 785 Config: &atc.GetStep{ 786 Name: "some-input", 787 Resource: "some-resource", 788 Params: atc.Params{ 789 "some-param": "some-value", 790 }, 791 Passed: []string{"job-1", "job-2"}, 792 Trigger: true, 793 }, 794 }, 795 { 796 Config: &atc.TaskStep{ 797 Name: "some-task", 798 Privileged: true, 799 ConfigPath: "some/config/path.yml", 800 Config: &atc.TaskConfig{ 801 RootfsURI: "some-image", 802 }, 803 }, 804 }, 805 }, 806 }, 807 { 808 Name: "some-other-job", 809 }, 810 { 811 Name: "some-private-job", 812 Public: false, 813 }, 814 { 815 Name: "other-serial-group-job", 816 SerialGroups: []string{"serial-group", "really-different-group"}, 817 }, 818 { 819 Name: "different-serial-group-job", 820 SerialGroups: []string{"different-serial-group"}, 821 }, 822 { 823 Name: "job-1", 824 }, 825 { 826 Name: "job-2", 827 }, 828 }, 829 Resources: atc.ResourceConfigs{ 830 { 831 Name: "some-resource", 832 Type: "some-type", 833 }, 834 { 835 Name: "some-other-resource", 836 Type: "some-type", 837 }, 838 }, 839 }, pipeline.ConfigVersion(), false) 840 Expect(err).ToNot(HaveOccurred()) 841 }) 842 } 843 844 JustBeforeEach(func() { 845 var found bool 846 var err error 847 job, found, err = pipeline.Job("some-job") 848 Expect(err).ToNot(HaveOccurred()) 849 Expect(found).To(BeTrue()) 850 851 scheduleFound, schedulingErr = job.ScheduleBuild(schedulingBuild) 852 853 reloadFound, err = schedulingBuild.Reload() 854 Expect(err).ToNot(HaveOccurred()) 855 }) 856 857 Context("when the scheduling build is created first", func() { 858 BeforeEach(func() { 859 var err error 860 schedulingBuild, err = job.CreateBuild() 861 Expect(err).ToNot(HaveOccurred()) 862 }) 863 864 Context("when the job config doesn't specify max in flight", func() { 865 BeforeEach(func() { 866 var created bool 867 var err error 868 pipeline, created, err = team.SavePipeline(atc.PipelineRef{Name: "other-pipeline"}, atc.Config{ 869 Jobs: atc.JobConfigs{ 870 { 871 Name: "some-job", 872 }, 873 }, 874 }, db.ConfigVersion(0), false) 875 Expect(err).ToNot(HaveOccurred()) 876 Expect(created).To(BeTrue()) 877 878 var found bool 879 job, found, err = pipeline.Job("some-job") 880 Expect(err).ToNot(HaveOccurred()) 881 Expect(found).To(BeTrue()) 882 }) 883 884 It("schedules the build", func() { 885 Expect(schedulingErr).ToNot(HaveOccurred()) 886 Expect(scheduleFound).To(BeTrue()) 887 }) 888 889 Context("when build exists", func() { 890 Context("when the pipeline is paused", func() { 891 BeforeEach(func() { 892 err := pipeline.Pause() 893 Expect(err).ToNot(HaveOccurred()) 894 }) 895 896 It("returns false", func() { 897 Expect(schedulingErr).ToNot(HaveOccurred()) 898 Expect(scheduleFound).To(BeFalse()) 899 Expect(reloadFound).To(BeTrue()) 900 Expect(schedulingBuild.IsScheduled()).To(BeFalse()) 901 }) 902 }) 903 904 Context("when the job is paused", func() { 905 BeforeEach(func() { 906 err := job.Pause() 907 Expect(err).ToNot(HaveOccurred()) 908 }) 909 910 It("returns false", func() { 911 Expect(schedulingErr).ToNot(HaveOccurred()) 912 Expect(scheduleFound).To(BeFalse()) 913 Expect(reloadFound).To(BeTrue()) 914 Expect(schedulingBuild.IsScheduled()).To(BeFalse()) 915 }) 916 }) 917 918 Context("when the pipeline and job is not paused", func() { 919 It("sets the build to scheduled", func() { 920 Expect(schedulingErr).ToNot(HaveOccurred()) 921 Expect(scheduleFound).To(BeTrue()) 922 Expect(reloadFound).To(BeTrue()) 923 Expect(schedulingBuild.IsScheduled()).To(BeTrue()) 924 }) 925 }) 926 }) 927 928 Context("when the build does not exist", func() { 929 var deleteFound bool 930 BeforeEach(func() { 931 var err error 932 deleteFound, err = schedulingBuild.Delete() 933 Expect(err).ToNot(HaveOccurred()) 934 }) 935 936 It("returns false", func() { 937 Expect(schedulingErr).To(HaveOccurred()) 938 Expect(scheduleFound).To(BeFalse()) 939 Expect(reloadFound).To(BeFalse()) 940 Expect(deleteFound).To(BeTrue()) 941 }) 942 }) 943 }) 944 945 Context("when the job config specifies max in flight = 2", func() { 946 Context("when there are 2 builds running", func() { 947 var startedBuild, scheduledBuild db.Build 948 949 BeforeEach(func() { 950 var err error 951 startedBuild, err = job.CreateBuild() 952 Expect(err).ToNot(HaveOccurred()) 953 scheduled, err := job.ScheduleBuild(startedBuild) 954 Expect(err).ToNot(HaveOccurred()) 955 Expect(scheduled).To(BeTrue()) 956 _, err = startedBuild.Start(atc.Plan{}) 957 Expect(err).NotTo(HaveOccurred()) 958 959 scheduledBuild, err = job.CreateBuild() 960 Expect(err).NotTo(HaveOccurred()) 961 scheduled, err = job.ScheduleBuild(scheduledBuild) 962 Expect(err).ToNot(HaveOccurred()) 963 Expect(scheduled).To(BeTrue()) 964 _, err = startedBuild.Start(atc.Plan{}) 965 Expect(err).NotTo(HaveOccurred()) 966 967 for _, s := range []db.BuildStatus{db.BuildStatusSucceeded, db.BuildStatusFailed, db.BuildStatusErrored, db.BuildStatusAborted} { 968 finishedBuild, err := job.CreateBuild() 969 Expect(err).NotTo(HaveOccurred()) 970 971 scheduled, err = job.ScheduleBuild(finishedBuild) 972 Expect(err).NotTo(HaveOccurred()) 973 Expect(scheduled).To(BeTrue()) 974 975 err = finishedBuild.Finish(s) 976 Expect(err).NotTo(HaveOccurred()) 977 } 978 979 otherJob, found, err := pipeline.Job("some-other-job") 980 Expect(err).NotTo(HaveOccurred()) 981 Expect(found).To(BeTrue()) 982 983 _, err = otherJob.CreateBuild() 984 Expect(err).NotTo(HaveOccurred()) 985 }) 986 987 saveMaxInFlightPipeline() 988 989 It("returns max in flight reached so it does not schedule", func() { 990 Expect(schedulingErr).ToNot(HaveOccurred()) 991 Expect(scheduleFound).To(BeFalse()) 992 Expect(reloadFound).To(BeTrue()) 993 }) 994 }) 995 996 Context("when there is 1 build running", func() { 997 BeforeEach(func() { 998 startedBuild, err := job.CreateBuild() 999 Expect(err).NotTo(HaveOccurred()) 1000 scheduled, err := job.ScheduleBuild(startedBuild) 1001 Expect(err).NotTo(HaveOccurred()) 1002 Expect(scheduled).To(BeTrue()) 1003 _, err = startedBuild.Start(atc.Plan{}) 1004 Expect(err).NotTo(HaveOccurred()) 1005 1006 for _, s := range []db.BuildStatus{db.BuildStatusSucceeded, db.BuildStatusFailed, db.BuildStatusErrored, db.BuildStatusAborted} { 1007 finishedBuild, err := job.CreateBuild() 1008 Expect(err).NotTo(HaveOccurred()) 1009 1010 scheduled, err = job.ScheduleBuild(finishedBuild) 1011 Expect(err).NotTo(HaveOccurred()) 1012 Expect(scheduled).To(BeTrue()) 1013 1014 err = finishedBuild.Finish(s) 1015 Expect(err).NotTo(HaveOccurred()) 1016 } 1017 1018 err = job.SaveNextInputMapping(nil, true) 1019 Expect(err).NotTo(HaveOccurred()) 1020 }) 1021 1022 saveMaxInFlightPipeline() 1023 1024 It("schedules the build", func() { 1025 Expect(schedulingErr).ToNot(HaveOccurred()) 1026 Expect(scheduleFound).To(BeTrue()) 1027 Expect(reloadFound).To(BeTrue()) 1028 }) 1029 }) 1030 }) 1031 1032 Context("when the job is in serial groups", func() { 1033 Context("when multiple jobs in the serial group is running", func() { 1034 BeforeEach(func() { 1035 var err error 1036 _, err = job.CreateBuild() 1037 Expect(err).NotTo(HaveOccurred()) 1038 1039 otherSerialJob, found, err := pipeline.Job("other-serial-group-job") 1040 Expect(err).NotTo(HaveOccurred()) 1041 Expect(found).To(BeTrue()) 1042 1043 serialGroupBuild, err := otherSerialJob.CreateBuild() 1044 Expect(err).NotTo(HaveOccurred()) 1045 1046 scheduled, err := otherSerialJob.ScheduleBuild(serialGroupBuild) 1047 Expect(err).NotTo(HaveOccurred()) 1048 Expect(scheduled).To(BeTrue()) 1049 1050 differentSerialJob, found, err := pipeline.Job("different-serial-group-job") 1051 Expect(err).NotTo(HaveOccurred()) 1052 Expect(found).To(BeTrue()) 1053 1054 differentSerialGroupBuild, err := differentSerialJob.CreateBuild() 1055 Expect(err).NotTo(HaveOccurred()) 1056 1057 scheduled, err = differentSerialJob.ScheduleBuild(differentSerialGroupBuild) 1058 Expect(err).NotTo(HaveOccurred()) 1059 Expect(scheduled).To(BeTrue()) 1060 }) 1061 1062 saveSerialGroupsPipeline() 1063 1064 It("does not schedule the build", func() { 1065 Expect(schedulingErr).ToNot(HaveOccurred()) 1066 Expect(scheduleFound).To(BeFalse()) 1067 }) 1068 }) 1069 1070 Context("when no jobs in the serial groups are running", func() { 1071 BeforeEach(func() { 1072 otherSerialJob, found, err := pipeline.Job("other-serial-group-job") 1073 Expect(err).NotTo(HaveOccurred()) 1074 Expect(found).To(BeTrue()) 1075 1076 serialGroupBuild, err := otherSerialJob.CreateBuild() 1077 Expect(err).NotTo(HaveOccurred()) 1078 1079 scheduled, err := otherSerialJob.ScheduleBuild(serialGroupBuild) 1080 Expect(err).NotTo(HaveOccurred()) 1081 Expect(scheduled).To(BeTrue()) 1082 1083 err = serialGroupBuild.Finish(db.BuildStatusSucceeded) 1084 Expect(err).NotTo(HaveOccurred()) 1085 1086 differentSerialJob, found, err := pipeline.Job("different-serial-group-job") 1087 Expect(err).NotTo(HaveOccurred()) 1088 Expect(found).To(BeTrue()) 1089 1090 differentSerialGroupBuild, err := differentSerialJob.CreateBuild() 1091 Expect(err).NotTo(HaveOccurred()) 1092 1093 scheduled, err = differentSerialJob.ScheduleBuild(differentSerialGroupBuild) 1094 Expect(err).NotTo(HaveOccurred()) 1095 Expect(scheduled).To(BeTrue()) 1096 1097 err = job.SaveNextInputMapping(nil, true) 1098 Expect(err).NotTo(HaveOccurred()) 1099 }) 1100 1101 saveSerialGroupsPipeline() 1102 1103 It("does schedule the build", func() { 1104 Expect(schedulingErr).ToNot(HaveOccurred()) 1105 Expect(scheduleFound).To(BeTrue()) 1106 Expect(reloadFound).To(BeTrue()) 1107 }) 1108 }) 1109 }) 1110 }) 1111 1112 Context("when the scheduling build is not the first one created (with serial groups)", func() { 1113 Context("when the scheduling build has inputs determined as false", func() { 1114 BeforeEach(func() { 1115 var err error 1116 schedulingBuild, err = job.CreateBuild() 1117 Expect(err).NotTo(HaveOccurred()) 1118 1119 err = job.SaveNextInputMapping(nil, false) 1120 Expect(err).NotTo(HaveOccurred()) 1121 }) 1122 1123 saveSerialGroupsPipeline() 1124 1125 It("does not schedule because the inputs determined is false", func() { 1126 Expect(schedulingErr).ToNot(HaveOccurred()) 1127 Expect(scheduleFound).To(BeFalse()) 1128 Expect(reloadFound).To(BeTrue()) 1129 }) 1130 }) 1131 1132 Context("when another build within the serial group is scheduled first", func() { 1133 BeforeEach(func() { 1134 otherSerialJob, found, err := pipeline.Job("other-serial-group-job") 1135 Expect(err).NotTo(HaveOccurred()) 1136 Expect(found).To(BeTrue()) 1137 1138 _, err = otherSerialJob.CreateBuild() 1139 Expect(err).NotTo(HaveOccurred()) 1140 1141 err = otherSerialJob.SaveNextInputMapping(nil, true) 1142 Expect(err).NotTo(HaveOccurred()) 1143 1144 schedulingBuild, err = job.CreateBuild() 1145 Expect(err).NotTo(HaveOccurred()) 1146 1147 err = job.SaveNextInputMapping(nil, true) 1148 Expect(err).NotTo(HaveOccurred()) 1149 }) 1150 1151 saveSerialGroupsPipeline() 1152 1153 It("does not schedule because the build we are trying to schedule is not the next most pending build in the serial group", func() { 1154 Expect(schedulingErr).ToNot(HaveOccurred()) 1155 Expect(scheduleFound).To(BeFalse()) 1156 Expect(reloadFound).To(BeTrue()) 1157 }) 1158 }) 1159 1160 Context("when the scheduling build has it's inputs determined and created earlier", func() { 1161 BeforeEach(func() { 1162 var err error 1163 schedulingBuild, err = job.CreateBuild() 1164 Expect(err).NotTo(HaveOccurred()) 1165 1166 otherSerialJob, found, err := pipeline.Job("other-serial-group-job") 1167 Expect(err).NotTo(HaveOccurred()) 1168 Expect(found).To(BeTrue()) 1169 1170 _, err = otherSerialJob.CreateBuild() 1171 Expect(err).NotTo(HaveOccurred()) 1172 1173 err = job.SaveNextInputMapping(nil, true) 1174 Expect(err).NotTo(HaveOccurred()) 1175 err = otherSerialJob.SaveNextInputMapping(nil, true) 1176 Expect(err).NotTo(HaveOccurred()) 1177 }) 1178 1179 saveSerialGroupsPipeline() 1180 1181 It("does schedule the build", func() { 1182 Expect(schedulingErr).ToNot(HaveOccurred()) 1183 Expect(scheduleFound).To(BeTrue()) 1184 Expect(reloadFound).To(BeTrue()) 1185 }) 1186 }) 1187 1188 Context("when the job is paused but has inputs determined", func() { 1189 BeforeEach(func() { 1190 var err error 1191 schedulingBuild, err = job.CreateBuild() 1192 Expect(err).NotTo(HaveOccurred()) 1193 1194 otherSerialJob, found, err := pipeline.Job("other-serial-group-job") 1195 Expect(err).NotTo(HaveOccurred()) 1196 Expect(found).To(BeTrue()) 1197 1198 _, err = otherSerialJob.CreateBuild() 1199 Expect(err).NotTo(HaveOccurred()) 1200 1201 err = job.SaveNextInputMapping(nil, true) 1202 Expect(err).NotTo(HaveOccurred()) 1203 err = otherSerialJob.SaveNextInputMapping(nil, true) 1204 Expect(err).NotTo(HaveOccurred()) 1205 1206 err = job.Pause() 1207 Expect(err).NotTo(HaveOccurred()) 1208 }) 1209 1210 saveSerialGroupsPipeline() 1211 1212 It("does not schedule the build", func() { 1213 Expect(schedulingErr).ToNot(HaveOccurred()) 1214 Expect(scheduleFound).To(BeFalse()) 1215 Expect(reloadFound).To(BeTrue()) 1216 }) 1217 }) 1218 1219 Context("when there are other succeeded builds within the same serial group", func() { 1220 BeforeEach(func() { 1221 otherSerialJob, found, err := pipeline.Job("other-serial-group-job") 1222 Expect(err).NotTo(HaveOccurred()) 1223 Expect(found).To(BeTrue()) 1224 1225 succeededBuild, err := otherSerialJob.CreateBuild() 1226 Expect(err).NotTo(HaveOccurred()) 1227 1228 err = succeededBuild.Finish(db.BuildStatusSucceeded) 1229 Expect(err).NotTo(HaveOccurred()) 1230 1231 err = job.SaveNextInputMapping(nil, true) 1232 Expect(err).NotTo(HaveOccurred()) 1233 err = otherSerialJob.SaveNextInputMapping(nil, true) 1234 Expect(err).NotTo(HaveOccurred()) 1235 1236 schedulingBuild, err = job.CreateBuild() 1237 Expect(err).NotTo(HaveOccurred()) 1238 }) 1239 1240 saveSerialGroupsPipeline() 1241 1242 It("does schedule builds because we only care about running or pending builds", func() { 1243 Expect(schedulingErr).ToNot(HaveOccurred()) 1244 Expect(scheduleFound).To(BeTrue()) 1245 Expect(reloadFound).To(BeTrue()) 1246 }) 1247 }) 1248 1249 Context("when the job we are trying to schedule has multiple serial groups", func() { 1250 BeforeEach(func() { 1251 otherSerialJob, found, err := pipeline.Job("some-job") 1252 Expect(err).NotTo(HaveOccurred()) 1253 Expect(found).To(BeTrue()) 1254 1255 _, err = otherSerialJob.CreateBuild() 1256 Expect(err).NotTo(HaveOccurred()) 1257 1258 job, found, err = pipeline.Job("other-serial-group-job") 1259 Expect(err).NotTo(HaveOccurred()) 1260 Expect(found).To(BeTrue()) 1261 1262 schedulingBuild, err = job.CreateBuild() 1263 Expect(err).NotTo(HaveOccurred()) 1264 1265 err = job.SaveNextInputMapping(nil, true) 1266 Expect(err).NotTo(HaveOccurred()) 1267 err = otherSerialJob.SaveNextInputMapping(nil, true) 1268 Expect(err).NotTo(HaveOccurred()) 1269 }) 1270 1271 saveSerialGroupsPipeline() 1272 1273 It("does not schedule a build because the a build within one of the serial groups was created earlier", func() { 1274 Expect(schedulingErr).ToNot(HaveOccurred()) 1275 Expect(scheduleFound).To(BeFalse()) 1276 Expect(reloadFound).To(BeTrue()) 1277 }) 1278 }) 1279 }) 1280 }) 1281 1282 Describe("GetNextBuildInputs", func() { 1283 var ( 1284 versions []atc.ResourceVersion 1285 spanContext db.SpanContext 1286 scenario *dbtest.Scenario 1287 ) 1288 1289 BeforeEach(func() { 1290 spanContext = db.SpanContext{"fake": "version"} 1291 1292 scenario = dbtest.Setup( 1293 builder.WithPipeline(atc.Config{ 1294 Jobs: atc.JobConfigs{ 1295 { 1296 Name: "some-job", 1297 PlanSequence: []atc.Step{ 1298 { 1299 Config: &atc.GetStep{ 1300 Name: "some-input", 1301 Resource: "some-resource", 1302 Passed: []string{"job-1", "job-2"}, 1303 Trigger: true, 1304 }, 1305 }, 1306 { 1307 Config: &atc.GetStep{ 1308 Name: "some-input-2", 1309 Resource: "some-resource", 1310 Passed: []string{"job-1"}, 1311 Trigger: true, 1312 }, 1313 }, 1314 { 1315 Config: &atc.GetStep{ 1316 Name: "some-input-3", 1317 Resource: "some-resource", 1318 Trigger: true, 1319 }, 1320 }, 1321 }, 1322 }, 1323 { 1324 Name: "job-1", 1325 }, 1326 { 1327 Name: "job-2", 1328 }, 1329 }, 1330 Resources: atc.ResourceConfigs{ 1331 { 1332 Name: "some-resource", 1333 Type: "some-base-resource-type", 1334 }, 1335 }, 1336 }), 1337 builder.WithSpanContext(spanContext), 1338 builder.WithResourceVersions( 1339 "some-resource", 1340 atc.Version{"version": "v1"}, 1341 atc.Version{"version": "v2"}, 1342 atc.Version{"version": "v3"}, 1343 ), 1344 ) 1345 1346 reversions, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 3}, nil) 1347 Expect(err).NotTo(HaveOccurred()) 1348 Expect(found).To(BeTrue()) 1349 1350 versions = []atc.ResourceVersion{reversions[2], reversions[1], reversions[0]} 1351 }) 1352 1353 Describe("partial next build inputs", func() { 1354 It("gets partial next build inputs for the given job name", func() { 1355 inputVersions := db.InputMapping{ 1356 "some-input-2": db.InputResult{ 1357 ResolveError: "disaster", 1358 }, 1359 } 1360 1361 err := scenario.Job("some-job").SaveNextInputMapping(inputVersions, false) 1362 Expect(err).NotTo(HaveOccurred()) 1363 1364 buildInputs := []db.BuildInput{ 1365 { 1366 Name: "some-input-2", 1367 ResolveError: "disaster", 1368 }, 1369 } 1370 1371 actualBuildInputs, err := scenario.Job("some-job").GetNextBuildInputs() 1372 Expect(err).NotTo(HaveOccurred()) 1373 1374 Expect(actualBuildInputs).To(ConsistOf(buildInputs)) 1375 }) 1376 1377 It("gets full next build inputs for the given job name", func() { 1378 inputVersions := db.InputMapping{ 1379 "some-input-1": db.InputResult{ 1380 Input: &db.AlgorithmInput{ 1381 AlgorithmVersion: db.AlgorithmVersion{ 1382 Version: db.ResourceVersion(convertToMD5(versions[0].Version)), 1383 ResourceID: scenario.Resource("some-resource").ID(), 1384 }, 1385 FirstOccurrence: false, 1386 }, 1387 PassedBuildIDs: []int{}, 1388 }, 1389 "some-input-2": db.InputResult{ 1390 Input: &db.AlgorithmInput{ 1391 AlgorithmVersion: db.AlgorithmVersion{ 1392 Version: db.ResourceVersion(convertToMD5(versions[1].Version)), 1393 ResourceID: scenario.Resource("some-resource").ID(), 1394 }, 1395 FirstOccurrence: false, 1396 }, 1397 PassedBuildIDs: []int{}, 1398 }, 1399 "some-input-3": db.InputResult{ 1400 Input: &db.AlgorithmInput{ 1401 AlgorithmVersion: db.AlgorithmVersion{ 1402 Version: db.ResourceVersion(convertToMD5(versions[2].Version)), 1403 ResourceID: scenario.Resource("some-resource").ID(), 1404 }, 1405 FirstOccurrence: false, 1406 }, 1407 PassedBuildIDs: []int{}, 1408 }, 1409 } 1410 1411 err := scenario.Job("some-job").SaveNextInputMapping(inputVersions, true) 1412 Expect(err).NotTo(HaveOccurred()) 1413 1414 buildInputs := []db.BuildInput{ 1415 { 1416 Name: "some-input-1", 1417 ResourceID: scenario.Resource("some-resource").ID(), 1418 Version: atc.Version{"version": "v1"}, 1419 FirstOccurrence: false, 1420 Context: spanContext, 1421 }, 1422 { 1423 Name: "some-input-2", 1424 ResourceID: scenario.Resource("some-resource").ID(), 1425 Version: atc.Version{"version": "v2"}, 1426 FirstOccurrence: false, 1427 Context: spanContext, 1428 }, 1429 { 1430 Name: "some-input-3", 1431 ResourceID: scenario.Resource("some-resource").ID(), 1432 Version: atc.Version{"version": "v3"}, 1433 FirstOccurrence: false, 1434 Context: spanContext, 1435 }, 1436 } 1437 1438 actualBuildInputs, err := scenario.Job("some-job").GetNextBuildInputs() 1439 Expect(err).NotTo(HaveOccurred()) 1440 1441 Expect(actualBuildInputs).To(ConsistOf(buildInputs)) 1442 }) 1443 }) 1444 }) 1445 1446 Describe("GetFullNextBuildInputs", func() { 1447 var ( 1448 versions []atc.ResourceVersion 1449 scenarioPipeline1 *dbtest.Scenario 1450 scenarioPipeline2 *dbtest.Scenario 1451 ) 1452 1453 BeforeEach(func() { 1454 scenarioPipeline1 = dbtest.Setup( 1455 builder.WithPipeline(atc.Config{ 1456 Jobs: atc.JobConfigs{ 1457 { 1458 Name: "some-job", 1459 PlanSequence: []atc.Step{ 1460 { 1461 Config: &atc.GetStep{ 1462 Name: "some-input", 1463 Resource: "some-resource", 1464 }, 1465 }, 1466 }, 1467 }, 1468 }, 1469 Resources: atc.ResourceConfigs{ 1470 { 1471 Name: "some-resource", 1472 Type: "some-base-resource-type", 1473 }, 1474 }, 1475 }), 1476 builder.WithResourceVersions( 1477 "some-resource", 1478 atc.Version{"version": "v1"}, 1479 atc.Version{"version": "v2"}, 1480 atc.Version{"version": "v3"}, 1481 ), 1482 builder.WithVersionMetadata("some-resource", atc.Version{"version": "v1"}, db.ResourceConfigMetadataFields{ 1483 db.ResourceConfigMetadataField{ 1484 Name: "name1", 1485 Value: "value1", 1486 }, 1487 }), 1488 ) 1489 1490 reversions, _, found, err := scenarioPipeline1.Resource("some-resource").Versions(db.Page{Limit: 3}, nil) 1491 Expect(err).NotTo(HaveOccurred()) 1492 Expect(found).To(BeTrue()) 1493 1494 versions = []atc.ResourceVersion{reversions[2], reversions[1], reversions[0]} 1495 1496 scenarioPipeline2 = dbtest.Setup( 1497 builder.WithPipeline(atc.Config{ 1498 Jobs: atc.JobConfigs{ 1499 { 1500 Name: "some-job", 1501 }, 1502 { 1503 Name: "some-other-job", 1504 }, 1505 }, 1506 Resources: atc.ResourceConfigs{ 1507 { 1508 Name: "some-resource", 1509 Type: "some-type", 1510 }, 1511 }, 1512 }), 1513 ) 1514 }) 1515 1516 It("gets next build inputs for the given job name", func() { 1517 inputVersions := db.InputMapping{ 1518 "some-input-1": db.InputResult{ 1519 Input: &db.AlgorithmInput{ 1520 AlgorithmVersion: db.AlgorithmVersion{ 1521 Version: db.ResourceVersion(convertToMD5(versions[0].Version)), 1522 ResourceID: scenarioPipeline1.Resource("some-resource").ID(), 1523 }, 1524 FirstOccurrence: false, 1525 }, 1526 PassedBuildIDs: []int{}, 1527 }, 1528 "some-input-2": db.InputResult{ 1529 Input: &db.AlgorithmInput{ 1530 AlgorithmVersion: db.AlgorithmVersion{ 1531 Version: db.ResourceVersion(convertToMD5(versions[1].Version)), 1532 ResourceID: scenarioPipeline1.Resource("some-resource").ID(), 1533 }, 1534 FirstOccurrence: true, 1535 }, 1536 PassedBuildIDs: []int{}, 1537 }, 1538 } 1539 err := scenarioPipeline1.Job("some-job").SaveNextInputMapping(inputVersions, true) 1540 Expect(err).NotTo(HaveOccurred()) 1541 1542 pipeline2InputVersions := db.InputMapping{ 1543 "some-input-3": db.InputResult{ 1544 Input: &db.AlgorithmInput{ 1545 AlgorithmVersion: db.AlgorithmVersion{ 1546 Version: db.ResourceVersion(convertToMD5(versions[2].Version)), 1547 ResourceID: scenarioPipeline2.Resource("some-resource").ID(), 1548 }, 1549 FirstOccurrence: false, 1550 }, 1551 PassedBuildIDs: []int{}, 1552 }, 1553 } 1554 err = scenarioPipeline2.Job("some-job").SaveNextInputMapping(pipeline2InputVersions, true) 1555 Expect(err).NotTo(HaveOccurred()) 1556 1557 buildInputs := []db.BuildInput{ 1558 { 1559 Name: "some-input-1", 1560 ResourceID: scenarioPipeline1.Resource("some-resource").ID(), 1561 Version: atc.Version{"version": "v1"}, 1562 FirstOccurrence: false, 1563 }, 1564 { 1565 Name: "some-input-2", 1566 ResourceID: scenarioPipeline1.Resource("some-resource").ID(), 1567 Version: atc.Version{"version": "v2"}, 1568 FirstOccurrence: true, 1569 }, 1570 } 1571 1572 actualBuildInputs, found, err := scenarioPipeline1.Job("some-job").GetFullNextBuildInputs() 1573 Expect(err).NotTo(HaveOccurred()) 1574 Expect(found).To(BeTrue()) 1575 1576 Expect(actualBuildInputs).To(ConsistOf(buildInputs)) 1577 1578 By("updating the set of next build inputs") 1579 inputVersions2 := db.InputMapping{ 1580 "some-input-2": db.InputResult{ 1581 Input: &db.AlgorithmInput{ 1582 AlgorithmVersion: db.AlgorithmVersion{ 1583 Version: db.ResourceVersion(convertToMD5(versions[2].Version)), 1584 ResourceID: scenarioPipeline1.Resource("some-resource").ID(), 1585 }, 1586 FirstOccurrence: false, 1587 }, 1588 PassedBuildIDs: []int{}, 1589 }, 1590 "some-input-3": db.InputResult{ 1591 Input: &db.AlgorithmInput{ 1592 AlgorithmVersion: db.AlgorithmVersion{ 1593 Version: db.ResourceVersion(convertToMD5(versions[2].Version)), 1594 ResourceID: scenarioPipeline1.Resource("some-resource").ID(), 1595 }, 1596 FirstOccurrence: true, 1597 }, 1598 PassedBuildIDs: []int{}, 1599 }, 1600 } 1601 err = scenarioPipeline1.Job("some-job").SaveNextInputMapping(inputVersions2, true) 1602 Expect(err).NotTo(HaveOccurred()) 1603 1604 buildInputs2 := []db.BuildInput{ 1605 { 1606 Name: "some-input-2", 1607 ResourceID: scenarioPipeline1.Resource("some-resource").ID(), 1608 Version: atc.Version{"version": "v3"}, 1609 FirstOccurrence: false, 1610 }, 1611 { 1612 Name: "some-input-3", 1613 ResourceID: scenarioPipeline1.Resource("some-resource").ID(), 1614 Version: atc.Version{"version": "v3"}, 1615 FirstOccurrence: true, 1616 }, 1617 } 1618 1619 actualBuildInputs2, found, err := scenarioPipeline1.Job("some-job").GetFullNextBuildInputs() 1620 Expect(err).NotTo(HaveOccurred()) 1621 Expect(found).To(BeTrue()) 1622 1623 Expect(actualBuildInputs2).To(ConsistOf(buildInputs2)) 1624 1625 By("updating next build inputs to an empty set when the mapping is nil") 1626 err = scenarioPipeline1.Job("some-job").SaveNextInputMapping(nil, true) 1627 Expect(err).NotTo(HaveOccurred()) 1628 1629 actualBuildInputs3, found, err := scenarioPipeline1.Job("some-job").GetFullNextBuildInputs() 1630 Expect(err).NotTo(HaveOccurred()) 1631 Expect(found).To(BeTrue()) 1632 Expect(actualBuildInputs3).To(BeEmpty()) 1633 }) 1634 1635 It("distinguishes between a job with no inputs and a job with missing inputs", func() { 1636 By("initially returning not found") 1637 _, found, err := scenarioPipeline1.Job("some-job").GetFullNextBuildInputs() 1638 Expect(err).NotTo(HaveOccurred()) 1639 Expect(found).To(BeFalse()) 1640 1641 By("returning found when an empty input mapping is saved") 1642 err = scenarioPipeline1.Job("some-job").SaveNextInputMapping(db.InputMapping{}, true) 1643 Expect(err).NotTo(HaveOccurred()) 1644 1645 _, found, err = scenarioPipeline1.Job("some-job").GetFullNextBuildInputs() 1646 Expect(err).NotTo(HaveOccurred()) 1647 Expect(found).To(BeTrue()) 1648 }) 1649 1650 It("does not grab inputs if inputs were not successfully determined", func() { 1651 inputVersions := db.InputMapping{ 1652 "some-input-1": db.InputResult{ 1653 ResolveError: "disaster", 1654 }, 1655 } 1656 err := scenarioPipeline1.Job("some-job").SaveNextInputMapping(inputVersions, false) 1657 Expect(err).NotTo(HaveOccurred()) 1658 1659 _, found, err := scenarioPipeline1.Job("some-job").GetFullNextBuildInputs() 1660 Expect(err).NotTo(HaveOccurred()) 1661 Expect(found).To(BeFalse()) 1662 }) 1663 }) 1664 1665 Describe("a build is created for a job", func() { 1666 var ( 1667 build1DB db.Build 1668 otherPipeline db.Pipeline 1669 otherJob db.Job 1670 ) 1671 1672 BeforeEach(func() { 1673 pipelineConfig := atc.Config{ 1674 Jobs: atc.JobConfigs{ 1675 { 1676 Name: "some-job", 1677 }, 1678 }, 1679 Resources: atc.ResourceConfigs{ 1680 { 1681 Name: "some-other-resource", 1682 Type: "some-type", 1683 }, 1684 }, 1685 } 1686 var err error 1687 otherPipeline, _, err = team.SavePipeline(atc.PipelineRef{Name: "some-other-pipeline"}, pipelineConfig, db.ConfigVersion(1), false) 1688 Expect(err).ToNot(HaveOccurred()) 1689 1690 build1DB, err = job.CreateBuild() 1691 Expect(err).ToNot(HaveOccurred()) 1692 1693 Expect(build1DB.ID()).NotTo(BeZero()) 1694 Expect(build1DB.JobName()).To(Equal("some-job")) 1695 Expect(build1DB.Name()).To(Equal("1")) 1696 Expect(build1DB.Status()).To(Equal(db.BuildStatusPending)) 1697 Expect(build1DB.IsScheduled()).To(BeFalse()) 1698 1699 var found bool 1700 otherJob, found, err = otherPipeline.Job("some-job") 1701 Expect(err).ToNot(HaveOccurred()) 1702 Expect(found).To(BeTrue()) 1703 }) 1704 1705 It("becomes the next pending build for job", func() { 1706 nextPendings, err := job.GetPendingBuilds() 1707 Expect(err).NotTo(HaveOccurred()) 1708 //time.Sleep(10 * time.Hour) 1709 Expect(nextPendings).NotTo(BeEmpty()) 1710 Expect(nextPendings[0].ID()).To(Equal(build1DB.ID())) 1711 }) 1712 1713 Context("and another build for a different pipeline is created with the same job name", func() { 1714 BeforeEach(func() { 1715 otherBuild, err := otherJob.CreateBuild() 1716 Expect(err).NotTo(HaveOccurred()) 1717 1718 Expect(otherBuild.ID()).NotTo(BeZero()) 1719 Expect(otherBuild.JobName()).To(Equal("some-job")) 1720 Expect(otherBuild.Name()).To(Equal("1")) 1721 Expect(otherBuild.Status()).To(Equal(db.BuildStatusPending)) 1722 Expect(otherBuild.IsScheduled()).To(BeFalse()) 1723 }) 1724 1725 It("does not change the next pending build for job", func() { 1726 nextPendingBuilds, err := job.GetPendingBuilds() 1727 Expect(err).NotTo(HaveOccurred()) 1728 Expect(nextPendingBuilds).To(Equal([]db.Build{build1DB})) 1729 }) 1730 }) 1731 1732 Context("when scheduled", func() { 1733 BeforeEach(func() { 1734 var err error 1735 var found bool 1736 found, err = job.ScheduleBuild(build1DB) 1737 Expect(err).NotTo(HaveOccurred()) 1738 Expect(found).To(BeTrue()) 1739 }) 1740 1741 It("remains the next pending build for job", func() { 1742 nextPendingBuilds, err := job.GetPendingBuilds() 1743 Expect(err).NotTo(HaveOccurred()) 1744 Expect(nextPendingBuilds).NotTo(BeEmpty()) 1745 Expect(nextPendingBuilds[0].ID()).To(Equal(build1DB.ID())) 1746 }) 1747 }) 1748 1749 Context("when started", func() { 1750 BeforeEach(func() { 1751 started, err := build1DB.Start(atc.Plan{ID: "some-id"}) 1752 Expect(err).NotTo(HaveOccurred()) 1753 Expect(started).To(BeTrue()) 1754 }) 1755 1756 It("saves the updated status, and the schema and private plan", func() { 1757 found, err := build1DB.Reload() 1758 Expect(err).NotTo(HaveOccurred()) 1759 Expect(found).To(BeTrue()) 1760 Expect(build1DB.Status()).To(Equal(db.BuildStatusStarted)) 1761 Expect(build1DB.Schema()).To(Equal("exec.v2")) 1762 Expect(build1DB.PrivatePlan()).To(Equal(atc.Plan{ID: "some-id"})) 1763 }) 1764 1765 It("saves the build's start time", func() { 1766 found, err := build1DB.Reload() 1767 Expect(err).NotTo(HaveOccurred()) 1768 Expect(found).To(BeTrue()) 1769 Expect(build1DB.StartTime().Unix()).To(BeNumerically("~", time.Now().Unix(), 3)) 1770 }) 1771 }) 1772 1773 Context("when the build finishes", func() { 1774 BeforeEach(func() { 1775 err := build1DB.Finish(db.BuildStatusSucceeded) 1776 Expect(err).NotTo(HaveOccurred()) 1777 }) 1778 1779 It("sets the build's status and end time", func() { 1780 found, err := build1DB.Reload() 1781 Expect(err).NotTo(HaveOccurred()) 1782 Expect(found).To(BeTrue()) 1783 Expect(build1DB.Status()).To(Equal(db.BuildStatusSucceeded)) 1784 Expect(build1DB.EndTime().Unix()).To(BeNumerically("~", time.Now().Unix(), 3)) 1785 }) 1786 }) 1787 1788 Context("and another is created for the same job", func() { 1789 var build2DB db.Build 1790 1791 BeforeEach(func() { 1792 var err error 1793 build2DB, err = job.CreateBuild() 1794 Expect(err).NotTo(HaveOccurred()) 1795 1796 Expect(build2DB.ID()).NotTo(BeZero()) 1797 Expect(build2DB.ID()).NotTo(Equal(build1DB.ID())) 1798 Expect(build2DB.Name()).To(Equal("2")) 1799 Expect(build2DB.Status()).To(Equal(db.BuildStatusPending)) 1800 }) 1801 1802 Describe("the first build", func() { 1803 It("remains the next pending build", func() { 1804 nextPendingBuilds, err := job.GetPendingBuilds() 1805 Expect(err).NotTo(HaveOccurred()) 1806 Expect(nextPendingBuilds).To(HaveLen(2)) 1807 Expect(nextPendingBuilds[0].ID()).To(Equal(build1DB.ID())) 1808 Expect(nextPendingBuilds[1].ID()).To(Equal(build2DB.ID())) 1809 }) 1810 }) 1811 }) 1812 1813 Context("when there is a rerun build created for an old build", func() { 1814 var rerunBuild db.Build 1815 var newBuild db.Build 1816 var newerBuild db.Build 1817 1818 BeforeEach(func() { 1819 var err error 1820 newBuild, err = job.CreateBuild() 1821 Expect(err).NotTo(HaveOccurred()) 1822 1823 newerBuild, err = job.CreateBuild() 1824 Expect(err).NotTo(HaveOccurred()) 1825 1826 err = newBuild.Finish(db.BuildStatusSucceeded) 1827 Expect(err).NotTo(HaveOccurred()) 1828 1829 rerunBuild, err = job.RerunBuild(newBuild) 1830 Expect(err).NotTo(HaveOccurred()) 1831 1832 Expect(rerunBuild.ID()).NotTo(BeZero()) 1833 Expect(rerunBuild.ID()).NotTo(Equal(newBuild.ID())) 1834 Expect(rerunBuild.Name()).To(Equal("2.1")) 1835 Expect(rerunBuild.Status()).To(Equal(db.BuildStatusPending)) 1836 }) 1837 1838 It("orders the builds with regular build first and then rerun of old build", func() { 1839 nextPendingBuilds, err := job.GetPendingBuilds() 1840 Expect(err).NotTo(HaveOccurred()) 1841 Expect(len(nextPendingBuilds)).To(Equal(3)) 1842 Expect(nextPendingBuilds[0].Name()).To(Equal(build1DB.Name())) 1843 Expect(nextPendingBuilds[1].Name()).To(Equal(rerunBuild.Name())) 1844 Expect(nextPendingBuilds[2].Name()).To(Equal(newerBuild.Name())) 1845 }) 1846 }) 1847 1848 Context("when there is a rerun build created for the newest build", func() { 1849 var rerunBuild db.Build 1850 var newBuild db.Build 1851 var newerBuild db.Build 1852 1853 BeforeEach(func() { 1854 var err error 1855 newBuild, err = job.CreateBuild() 1856 Expect(err).NotTo(HaveOccurred()) 1857 1858 rerunBuild, err = job.RerunBuild(newBuild) 1859 Expect(err).NotTo(HaveOccurred()) 1860 1861 newerBuild, err = job.CreateBuild() 1862 Expect(err).NotTo(HaveOccurred()) 1863 1864 Expect(rerunBuild.ID()).NotTo(BeZero()) 1865 Expect(rerunBuild.ID()).NotTo(Equal(newBuild.ID())) 1866 Expect(rerunBuild.Name()).To(Equal("2.1")) 1867 Expect(rerunBuild.Status()).To(Equal(db.BuildStatusPending)) 1868 }) 1869 1870 It("orders the builds with rerun of new build", func() { 1871 nextPendingBuilds, err := job.GetPendingBuilds() 1872 Expect(err).NotTo(HaveOccurred()) 1873 Expect(len(nextPendingBuilds)).To(Equal(4)) 1874 Expect(nextPendingBuilds[0].ID()).To(Equal(build1DB.ID())) 1875 Expect(nextPendingBuilds[1].ID()).To(Equal(newBuild.ID())) 1876 Expect(nextPendingBuilds[2].ID()).To(Equal(rerunBuild.ID())) 1877 Expect(nextPendingBuilds[3].ID()).To(Equal(newerBuild.ID())) 1878 }) 1879 }) 1880 1881 Context("when there are multiple reruns for multiple pending builds", func() { 1882 var rerunBuild db.Build 1883 var rerunBuild2 db.Build 1884 var rerunBuild3 db.Build 1885 var newBuild db.Build 1886 var newerBuild db.Build 1887 1888 BeforeEach(func() { 1889 var err error 1890 newBuild, err = job.CreateBuild() 1891 Expect(err).NotTo(HaveOccurred()) 1892 1893 newerBuild, err = job.CreateBuild() 1894 Expect(err).NotTo(HaveOccurred()) 1895 1896 rerunBuild3, err = job.RerunBuild(newerBuild) 1897 Expect(err).NotTo(HaveOccurred()) 1898 1899 rerunBuild, err = job.RerunBuild(newBuild) 1900 Expect(err).NotTo(HaveOccurred()) 1901 1902 rerunBuild2, err = job.RerunBuild(rerunBuild) 1903 Expect(err).NotTo(HaveOccurred()) 1904 1905 Expect(rerunBuild.ID()).NotTo(BeZero()) 1906 Expect(rerunBuild.ID()).NotTo(Equal(newBuild.ID())) 1907 Expect(rerunBuild.Name()).To(Equal("2.1")) 1908 Expect(rerunBuild.Status()).To(Equal(db.BuildStatusPending)) 1909 }) 1910 1911 It("orders the builds with ascending reruns following original builds", func() { 1912 nextPendingBuilds, err := job.GetPendingBuilds() 1913 Expect(err).NotTo(HaveOccurred()) 1914 Expect(len(nextPendingBuilds)).To(Equal(6)) 1915 Expect(nextPendingBuilds[0].Name()).To(Equal(build1DB.Name())) 1916 Expect(nextPendingBuilds[1].Name()).To(Equal(newBuild.Name())) 1917 Expect(nextPendingBuilds[2].Name()).To(Equal(rerunBuild.Name())) 1918 Expect(nextPendingBuilds[3].Name()).To(Equal(rerunBuild2.Name())) 1919 Expect(nextPendingBuilds[4].Name()).To(Equal(newerBuild.Name())) 1920 Expect(nextPendingBuilds[5].Name()).To(Equal(rerunBuild3.Name())) 1921 }) 1922 }) 1923 }) 1924 1925 Describe("EnsurePendingBuildExists", func() { 1926 Context("when only a started build exists", func() { 1927 It("creates a build and updates the next build for the job", func() { 1928 err := job.EnsurePendingBuildExists(context.TODO()) 1929 Expect(err).NotTo(HaveOccurred()) 1930 1931 pendingBuilds, err := job.GetPendingBuilds() 1932 Expect(err).NotTo(HaveOccurred()) 1933 Expect(pendingBuilds).To(HaveLen(1)) 1934 1935 _, nextBuild, err := job.FinishedAndNextBuild() 1936 Expect(err).NotTo(HaveOccurred()) 1937 Expect(pendingBuilds[0].ID()).To(Equal(nextBuild.ID())) 1938 }) 1939 1940 Context("when tracing is configured", func() { 1941 BeforeEach(func() { 1942 tracing.ConfigureTraceProvider(tracetest.NewProvider()) 1943 }) 1944 1945 AfterEach(func() { 1946 tracing.Configured = false 1947 }) 1948 1949 It("propagates span context", func() { 1950 ctx, span := tracing.StartSpan(context.Background(), "fake-operation", nil) 1951 traceID := span.SpanContext().TraceID.String() 1952 1953 job.EnsurePendingBuildExists(ctx) 1954 1955 pendingBuilds, _ := job.GetPendingBuilds() 1956 spanContext := pendingBuilds[0].SpanContext() 1957 traceParent := spanContext.Get("traceparent") 1958 Expect(traceParent).To(ContainSubstring(traceID)) 1959 }) 1960 }) 1961 1962 It("doesn't create another build the second time it's called", func() { 1963 err := job.EnsurePendingBuildExists(context.TODO()) 1964 Expect(err).NotTo(HaveOccurred()) 1965 1966 err = job.EnsurePendingBuildExists(context.TODO()) 1967 Expect(err).NotTo(HaveOccurred()) 1968 1969 builds2, err := job.GetPendingBuilds() 1970 Expect(err).NotTo(HaveOccurred()) 1971 Expect(builds2).To(HaveLen(1)) 1972 1973 started, err := builds2[0].Start(atc.Plan{}) 1974 Expect(err).NotTo(HaveOccurred()) 1975 Expect(started).To(BeTrue()) 1976 1977 builds2, err = job.GetPendingBuilds() 1978 Expect(err).NotTo(HaveOccurred()) 1979 Expect(builds2).To(HaveLen(0)) 1980 }) 1981 }) 1982 }) 1983 1984 Describe("Clear task cache", func() { 1985 Context("when task cache exists", func() { 1986 var ( 1987 someOtherJob db.Job 1988 rowsDeleted int64 1989 ) 1990 1991 BeforeEach(func() { 1992 var ( 1993 err error 1994 found bool 1995 ) 1996 1997 usedTaskCache, err := taskCacheFactory.FindOrCreate(job.ID(), "some-task", "some-path") 1998 Expect(err).ToNot(HaveOccurred()) 1999 2000 _, err = workerTaskCacheFactory.FindOrCreate(db.WorkerTaskCache{ 2001 TaskCache: usedTaskCache, 2002 WorkerName: defaultWorker.Name(), 2003 }) 2004 Expect(err).ToNot(HaveOccurred()) 2005 2006 someOtherJob, found, err = pipeline.Job("some-other-job") 2007 Expect(err).NotTo(HaveOccurred()) 2008 Expect(found).To(BeTrue()) 2009 Expect(someOtherJob).ToNot(BeNil()) 2010 2011 otherUsedTaskCache, err := taskCacheFactory.FindOrCreate(someOtherJob.ID(), "some-other-task", "some-other-path") 2012 Expect(err).ToNot(HaveOccurred()) 2013 2014 _, err = workerTaskCacheFactory.FindOrCreate(db.WorkerTaskCache{ 2015 TaskCache: otherUsedTaskCache, 2016 WorkerName: defaultWorker.Name(), 2017 }) 2018 Expect(err).ToNot(HaveOccurred()) 2019 2020 }) 2021 2022 Context("when a path is provided", func() { 2023 BeforeEach(func() { 2024 var err error 2025 rowsDeleted, err = job.ClearTaskCache("some-task", "some-path") 2026 Expect(err).NotTo(HaveOccurred()) 2027 }) 2028 2029 It("deletes a row from the task_caches table", func() { 2030 Expect(rowsDeleted).To(Equal(int64(1))) 2031 }) 2032 2033 It("removes the task cache", func() { 2034 usedTaskCache, found, err := taskCacheFactory.Find(job.ID(), "some-task", "some-path") 2035 Expect(err).ToNot(HaveOccurred()) 2036 Expect(usedTaskCache).To(BeNil()) 2037 Expect(found).To(BeFalse()) 2038 }) 2039 2040 It("doesn't remove other jobs caches", func() { 2041 otherUsedTaskCache, found, err := taskCacheFactory.Find(someOtherJob.ID(), "some-other-task", "some-other-path") 2042 Expect(err).ToNot(HaveOccurred()) 2043 Expect(found).To(BeTrue()) 2044 Expect(err).ToNot(HaveOccurred()) 2045 2046 _, err = workerTaskCacheFactory.FindOrCreate(db.WorkerTaskCache{ 2047 TaskCache: otherUsedTaskCache, 2048 WorkerName: defaultWorker.Name(), 2049 }) 2050 Expect(err).ToNot(HaveOccurred()) 2051 }) 2052 2053 Context("but the cache path doesn't exist", func() { 2054 BeforeEach(func() { 2055 var err error 2056 rowsDeleted, err = job.ClearTaskCache("some-task", "some-nonexistent-path") 2057 Expect(err).NotTo(HaveOccurred()) 2058 2059 }) 2060 It("deletes 0 rows", func() { 2061 Expect(rowsDeleted).To(Equal(int64(0))) 2062 }) 2063 }) 2064 }) 2065 2066 Context("when a path is not provided", func() { 2067 Context("when a non-existent step-name is provided", func() { 2068 BeforeEach(func() { 2069 var err error 2070 rowsDeleted, err = job.ClearTaskCache("some-nonexistent-task", "") 2071 Expect(err).NotTo(HaveOccurred()) 2072 }) 2073 2074 It("does not delete any rows from the task_caches table", func() { 2075 Expect(rowsDeleted).To(BeZero()) 2076 }) 2077 2078 It("should not delete any task steps", func() { 2079 usedTaskCache, found, err := taskCacheFactory.Find(job.ID(), "some-task", "some-path") 2080 Expect(err).ToNot(HaveOccurred()) 2081 Expect(found).To(BeTrue()) 2082 Expect(err).ToNot(HaveOccurred()) 2083 2084 _, found, err = workerTaskCacheFactory.Find(db.WorkerTaskCache{ 2085 TaskCache: usedTaskCache, 2086 WorkerName: defaultWorker.Name(), 2087 }) 2088 Expect(found).To(BeTrue()) 2089 Expect(err).ToNot(HaveOccurred()) 2090 }) 2091 2092 }) 2093 2094 Context("when an existing step-name is provided", func() { 2095 BeforeEach(func() { 2096 var err error 2097 rowsDeleted, err = job.ClearTaskCache("some-task", "") 2098 Expect(err).NotTo(HaveOccurred()) 2099 }) 2100 2101 It("deletes a row from the task_caches table", func() { 2102 Expect(rowsDeleted).To(Equal(int64(1))) 2103 }) 2104 2105 It("removes the task cache", func() { 2106 _, found, err := taskCacheFactory.Find(job.ID(), "some-task", "some-path") 2107 Expect(found).To(BeFalse()) 2108 Expect(err).ToNot(HaveOccurred()) 2109 }) 2110 2111 It("doesn't remove other jobs caches", func() { 2112 _, found, err := taskCacheFactory.Find(someOtherJob.ID(), "some-other-task", "some-other-path") 2113 Expect(found).To(BeTrue()) 2114 Expect(err).ToNot(HaveOccurred()) 2115 }) 2116 }) 2117 }) 2118 }) 2119 }) 2120 2121 Describe("New Inputs", func() { 2122 It("starts out as false", func() { 2123 Expect(job.HasNewInputs()).To(BeFalse()) 2124 }) 2125 2126 It("can be set to true then back to false", func() { 2127 job.SetHasNewInputs(true) 2128 2129 found, err := job.Reload() 2130 2131 Expect(err).NotTo(HaveOccurred()) 2132 Expect(found).To(BeTrue()) 2133 2134 Expect(job.HasNewInputs()).To(BeTrue()) 2135 2136 job.SetHasNewInputs(false) 2137 2138 found, err = job.Reload() 2139 2140 Expect(err).NotTo(HaveOccurred()) 2141 Expect(found).To(BeTrue()) 2142 2143 Expect(job.HasNewInputs()).To(BeFalse()) 2144 }) 2145 }) 2146 2147 Describe("AlgorithmInputs", func() { 2148 var scenario *dbtest.Scenario 2149 var inputs db.InputConfigs 2150 2151 JustBeforeEach(func() { 2152 var err error 2153 inputs, err = scenario.Job("some-job").AlgorithmInputs() 2154 Expect(err).ToNot(HaveOccurred()) 2155 }) 2156 2157 Context("when there is an input configured for the job", func() { 2158 BeforeEach(func() { 2159 scenario = dbtest.Setup( 2160 builder.WithPipeline(atc.Config{ 2161 Jobs: atc.JobConfigs{ 2162 { 2163 Name: "some-job", 2164 PlanSequence: []atc.Step{ 2165 { 2166 Config: &atc.GetStep{ 2167 Name: "some-input", 2168 Resource: "some-resource", 2169 Params: atc.Params{ 2170 "some-param": "some-value", 2171 }, 2172 Passed: []string{"job-1", "job-2"}, 2173 Trigger: true, 2174 Version: &atc.VersionConfig{Every: true}, 2175 }, 2176 }, 2177 }, 2178 }, 2179 { 2180 Name: "job-1", 2181 }, 2182 { 2183 Name: "job-2", 2184 }, 2185 }, 2186 Resources: atc.ResourceConfigs{ 2187 { 2188 Name: "some-resource", 2189 Type: "some-type", 2190 }, 2191 }, 2192 }), 2193 ) 2194 }) 2195 2196 It("returns the input for the job", func() { 2197 Expect(inputs).To(Equal(db.InputConfigs{ 2198 { 2199 Name: "some-input", 2200 JobID: scenario.Job("some-job").ID(), 2201 ResourceID: scenario.Resource("some-resource").ID(), 2202 Passed: db.JobSet{ 2203 scenario.Job("job-1").ID(): true, 2204 scenario.Job("job-2").ID(): true, 2205 }, 2206 UseEveryVersion: true, 2207 Trigger: true, 2208 }, 2209 })) 2210 }) 2211 }) 2212 2213 Context("when the input is pinned through the get step", func() { 2214 BeforeEach(func() { 2215 scenario = dbtest.Setup( 2216 builder.WithPipeline(atc.Config{ 2217 Jobs: atc.JobConfigs{ 2218 { 2219 Name: "some-job", 2220 PlanSequence: []atc.Step{ 2221 { 2222 Config: &atc.GetStep{ 2223 Name: "some-pinned-input", 2224 Resource: "some-resource", 2225 Version: &atc.VersionConfig{Pinned: atc.Version{"input": "pinned"}}, 2226 }, 2227 }, 2228 }, 2229 }, 2230 }, 2231 Resources: atc.ResourceConfigs{ 2232 { 2233 Name: "some-resource", 2234 Type: "some-base-resource-type", 2235 Source: atc.Source{"some": "source"}, 2236 }, 2237 }, 2238 }), 2239 ) 2240 }) 2241 2242 It("pins the inputs to that version", func() { 2243 Expect(inputs).To(Equal(db.InputConfigs{ 2244 { 2245 Name: "some-pinned-input", 2246 JobID: scenario.Job("some-job").ID(), 2247 ResourceID: scenario.Resource("some-resource").ID(), 2248 PinnedVersion: atc.Version{"input": "pinned"}, 2249 }, 2250 })) 2251 }) 2252 2253 Context("when the input is also pinned through the api", func() { 2254 BeforeEach(func() { 2255 scenario.Run( 2256 builder.WithPinnedVersion("some-resource", atc.Version{"api": "pinned"}), 2257 ) 2258 }) 2259 2260 It("resolves the pinned version to the version pinned through the get step", func() { 2261 Expect(inputs).To(Equal(db.InputConfigs{ 2262 { 2263 Name: "some-pinned-input", 2264 JobID: scenario.Job("some-job").ID(), 2265 ResourceID: scenario.Resource("some-resource").ID(), 2266 PinnedVersion: atc.Version{"input": "pinned"}, 2267 }, 2268 })) 2269 }) 2270 }) 2271 }) 2272 2273 Context("when the input is pinned through the resource config", func() { 2274 BeforeEach(func() { 2275 scenario = dbtest.Setup( 2276 builder.WithPipeline(atc.Config{ 2277 Jobs: atc.JobConfigs{ 2278 { 2279 Name: "some-job", 2280 PlanSequence: []atc.Step{ 2281 { 2282 Config: &atc.GetStep{ 2283 Name: "some-pinned-input", 2284 Resource: "some-resource", 2285 }, 2286 }, 2287 }, 2288 }, 2289 }, 2290 Resources: atc.ResourceConfigs{ 2291 { 2292 Name: "some-resource", 2293 Type: "some-type", 2294 Source: atc.Source{"some": "source"}, 2295 Version: atc.Version{"some": "version"}, 2296 }, 2297 }, 2298 }), 2299 ) 2300 }) 2301 2302 It("pins the inputs to that version", func() { 2303 Expect(inputs).To(Equal(db.InputConfigs{ 2304 { 2305 Name: "some-pinned-input", 2306 JobID: scenario.Job("some-job").ID(), 2307 ResourceID: scenario.Resource("some-resource").ID(), 2308 PinnedVersion: atc.Version{"some": "version"}, 2309 }, 2310 })) 2311 }) 2312 }) 2313 2314 Context("when the input is pinned through the api", func() { 2315 BeforeEach(func() { 2316 scenario = dbtest.Setup( 2317 builder.WithPipeline(atc.Config{ 2318 Jobs: atc.JobConfigs{ 2319 { 2320 Name: "some-job", 2321 PlanSequence: []atc.Step{ 2322 { 2323 Config: &atc.GetStep{ 2324 Name: "some-pinned-input", 2325 Resource: "some-resource", 2326 }, 2327 }, 2328 }, 2329 }, 2330 }, 2331 Resources: atc.ResourceConfigs{ 2332 { 2333 Name: "some-resource", 2334 Type: "some-base-resource-type", 2335 Source: atc.Source{"some": "source"}, 2336 }, 2337 }, 2338 }), 2339 builder.WithPinnedVersion("some-resource", atc.Version{"some": "version"}), 2340 ) 2341 }) 2342 2343 It("pins the inputs to that version", func() { 2344 Expect(inputs).To(Equal(db.InputConfigs{ 2345 { 2346 Name: "some-pinned-input", 2347 JobID: scenario.Job("some-job").ID(), 2348 ResourceID: scenario.Resource("some-resource").ID(), 2349 PinnedVersion: atc.Version{"some": "version"}, 2350 }, 2351 })) 2352 }) 2353 }) 2354 2355 Context("when there are multiple inputs", func() { 2356 BeforeEach(func() { 2357 scenario = dbtest.Setup( 2358 builder.WithPipeline(atc.Config{ 2359 Jobs: atc.JobConfigs{ 2360 { 2361 Name: "some-job", 2362 PlanSequence: []atc.Step{ 2363 { 2364 Config: &atc.GetStep{ 2365 Name: "some-input", 2366 Resource: "some-resource", 2367 Trigger: true, 2368 Version: &atc.VersionConfig{Every: true}, 2369 }, 2370 }, 2371 { 2372 Config: &atc.GetStep{ 2373 Name: "some-resource", 2374 }, 2375 }, 2376 { 2377 Config: &atc.GetStep{ 2378 Name: "some-other-resource", 2379 Trigger: true, 2380 Version: &atc.VersionConfig{Latest: true}, 2381 }, 2382 }, 2383 }, 2384 }, 2385 { 2386 Name: "some-other-job", 2387 PlanSequence: []atc.Step{ 2388 { 2389 Config: &atc.GetStep{ 2390 Name: "other-job-resource", 2391 Resource: "some-resource", 2392 }, 2393 }, 2394 }, 2395 }, 2396 }, 2397 Resources: atc.ResourceConfigs{ 2398 { 2399 Name: "some-resource", 2400 Type: "some-type", 2401 }, 2402 { 2403 Name: "some-other-resource", 2404 Type: "some-type", 2405 }, 2406 }, 2407 }), 2408 ) 2409 }) 2410 2411 It("returns all the inputs correctly", func() { 2412 Expect(inputs).To(HaveLen(3)) 2413 Expect(inputs).To(ConsistOf( 2414 db.InputConfig{ 2415 Name: "some-input", 2416 JobID: scenario.Job("some-job").ID(), 2417 ResourceID: scenario.Resource("some-resource").ID(), 2418 UseEveryVersion: true, 2419 Trigger: true, 2420 }, 2421 db.InputConfig{ 2422 Name: "some-resource", 2423 JobID: scenario.Job("some-job").ID(), 2424 ResourceID: scenario.Resource("some-resource").ID(), 2425 }, 2426 db.InputConfig{ 2427 Name: "some-other-resource", 2428 JobID: scenario.Job("some-job").ID(), 2429 ResourceID: scenario.Resource("some-other-resource").ID(), 2430 Trigger: true, 2431 })) 2432 }) 2433 }) 2434 2435 Context("when the job has puts and tasks", func() { 2436 BeforeEach(func() { 2437 scenario = dbtest.Setup( 2438 builder.WithPipeline(atc.Config{ 2439 Jobs: atc.JobConfigs{ 2440 { 2441 Name: "some-job", 2442 PlanSequence: []atc.Step{ 2443 { 2444 Config: &atc.PutStep{ 2445 Name: "some-resource", 2446 }, 2447 }, 2448 { 2449 Config: &atc.TaskStep{ 2450 Name: "some-task", 2451 Privileged: true, 2452 ConfigPath: "some/config/path.yml", 2453 Config: &atc.TaskConfig{ 2454 RootfsURI: "some-image", 2455 }, 2456 }, 2457 }, 2458 { 2459 Config: &atc.GetStep{ 2460 Name: "some-resource", 2461 }, 2462 }, 2463 }, 2464 }, 2465 }, 2466 Resources: atc.ResourceConfigs{ 2467 { 2468 Name: "some-resource", 2469 Type: "some-type", 2470 }, 2471 }, 2472 }), 2473 ) 2474 }) 2475 2476 It("only returns the gets (inputs to the job)", func() { 2477 Expect(inputs).To(Equal(db.InputConfigs{ 2478 { 2479 Name: "some-resource", 2480 JobID: scenario.Job("some-job").ID(), 2481 ResourceID: scenario.Resource("some-resource").ID(), 2482 }, 2483 })) 2484 }) 2485 }) 2486 }) 2487 2488 Describe("Inputs", func() { 2489 var inputsJob db.Job 2490 2491 BeforeEach(func() { 2492 inputsPipeline, _, err := team.SavePipeline(atc.PipelineRef{Name: "inputs-pipeline"}, atc.Config{ 2493 Jobs: atc.JobConfigs{ 2494 { 2495 Name: "some-job", 2496 PlanSequence: []atc.Step{ 2497 { 2498 Config: &atc.PutStep{ 2499 Name: "some-resource", 2500 }, 2501 }, 2502 { 2503 Config: &atc.GetStep{ 2504 Name: "some-input", 2505 Resource: "some-resource", 2506 Params: atc.Params{ 2507 "some-param": "some-value", 2508 }, 2509 Passed: []string{"job-1", "job-2"}, 2510 Trigger: true, 2511 Version: &atc.VersionConfig{Every: true}, 2512 }, 2513 }, 2514 { 2515 Config: &atc.TaskStep{ 2516 Name: "some-task", 2517 Privileged: true, 2518 ConfigPath: "some/config/path.yml", 2519 Config: &atc.TaskConfig{ 2520 RootfsURI: "some-image", 2521 }, 2522 }, 2523 }, 2524 { 2525 Config: &atc.GetStep{ 2526 Name: "some-resource", 2527 }, 2528 }, 2529 { 2530 Config: &atc.GetStep{ 2531 Name: "some-other-input", 2532 Resource: "some-resource", 2533 Version: &atc.VersionConfig{Latest: true}, 2534 }, 2535 }, 2536 { 2537 Config: &atc.GetStep{ 2538 Name: "some-other-resource", 2539 Trigger: true, 2540 Version: &atc.VersionConfig{Pinned: atc.Version{"pinned": "version"}}, 2541 }, 2542 }, 2543 }, 2544 }, 2545 { 2546 Name: "some-other-job", 2547 PlanSequence: []atc.Step{ 2548 { 2549 Config: &atc.GetStep{ 2550 Name: "other-job-resource", 2551 Resource: "some-resource", 2552 }, 2553 }, 2554 }, 2555 }, 2556 { 2557 Name: "job-1", 2558 }, 2559 { 2560 Name: "job-2", 2561 }, 2562 }, 2563 Resources: atc.ResourceConfigs{ 2564 { 2565 Name: "some-resource", 2566 Type: "some-type", 2567 }, 2568 { 2569 Name: "some-other-resource", 2570 Type: "some-type", 2571 }, 2572 }, 2573 }, db.ConfigVersion(0), false) 2574 Expect(err).ToNot(HaveOccurred()) 2575 2576 var found bool 2577 inputsJob, found, err = inputsPipeline.Job("some-job") 2578 Expect(err).ToNot(HaveOccurred()) 2579 Expect(found).To(BeTrue()) 2580 }) 2581 2582 It("returns inputs for the job", func() { 2583 inputs, err := inputsJob.Inputs() 2584 Expect(err).ToNot(HaveOccurred()) 2585 2586 Expect(inputs).To(Equal([]atc.JobInput{ 2587 { 2588 Name: "some-input", 2589 Resource: "some-resource", 2590 Passed: []string{"job-1", "job-2"}, 2591 Trigger: true, 2592 Version: &atc.VersionConfig{Every: true}, 2593 }, 2594 { 2595 Name: "some-other-input", 2596 Resource: "some-resource", 2597 Version: &atc.VersionConfig{Latest: true}, 2598 }, 2599 { 2600 Name: "some-other-resource", 2601 Resource: "some-other-resource", 2602 Trigger: true, 2603 Version: &atc.VersionConfig{Pinned: atc.Version{"pinned": "version"}}, 2604 }, 2605 { 2606 Name: "some-resource", 2607 Resource: "some-resource", 2608 }, 2609 })) 2610 }) 2611 }) 2612 2613 Describe("Outputs", func() { 2614 var outputsJob db.Job 2615 2616 BeforeEach(func() { 2617 outputsPipeline, _, err := team.SavePipeline(atc.PipelineRef{Name: "outputs-pipeline"}, atc.Config{ 2618 Jobs: atc.JobConfigs{ 2619 { 2620 Name: "some-job", 2621 PlanSequence: []atc.Step{ 2622 { 2623 Config: &atc.PutStep{ 2624 Name: "some-other-resource", 2625 }, 2626 }, 2627 { 2628 Config: &atc.TaskStep{ 2629 Name: "some-task", 2630 Privileged: true, 2631 ConfigPath: "some/config/path.yml", 2632 Config: &atc.TaskConfig{ 2633 RootfsURI: "some-image", 2634 }, 2635 }, 2636 }, 2637 { 2638 Config: &atc.GetStep{ 2639 Name: "some-resource", 2640 }, 2641 }, 2642 { 2643 Config: &atc.PutStep{ 2644 Name: "some-output", 2645 Resource: "some-resource", 2646 }, 2647 }, 2648 { 2649 Config: &atc.PutStep{ 2650 Name: "some-other-output", 2651 Resource: "some-resource", 2652 }, 2653 }, 2654 }, 2655 }, 2656 { 2657 Name: "some-other-job", 2658 PlanSequence: []atc.Step{ 2659 { 2660 Config: &atc.PutStep{ 2661 Name: "other-job-resource", 2662 Resource: "some-resource", 2663 }, 2664 }, 2665 }, 2666 }, 2667 }, 2668 Resources: atc.ResourceConfigs{ 2669 { 2670 Name: "some-resource", 2671 Type: "some-type", 2672 }, 2673 { 2674 Name: "some-other-resource", 2675 Type: "some-type", 2676 }, 2677 }, 2678 }, db.ConfigVersion(0), false) 2679 Expect(err).ToNot(HaveOccurred()) 2680 2681 var found bool 2682 outputsJob, found, err = outputsPipeline.Job("some-job") 2683 Expect(err).ToNot(HaveOccurred()) 2684 Expect(found).To(BeTrue()) 2685 }) 2686 2687 It("returns outputs for the job", func() { 2688 outputs, err := outputsJob.Outputs() 2689 Expect(err).ToNot(HaveOccurred()) 2690 2691 Expect(outputs).To(Equal([]atc.JobOutput{ 2692 { 2693 Name: "some-other-output", 2694 Resource: "some-resource", 2695 }, 2696 { 2697 Name: "some-other-resource", 2698 Resource: "some-other-resource", 2699 }, 2700 { 2701 Name: "some-output", 2702 Resource: "some-resource", 2703 }, 2704 })) 2705 }) 2706 }) 2707 })