github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/db/resource_test.go (about) 1 package db_test 2 3 import ( 4 "context" 5 "strconv" 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" 15 "go.opentelemetry.io/otel/api/trace/tracetest" 16 ) 17 18 var _ = Describe("Resource", func() { 19 var pipeline db.Pipeline 20 21 BeforeEach(func() { 22 var ( 23 created bool 24 err error 25 ) 26 27 pipeline, created, err = defaultTeam.SavePipeline( 28 atc.PipelineRef{Name: "pipeline-with-resources"}, 29 atc.Config{ 30 Resources: atc.ResourceConfigs{ 31 { 32 Name: "some-resource", 33 Type: "registry-image", 34 WebhookToken: "some-token", 35 Source: atc.Source{"some": "repository"}, 36 Version: atc.Version{"ref": "abcdef"}, 37 CheckTimeout: "999m", 38 }, 39 { 40 Name: "some-other-resource", 41 Public: true, 42 Type: "git", 43 Source: atc.Source{"some": "other-repository"}, 44 }, 45 { 46 Name: "some-secret-resource", 47 Public: false, 48 Type: "git", 49 Source: atc.Source{"some": "((secret-repository))"}, 50 }, 51 { 52 Name: "some-resource-custom-check", 53 Type: "git", 54 Source: atc.Source{"some": "some-repository"}, 55 CheckEvery: "10ms", 56 CheckTimeout: "1m", 57 }, 58 }, 59 Jobs: atc.JobConfigs{ 60 { 61 Name: "job-using-resource", 62 PlanSequence: []atc.Step{ 63 { 64 Config: &atc.GetStep{ 65 Name: "some-other-resource", 66 }, 67 }, 68 }, 69 }, 70 { 71 Name: "not-using-resource", 72 }, 73 }, 74 }, 75 0, 76 false, 77 ) 78 Expect(err).ToNot(HaveOccurred()) 79 Expect(created).To(BeTrue()) 80 }) 81 82 Describe("(Pipeline).Resources", func() { 83 var resources []db.Resource 84 85 JustBeforeEach(func() { 86 var err error 87 resources, err = pipeline.Resources() 88 Expect(err).ToNot(HaveOccurred()) 89 }) 90 91 It("returns the resources", func() { 92 Expect(resources).To(HaveLen(4)) 93 94 ids := map[int]struct{}{} 95 96 for _, r := range resources { 97 ids[r.ID()] = struct{}{} 98 99 switch r.Name() { 100 case "some-resource": 101 Expect(r.Type()).To(Equal("registry-image")) 102 Expect(r.Source()).To(Equal(atc.Source{"some": "repository"})) 103 Expect(r.ConfigPinnedVersion()).To(Equal(atc.Version{"ref": "abcdef"})) 104 Expect(r.CurrentPinnedVersion()).To(Equal(r.ConfigPinnedVersion())) 105 Expect(r.HasWebhook()).To(BeTrue()) 106 case "some-other-resource": 107 Expect(r.Type()).To(Equal("git")) 108 Expect(r.Source()).To(Equal(atc.Source{"some": "other-repository"})) 109 Expect(r.HasWebhook()).To(BeFalse()) 110 case "some-secret-resource": 111 Expect(r.Type()).To(Equal("git")) 112 Expect(r.Source()).To(Equal(atc.Source{"some": "((secret-repository))"})) 113 Expect(r.HasWebhook()).To(BeFalse()) 114 case "some-resource-custom-check": 115 Expect(r.Type()).To(Equal("git")) 116 Expect(r.Source()).To(Equal(atc.Source{"some": "some-repository"})) 117 Expect(r.CheckEvery()).To(Equal("10ms")) 118 Expect(r.CheckTimeout()).To(Equal("1m")) 119 Expect(r.HasWebhook()).To(BeFalse()) 120 } 121 } 122 }) 123 }) 124 125 Describe("(Pipeline).Resource", func() { 126 var ( 127 scenario *dbtest.Scenario 128 ) 129 130 Context("when the resource exists", func() { 131 BeforeEach(func() { 132 scenario = dbtest.Setup( 133 builder.WithPipeline(atc.Config{ 134 Resources: atc.ResourceConfigs{ 135 { 136 Name: "some-resource", 137 Type: "some-base-resource-type", 138 Source: atc.Source{"some": "repository"}, 139 }, 140 }, 141 }), 142 ) 143 }) 144 145 It("returns the resource", func() { 146 Expect(scenario.Resource("some-resource").Name()).To(Equal("some-resource")) 147 Expect(scenario.Resource("some-resource").Type()).To(Equal("some-base-resource-type")) 148 Expect(scenario.Resource("some-resource").Source()).To(Equal(atc.Source{"some": "repository"})) 149 }) 150 }) 151 152 Context("when the resource does not exist", func() { 153 var resource db.Resource 154 var found bool 155 var err error 156 157 BeforeEach(func() { 158 scenario = dbtest.Setup( 159 builder.WithPipeline(atc.Config{ 160 Resources: atc.ResourceConfigs{ 161 { 162 Name: "some-resource", 163 Type: "some-base-resource-type", 164 Source: atc.Source{"some": "repository"}, 165 }, 166 }, 167 }), 168 ) 169 170 resource, found, err = scenario.Pipeline.Resource("bonkers") 171 Expect(err).ToNot(HaveOccurred()) 172 }) 173 174 It("returns nil", func() { 175 Expect(found).To(BeFalse()) 176 Expect(resource).To(BeNil()) 177 }) 178 }) 179 }) 180 181 Describe("Enable/Disable Version", func() { 182 var scenario *dbtest.Scenario 183 184 BeforeEach(func() { 185 scenario = dbtest.Setup( 186 builder.WithPipeline(atc.Config{ 187 Jobs: atc.JobConfigs{ 188 { 189 Name: "job-using-resource", 190 PlanSequence: []atc.Step{ 191 { 192 Config: &atc.GetStep{ 193 Name: "some-other-resource", 194 }, 195 }, 196 }, 197 }, 198 { 199 Name: "not-using-resource", 200 }, 201 }, 202 Resources: atc.ResourceConfigs{ 203 { 204 Name: "some-other-resource", 205 Type: "some-base-resource-type", 206 Source: atc.Source{"some": "other-repository"}, 207 }, 208 }, 209 }), 210 builder.WithResourceVersions("some-other-resource", atc.Version{"disabled": "version"}), 211 ) 212 }) 213 214 Context("when disabling a version that exists", func() { 215 var disableErr error 216 var requestedSchedule1 time.Time 217 var requestedSchedule2 time.Time 218 219 BeforeEach(func() { 220 requestedSchedule1 = scenario.Job("job-using-resource").ScheduleRequestedTime() 221 requestedSchedule2 = scenario.Job("not-using-resource").ScheduleRequestedTime() 222 223 scenario.Run(builder.WithDisabledVersion("some-other-resource", atc.Version{"disabled": "version"})) 224 }) 225 226 It("successfully disables the version", func() { 227 Expect(disableErr).ToNot(HaveOccurred()) 228 229 versions, _, found, err := scenario.Resource("some-other-resource").Versions(db.Page{Limit: 3}, nil) 230 Expect(err).ToNot(HaveOccurred()) 231 Expect(found).To(BeTrue()) 232 Expect(versions).To(HaveLen(1)) 233 Expect(versions[0].Version).To(Equal(atc.Version{"disabled": "version"})) 234 Expect(versions[0].Enabled).To(BeFalse()) 235 }) 236 237 It("requests schedule on the jobs using that resource", func() { 238 found, err := scenario.Job("job-using-resource").Reload() 239 Expect(err).NotTo(HaveOccurred()) 240 Expect(found).To(BeTrue()) 241 242 Expect(scenario.Job("job-using-resource").ScheduleRequestedTime()).Should(BeTemporally(">", requestedSchedule1)) 243 }) 244 245 It("does not request schedule on jobs that do not use the resource", func() { 246 found, err := scenario.Job("not-using-resource").Reload() 247 Expect(err).NotTo(HaveOccurred()) 248 Expect(found).To(BeTrue()) 249 250 Expect(scenario.Job("not-using-resource").ScheduleRequestedTime()).Should(BeTemporally("==", requestedSchedule2)) 251 }) 252 253 Context("when enabling that version", func() { 254 BeforeEach(func() { 255 scenario.Run(builder.WithEnabledVersion("some-other-resource", atc.Version{"disabled": "version"})) 256 }) 257 258 It("successfully enables the version", func() { 259 versions, _, found, err := scenario.Resource("some-other-resource").Versions(db.Page{Limit: 3}, nil) 260 Expect(err).ToNot(HaveOccurred()) 261 Expect(found).To(BeTrue()) 262 Expect(versions).To(HaveLen(1)) 263 Expect(versions[0].Version).To(Equal(atc.Version{"disabled": "version"})) 264 Expect(versions[0].Enabled).To(BeTrue()) 265 }) 266 267 It("request schedule on the jobs using that resource", func() { 268 found, err := scenario.Job("job-using-resource").Reload() 269 Expect(err).NotTo(HaveOccurred()) 270 Expect(found).To(BeTrue()) 271 272 Expect(scenario.Job("job-using-resource").ScheduleRequestedTime()).Should(BeTemporally(">", requestedSchedule1)) 273 }) 274 275 It("does not request schedule on jobs that do not use the resource", func() { 276 found, err := scenario.Job("not-using-resource").Reload() 277 Expect(err).NotTo(HaveOccurred()) 278 Expect(found).To(BeTrue()) 279 280 Expect(scenario.Job("not-using-resource").ScheduleRequestedTime()).Should(BeTemporally("==", requestedSchedule2)) 281 }) 282 }) 283 }) 284 285 Context("when disabling version that does not exist", func() { 286 var disableErr error 287 BeforeEach(func() { 288 resource, found, err := scenario.Pipeline.Resource("some-other-resource") 289 Expect(err).ToNot(HaveOccurred()) 290 Expect(found).To(BeTrue()) 291 292 disableErr = resource.DisableVersion(123456) 293 }) 294 295 It("returns an error", func() { 296 Expect(disableErr).To(HaveOccurred()) 297 }) 298 }) 299 300 Context("when enabling a version that is already enabled", func() { 301 var enableError error 302 BeforeEach(func() { 303 resource, found, err := scenario.Pipeline.Resource("some-other-resource") 304 Expect(err).ToNot(HaveOccurred()) 305 Expect(found).To(BeTrue()) 306 307 enableError = resource.EnableVersion(scenario.ResourceVersion("some-other-resource", atc.Version{"disabled": "version"}).ID()) 308 }) 309 310 It("returns a non one row affected error", func() { 311 Expect(enableError).To(Equal(db.NonOneRowAffectedError{0})) 312 }) 313 }) 314 }) 315 316 Describe("SetResourceConfigScope", func() { 317 var pipeline db.Pipeline 318 var resource db.Resource 319 var scope db.ResourceConfigScope 320 321 BeforeEach(func() { 322 config := atc.Config{ 323 Resources: atc.ResourceConfigs{ 324 { 325 Name: "some-resource", 326 Type: defaultWorkerResourceType.Type, 327 Source: atc.Source{"some": "repository"}, 328 }, 329 { 330 Name: "some-other-resource", 331 Type: defaultWorkerResourceType.Type, 332 Source: atc.Source{"some": "other-repository"}, 333 }, 334 }, 335 Jobs: atc.JobConfigs{ 336 { 337 Name: "using-resource", 338 PlanSequence: []atc.Step{ 339 { 340 Config: &atc.GetStep{ 341 Name: "some-resource", 342 }, 343 }, 344 }, 345 }, 346 { 347 Name: "not-using-resource", 348 PlanSequence: []atc.Step{ 349 { 350 Config: &atc.GetStep{ 351 Name: "some-other-resource", 352 }, 353 }, 354 }, 355 }, 356 }, 357 } 358 359 var err error 360 361 var created bool 362 pipeline, created, err = defaultTeam.SavePipeline( 363 atc.PipelineRef{Name: "some-pipeline-with-two-jobs"}, 364 config, 365 0, 366 false, 367 ) 368 Expect(err).ToNot(HaveOccurred()) 369 Expect(created).To(BeTrue()) 370 371 var found bool 372 resource, found, err = pipeline.Resource("some-resource") 373 Expect(err).ToNot(HaveOccurred()) 374 Expect(found).To(BeTrue()) 375 376 resourceConfig, err := resourceConfigFactory.FindOrCreateResourceConfig(resource.Type(), resource.Source(), atc.VersionedResourceTypes{}) 377 Expect(err).ToNot(HaveOccurred()) 378 379 scope, err = resourceConfig.FindOrCreateScope(resource) 380 Expect(err).ToNot(HaveOccurred()) 381 }) 382 383 It("associates the resource to the config and scope", func() { 384 Expect(resource.ResourceConfigID()).To(BeZero()) 385 Expect(resource.ResourceConfigScopeID()).To(BeZero()) 386 387 Expect(resource.SetResourceConfigScope(scope)).To(Succeed()) 388 389 _, err := resource.Reload() 390 Expect(err).ToNot(HaveOccurred()) 391 392 Expect(resource.ResourceConfigID()).To(Equal(scope.ResourceConfig().ID())) 393 Expect(resource.ResourceConfigScopeID()).To(Equal(scope.ID())) 394 }) 395 396 It("requests scheduling for downstream jobs", func() { 397 job, found, err := pipeline.Job("using-resource") 398 Expect(err).NotTo(HaveOccurred()) 399 Expect(found).To(BeTrue()) 400 401 otherJob, found, err := pipeline.Job("not-using-resource") 402 Expect(err).NotTo(HaveOccurred()) 403 Expect(found).To(BeTrue()) 404 405 requestedSchedule := job.ScheduleRequestedTime() 406 otherRequestedSchedule := otherJob.ScheduleRequestedTime() 407 408 Expect(resource.SetResourceConfigScope(scope)).To(Succeed()) 409 410 found, err = job.Reload() 411 Expect(err).NotTo(HaveOccurred()) 412 Expect(found).To(BeTrue()) 413 414 found, err = otherJob.Reload() 415 Expect(err).NotTo(HaveOccurred()) 416 Expect(found).To(BeTrue()) 417 418 Expect(job.ScheduleRequestedTime()).Should(BeTemporally(">", requestedSchedule)) 419 Expect(otherJob.ScheduleRequestedTime()).Should(Equal(otherRequestedSchedule)) 420 }) 421 }) 422 423 Describe("CheckPlan", func() { 424 var resource db.Resource 425 var resourceTypes db.ResourceTypes 426 427 BeforeEach(func() { 428 var err error 429 var found bool 430 resource, found, err = pipeline.Resource("some-resource") 431 Expect(err).ToNot(HaveOccurred()) 432 Expect(found).To(BeTrue()) 433 434 resourceTypes, err = pipeline.ResourceTypes() 435 Expect(err).ToNot(HaveOccurred()) 436 }) 437 438 It("returns a plan which will update the resource", func() { 439 defaults := atc.Source{"sdk": "sdv"} 440 Expect(resource.CheckPlan(atc.Version{"some": "version"}, time.Minute, resourceTypes, defaults)).To(Equal(atc.CheckPlan{ 441 Name: resource.Name(), 442 Type: resource.Type(), 443 Source: defaults.Merge(resource.Source()), 444 Tags: resource.Tags(), 445 Timeout: "999m", 446 447 FromVersion: atc.Version{"some": "version"}, 448 449 Interval: "1m0s", 450 451 VersionedResourceTypes: resourceTypes.Deserialize(), 452 453 Resource: resource.Name(), 454 })) 455 }) 456 }) 457 458 Describe("CreateBuild", func() { 459 var ctx context.Context 460 var manuallyTriggered bool 461 var plan atc.Plan 462 var build db.Build 463 var created bool 464 465 BeforeEach(func() { 466 ctx = context.TODO() 467 manuallyTriggered = false 468 plan = atc.Plan{ 469 ID: "some-plan", 470 Check: &atc.CheckPlan{ 471 Name: "wreck", 472 }, 473 } 474 }) 475 476 JustBeforeEach(func() { 477 var err error 478 build, created, err = defaultResource.CreateBuild(ctx, manuallyTriggered, plan) 479 Expect(err).ToNot(HaveOccurred()) 480 }) 481 482 It("creates a started build for a resource", func() { 483 Expect(created).To(BeTrue()) 484 Expect(build).ToNot(BeNil()) 485 Expect(build.Name()).To(Equal(db.CheckBuildName)) 486 Expect(build.ResourceID()).To(Equal(defaultResource.ID())) 487 Expect(build.PipelineID()).To(Equal(defaultResource.PipelineID())) 488 Expect(build.TeamID()).To(Equal(defaultResource.TeamID())) 489 Expect(build.IsManuallyTriggered()).To(BeFalse()) 490 Expect(build.Status()).To(Equal(db.BuildStatusStarted)) 491 Expect(build.PrivatePlan()).To(Equal(plan)) 492 }) 493 494 It("associates the resource to the build", func() { 495 exists, err := defaultResource.Reload() 496 Expect(err).ToNot(HaveOccurred()) 497 Expect(exists).To(BeTrue()) 498 499 Expect(defaultResource.BuildSummary()).To(Equal(&atc.BuildSummary{ 500 ID: build.ID(), 501 Name: db.CheckBuildName, 502 Status: atc.StatusStarted, 503 StartTime: build.StartTime().Unix(), 504 TeamName: defaultTeam.Name(), 505 PipelineID: defaultPipeline.ID(), 506 PipelineName: defaultPipeline.Name(), 507 PipelineInstanceVars: defaultPipeline.InstanceVars(), 508 })) 509 }) 510 511 Context("when tracing is configured", func() { 512 var span trace.Span 513 514 BeforeEach(func() { 515 tracing.ConfigureTraceProvider(tracetest.NewProvider()) 516 517 ctx, span = tracing.StartSpan(context.Background(), "fake-operation", nil) 518 }) 519 520 AfterEach(func() { 521 tracing.Configured = false 522 }) 523 524 It("propagates span context", func() { 525 traceID := span.SpanContext().TraceID.String() 526 buildContext := build.SpanContext() 527 traceParent := buildContext.Get("traceparent") 528 Expect(traceParent).To(ContainSubstring(traceID)) 529 }) 530 }) 531 532 Context("when another build already exists", func() { 533 var prevBuild db.Build 534 535 BeforeEach(func() { 536 var err error 537 var prevCreated bool 538 prevBuild, prevCreated, err = defaultResource.CreateBuild(ctx, false, plan) 539 Expect(err).ToNot(HaveOccurred()) 540 Expect(prevCreated).To(BeTrue()) 541 }) 542 543 It("does not create the second build", func() { 544 Expect(created).To(BeFalse()) 545 }) 546 547 Context("when manually triggered", func() { 548 BeforeEach(func() { 549 manuallyTriggered = true 550 }) 551 552 It("creates a manually triggered resource build", func() { 553 Expect(created).To(BeTrue()) 554 Expect(build.IsManuallyTriggered()).To(BeTrue()) 555 Expect(build.ResourceID()).To(Equal(defaultResource.ID())) 556 }) 557 558 It("associates the resource to the build", func() { 559 exists, err := defaultResource.Reload() 560 Expect(err).ToNot(HaveOccurred()) 561 Expect(exists).To(BeTrue()) 562 563 Expect(defaultResource.BuildSummary()).To(Equal(&atc.BuildSummary{ 564 ID: build.ID(), 565 Name: db.CheckBuildName, 566 Status: atc.StatusStarted, 567 StartTime: build.StartTime().Unix(), 568 TeamName: defaultTeam.Name(), 569 PipelineID: defaultPipeline.ID(), 570 PipelineName: defaultPipeline.Name(), 571 PipelineInstanceVars: defaultPipeline.InstanceVars(), 572 })) 573 }) 574 }) 575 576 Context("when the previous build is finished", func() { 577 BeforeEach(func() { 578 Expect(prevBuild.Finish(db.BuildStatusSucceeded)).To(Succeed()) 579 }) 580 581 It("creates the build", func() { 582 Expect(created).To(BeTrue()) 583 Expect(build.ResourceID()).To(Equal(defaultResource.ID())) 584 }) 585 586 Context("when there are multiple completed previous builds", func() { 587 var prevBuilds []db.Build 588 589 BeforeEach(func() { 590 prevBuilds = []db.Build{prevBuild} 591 592 for i := 0; i < 10; i++ { 593 prev, created, err := defaultResource.CreateBuild(ctx, true, plan) 594 Expect(err).ToNot(HaveOccurred()) 595 Expect(created).To(BeTrue()) 596 597 Expect(prev.Finish(db.BuildStatusSucceeded)).To(Succeed()) 598 599 prevBuilds = append(prevBuilds, prev) 600 } 601 }) 602 603 It("deletes all previous builds", func() { 604 for _, prev := range prevBuilds { 605 found, err := prev.Reload() 606 Expect(err).ToNot(HaveOccurred()) 607 Expect(found).To(BeFalse()) 608 } 609 }) 610 611 It("deletes the previous builds' events", func() { 612 for _, prev := range prevBuilds { 613 var exists bool 614 err := dbConn.QueryRow(`SELECT EXISTS ( 615 SELECT 1 616 FROM build_events 617 WHERE build_id = $1 618 )`, prev.ID()).Scan(&exists) 619 Expect(err).ToNot(HaveOccurred()) 620 Expect(exists).To(BeFalse()) 621 } 622 }) 623 }) 624 }) 625 }) 626 }) 627 628 Context("Versions", func() { 629 var ( 630 scenario *dbtest.Scenario 631 ) 632 633 Context("with version filters", func() { 634 var filter atc.Version 635 var resourceVersions []atc.ResourceVersion 636 637 BeforeEach(func() { 638 scenario = dbtest.Setup( 639 builder.WithPipeline(atc.Config{ 640 Resources: atc.ResourceConfigs{ 641 { 642 Name: "some-resource", 643 Type: "some-base-resource-type", 644 Source: atc.Source{"some": "repository"}, 645 }, 646 }, 647 }), 648 builder.WithResourceVersions( 649 "some-resource", 650 atc.Version{"ref": "v0", "commit": "v0"}, 651 atc.Version{"ref": "v1", "commit": "v1"}, 652 atc.Version{"ref": "v2", "commit": "v2"}, 653 ), 654 ) 655 656 resourceVersions = make([]atc.ResourceVersion, 0) 657 658 for i := 0; i < 3; i++ { 659 rcv := scenario.ResourceVersion("some-resource", atc.Version{ 660 "ref": "v" + strconv.Itoa(i), 661 "commit": "v" + strconv.Itoa(i), 662 }) 663 664 var metadata []atc.MetadataField 665 666 for _, v := range rcv.Metadata() { 667 metadata = append(metadata, atc.MetadataField(v)) 668 } 669 670 resourceVersion := atc.ResourceVersion{ 671 ID: rcv.ID(), 672 Version: atc.Version(rcv.Version()), 673 Metadata: metadata, 674 Enabled: true, 675 } 676 677 resourceVersions = append(resourceVersions, resourceVersion) 678 } 679 }) 680 681 Context("when filter include one field that matches", func() { 682 BeforeEach(func() { 683 filter = atc.Version{"ref": "v2"} 684 }) 685 686 It("return version that matches field filter", func() { 687 result, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 10}, filter) 688 Expect(err).ToNot(HaveOccurred()) 689 Expect(found).To(BeTrue()) 690 Expect(len(result)).To(Equal(1)) 691 Expect(result[0].Version).To(Equal(resourceVersions[2].Version)) 692 }) 693 }) 694 695 Context("when filter include one field that doesn't match", func() { 696 BeforeEach(func() { 697 filter = atc.Version{"ref": "v20"} 698 }) 699 700 It("return no version", func() { 701 result, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 10}, filter) 702 Expect(err).ToNot(HaveOccurred()) 703 Expect(found).To(BeTrue()) 704 Expect(len(result)).To(Equal(0)) 705 }) 706 }) 707 708 Context("when filter include two fields that match", func() { 709 BeforeEach(func() { 710 filter = atc.Version{"ref": "v1", "commit": "v1"} 711 }) 712 713 It("return version", func() { 714 result, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 10}, filter) 715 Expect(err).ToNot(HaveOccurred()) 716 Expect(found).To(BeTrue()) 717 Expect(len(result)).To(Equal(1)) 718 Expect(result[0].Version).To(Equal(resourceVersions[1].Version)) 719 }) 720 }) 721 722 Context("when filter include two fields and one of them does not match", func() { 723 BeforeEach(func() { 724 filter = atc.Version{"ref": "v1", "commit": "v2"} 725 }) 726 727 It("return no version", func() { 728 result, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 10}, filter) 729 Expect(err).ToNot(HaveOccurred()) 730 Expect(found).To(BeTrue()) 731 Expect(len(result)).To(Equal(0)) 732 }) 733 }) 734 }) 735 736 Context("when resource has versions created in order of check order", func() { 737 var resourceVersions []atc.ResourceVersion 738 739 BeforeEach(func() { 740 scenario = dbtest.Setup( 741 builder.WithPipeline(atc.Config{ 742 Resources: atc.ResourceConfigs{ 743 { 744 Name: "some-resource", 745 Type: "some-base-resource-type", 746 Source: atc.Source{"some": "repository"}, 747 }, 748 }, 749 }), 750 builder.WithResourceVersions( 751 "some-resource", 752 atc.Version{"ref": "v0"}, 753 atc.Version{"ref": "v1"}, 754 atc.Version{"ref": "v2"}, 755 atc.Version{"ref": "v3"}, 756 atc.Version{"ref": "v4"}, 757 atc.Version{"ref": "v5"}, 758 atc.Version{"ref": "v6"}, 759 atc.Version{"ref": "v7"}, 760 atc.Version{"ref": "v8"}, 761 atc.Version{"ref": "v9"}, 762 ), 763 ) 764 765 resourceVersions = make([]atc.ResourceVersion, 0) 766 767 for i := 0; i < 10; i++ { 768 rcv := scenario.ResourceVersion("some-resource", atc.Version{"ref": "v" + strconv.Itoa(i)}) 769 770 var metadata []atc.MetadataField 771 772 for _, v := range rcv.Metadata() { 773 metadata = append(metadata, atc.MetadataField(v)) 774 } 775 776 resourceVersion := atc.ResourceVersion{ 777 ID: rcv.ID(), 778 Version: atc.Version(rcv.Version()), 779 Metadata: metadata, 780 Enabled: true, 781 } 782 783 resourceVersions = append(resourceVersions, resourceVersion) 784 } 785 }) 786 787 Context("with no from/to", func() { 788 It("returns the first page, with the given limit, and a next page", func() { 789 historyPage, pagination, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 2}, nil) 790 Expect(err).ToNot(HaveOccurred()) 791 Expect(found).To(BeTrue()) 792 Expect(len(historyPage)).To(Equal(2)) 793 Expect(historyPage[0].Version).To(Equal(resourceVersions[9].Version)) 794 Expect(historyPage[1].Version).To(Equal(resourceVersions[8].Version)) 795 Expect(pagination.Newer).To(BeNil()) 796 Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(resourceVersions[7].ID), Limit: 2})) 797 }) 798 }) 799 800 Context("with a to that places it in the middle of the versions", func() { 801 It("returns the versions, with previous/next pages", func() { 802 historyPage, pagination, found, err := scenario.Resource("some-resource").Versions(db.Page{To: db.NewIntPtr(resourceVersions[6].ID), Limit: 2}, nil) 803 Expect(err).ToNot(HaveOccurred()) 804 Expect(found).To(BeTrue()) 805 Expect(len(historyPage)).To(Equal(2)) 806 Expect(historyPage[0].Version).To(Equal(resourceVersions[6].Version)) 807 Expect(historyPage[1].Version).To(Equal(resourceVersions[5].Version)) 808 Expect(pagination.Newer).To(Equal(&db.Page{From: db.NewIntPtr(resourceVersions[7].ID), Limit: 2})) 809 Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(resourceVersions[4].ID), Limit: 2})) 810 }) 811 }) 812 813 Context("with a to that places it to the oldest version", func() { 814 It("returns the versions, with no next page", func() { 815 historyPage, pagination, found, err := scenario.Resource("some-resource").Versions(db.Page{To: db.NewIntPtr(resourceVersions[1].ID), Limit: 2}, nil) 816 Expect(err).ToNot(HaveOccurred()) 817 Expect(found).To(BeTrue()) 818 Expect(len(historyPage)).To(Equal(2)) 819 Expect(historyPage[0].Version).To(Equal(resourceVersions[1].Version)) 820 Expect(historyPage[1].Version).To(Equal(resourceVersions[0].Version)) 821 Expect(pagination.Newer).To(Equal(&db.Page{From: db.NewIntPtr(resourceVersions[2].ID), Limit: 2})) 822 Expect(pagination.Older).To(BeNil()) 823 }) 824 }) 825 826 Context("with a from that places it in the middle of the versions", func() { 827 It("returns the versions, with previous/next pages", func() { 828 historyPage, pagination, found, err := scenario.Resource("some-resource").Versions(db.Page{From: db.NewIntPtr(resourceVersions[6].ID), Limit: 2}, nil) 829 Expect(err).ToNot(HaveOccurred()) 830 Expect(found).To(BeTrue()) 831 Expect(len(historyPage)).To(Equal(2)) 832 Expect(historyPage[0].Version).To(Equal(resourceVersions[7].Version)) 833 Expect(historyPage[1].Version).To(Equal(resourceVersions[6].Version)) 834 Expect(pagination.Newer).To(Equal(&db.Page{From: db.NewIntPtr(resourceVersions[8].ID), Limit: 2})) 835 Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(resourceVersions[5].ID), Limit: 2})) 836 }) 837 }) 838 839 Context("with a from that places it at the beginning of the most recent versions", func() { 840 It("returns the versions, with no previous page", func() { 841 historyPage, pagination, found, err := scenario.Resource("some-resource").Versions(db.Page{From: db.NewIntPtr(resourceVersions[8].ID), Limit: 2}, nil) 842 Expect(err).ToNot(HaveOccurred()) 843 Expect(found).To(BeTrue()) 844 Expect(len(historyPage)).To(Equal(2)) 845 Expect(historyPage[0].Version).To(Equal(resourceVersions[9].Version)) 846 Expect(historyPage[1].Version).To(Equal(resourceVersions[8].Version)) 847 Expect(pagination.Newer).To(BeNil()) 848 Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(resourceVersions[7].ID), Limit: 2})) 849 }) 850 }) 851 852 Context("when the version has metadata", func() { 853 BeforeEach(func() { 854 metadata := []db.ResourceConfigMetadataField{{Name: "name1", Value: "value1"}} 855 856 scenario.Run(builder.WithVersionMetadata("some-resource", atc.Version(resourceVersions[9].Version), metadata)) 857 }) 858 859 It("returns the metadata in the version history", func() { 860 historyPage, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 1}, nil) 861 Expect(err).ToNot(HaveOccurred()) 862 Expect(found).To(BeTrue()) 863 Expect(len(historyPage)).To(Equal(1)) 864 Expect(historyPage[0].Version).To(Equal(resourceVersions[9].Version)) 865 Expect(historyPage[0].Metadata).To(Equal([]atc.MetadataField{{Name: "name1", Value: "value1"}})) 866 }) 867 868 It("maintains existing metadata after same version is saved with no metadata", func() { 869 scenario.Run(builder.WithResourceVersions("some-resource", atc.Version(resourceVersions[9].Version))) 870 871 historyPage, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 1}, atc.Version{}) 872 Expect(err).ToNot(HaveOccurred()) 873 Expect(found).To(BeTrue()) 874 Expect(len(historyPage)).To(Equal(1)) 875 Expect(historyPage[0].Version).To(Equal(resourceVersions[9].Version)) 876 Expect(historyPage[0].Metadata).To(Equal([]atc.MetadataField{{Name: "name1", Value: "value1"}})) 877 }) 878 879 It("updates metadata after same version is saved with different metadata", func() { 880 newMetadata := []db.ResourceConfigMetadataField{{Name: "name-new", Value: "value-new"}} 881 scenario.Run(builder.WithVersionMetadata("some-resource", atc.Version(resourceVersions[9].Version), newMetadata)) 882 883 historyPage, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 1}, atc.Version{}) 884 Expect(err).ToNot(HaveOccurred()) 885 Expect(found).To(BeTrue()) 886 Expect(len(historyPage)).To(Equal(1)) 887 Expect(historyPage[0].Version).To(Equal(resourceVersions[9].Version)) 888 Expect(historyPage[0].Metadata).To(Equal([]atc.MetadataField{{Name: "name-new", Value: "value-new"}})) 889 }) 890 }) 891 892 Context("when a version is disabled", func() { 893 BeforeEach(func() { 894 scenario.Run(builder.WithDisabledVersion("some-resource", resourceVersions[9].Version)) 895 896 resourceVersions[9].Enabled = false 897 }) 898 899 It("returns a disabled version", func() { 900 historyPage, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 1}, nil) 901 Expect(err).ToNot(HaveOccurred()) 902 Expect(found).To(BeTrue()) 903 Expect(historyPage).To(ConsistOf([]atc.ResourceVersion{resourceVersions[9]})) 904 }) 905 }) 906 907 Context("when the version metadata is updated", func() { 908 var metadata db.ResourceConfigMetadataFields 909 910 BeforeEach(func() { 911 metadata = []db.ResourceConfigMetadataField{{Name: "name1", Value: "value1"}} 912 913 scenario.Run(builder.WithVersionMetadata("some-resource", atc.Version(resourceVersions[9].Version), metadata)) 914 }) 915 916 It("returns a version with metadata updated", func() { 917 historyPage, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 1}, nil) 918 Expect(err).ToNot(HaveOccurred()) 919 Expect(found).To(BeTrue()) 920 Expect(len(historyPage)).To(Equal(1)) 921 Expect(historyPage[0].Version).To(Equal(resourceVersions[9].Version)) 922 Expect(historyPage[0].Metadata).To(Equal([]atc.MetadataField{{Name: "name1", Value: "value1"}})) 923 }) 924 }) 925 }) 926 927 Context("when check orders are different than versions ids", func() { 928 var resourceVersions []atc.ResourceVersion 929 930 BeforeEach(func() { 931 scenario = dbtest.Setup( 932 builder.WithPipeline(atc.Config{ 933 Resources: atc.ResourceConfigs{ 934 { 935 Name: "some-resource", 936 Type: "some-base-resource-type", 937 Source: atc.Source{"some": "repository"}, 938 }, 939 }, 940 }), 941 builder.WithResourceVersions( 942 "some-resource", 943 atc.Version{"ref": "v1"}, // id: 1, check_order: 1 944 atc.Version{"ref": "v3"}, // id: 2, check_order: 2 945 atc.Version{"ref": "v4"}, // id: 3, check_order: 3 946 ), 947 builder.WithResourceVersions( 948 "some-resource", 949 atc.Version{"ref": "v2"}, // id: 4, check_order: 4 950 atc.Version{"ref": "v3"}, // id: 2, check_order: 5 951 atc.Version{"ref": "v4"}, // id: 3, check_order: 6 952 ), 953 ) 954 955 for i := 1; i < 5; i++ { 956 rcv := scenario.ResourceVersion("some-resource", atc.Version{"ref": "v" + strconv.Itoa(i)}) 957 958 var metadata []atc.MetadataField 959 960 for _, v := range rcv.Metadata() { 961 metadata = append(metadata, atc.MetadataField(v)) 962 } 963 964 resourceVersion := atc.ResourceVersion{ 965 ID: rcv.ID(), 966 Version: atc.Version(rcv.Version()), 967 Metadata: metadata, 968 Enabled: true, 969 } 970 971 resourceVersions = append(resourceVersions, resourceVersion) 972 } 973 974 // ids ordered by check order now: [3, 2, 4, 1] 975 }) 976 977 Context("with no from/to", func() { 978 It("returns versions ordered by check order", func() { 979 historyPage, pagination, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 4}, nil) 980 Expect(err).ToNot(HaveOccurred()) 981 Expect(found).To(BeTrue()) 982 Expect(historyPage).To(HaveLen(4)) 983 Expect(historyPage[0].Version).To(Equal(resourceVersions[3].Version)) 984 Expect(historyPage[1].Version).To(Equal(resourceVersions[2].Version)) 985 Expect(historyPage[2].Version).To(Equal(resourceVersions[1].Version)) 986 Expect(historyPage[3].Version).To(Equal(resourceVersions[0].Version)) 987 Expect(pagination.Newer).To(BeNil()) 988 Expect(pagination.Older).To(BeNil()) 989 }) 990 }) 991 992 Context("with from", func() { 993 It("returns the versions, with previous/next pages including from", func() { 994 historyPage, pagination, found, err := scenario.Resource("some-resource").Versions(db.Page{From: db.NewIntPtr(resourceVersions[1].ID), Limit: 2}, nil) 995 Expect(err).ToNot(HaveOccurred()) 996 Expect(found).To(BeTrue()) 997 Expect(historyPage).To(HaveLen(2)) 998 Expect(historyPage[0].Version).To(Equal(resourceVersions[2].Version)) 999 Expect(historyPage[1].Version).To(Equal(resourceVersions[1].Version)) 1000 Expect(pagination.Newer).To(Equal(&db.Page{From: db.NewIntPtr(resourceVersions[3].ID), Limit: 2})) 1001 Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(resourceVersions[0].ID), Limit: 2})) 1002 }) 1003 }) 1004 1005 Context("with to", func() { 1006 It("returns the builds, with previous/next pages including to", func() { 1007 historyPage, pagination, found, err := scenario.Resource("some-resource").Versions(db.Page{To: db.NewIntPtr(resourceVersions[2].ID), Limit: 2}, nil) 1008 Expect(err).ToNot(HaveOccurred()) 1009 Expect(found).To(BeTrue()) 1010 Expect(historyPage).To(HaveLen(2)) 1011 Expect(historyPage[0].Version).To(Equal(resourceVersions[2].Version)) 1012 Expect(historyPage[1].Version).To(Equal(resourceVersions[1].Version)) 1013 Expect(pagination.Newer).To(Equal(&db.Page{From: db.NewIntPtr(resourceVersions[3].ID), Limit: 2})) 1014 Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(resourceVersions[0].ID), Limit: 2})) 1015 }) 1016 }) 1017 }) 1018 }) 1019 1020 Describe("PinVersion/UnpinVersion", func() { 1021 var ( 1022 scenario *dbtest.Scenario 1023 ) 1024 1025 BeforeEach(func() { 1026 scenario = dbtest.Setup( 1027 builder.WithPipeline(atc.Config{ 1028 Resources: atc.ResourceConfigs{ 1029 { 1030 Name: "some-resource", 1031 Type: "some-base-resource-type", 1032 Source: atc.Source{"some": "repository"}, 1033 }, 1034 }, 1035 Jobs: atc.JobConfigs{ 1036 { 1037 Name: "job-using-resource", 1038 PlanSequence: []atc.Step{ 1039 { 1040 Config: &atc.GetStep{ 1041 Name: "some-resource", 1042 }, 1043 }, 1044 }, 1045 }, 1046 { 1047 Name: "not-using-resource", 1048 }, 1049 }, 1050 }), 1051 builder.WithResourceVersions( 1052 "some-resource", 1053 atc.Version{"version": "v1"}, 1054 atc.Version{"version": "v2"}, 1055 atc.Version{"version": "v3"}, 1056 ), 1057 ) 1058 }) 1059 1060 Context("when we use an invalid version id (does not exist)", func() { 1061 var ( 1062 pinnedVersion atc.Version 1063 ) 1064 1065 BeforeEach(func() { 1066 found, err := scenario.Resource("some-resource").PinVersion(scenario.ResourceVersion("some-resource", atc.Version{"version": "v1"}).ID()) 1067 Expect(found).To(BeTrue()) 1068 Expect(err).ToNot(HaveOccurred()) 1069 1070 Expect(scenario.Resource("some-resource").CurrentPinnedVersion()).To(Equal(scenario.Resource("some-resource").APIPinnedVersion())) 1071 pinnedVersion = scenario.Resource("some-resource").APIPinnedVersion() 1072 }) 1073 1074 It("returns not found and does not update anything", func() { 1075 found, err := scenario.Resource("some-resource").PinVersion(-1) 1076 Expect(found).To(BeFalse()) 1077 Expect(err).To(HaveOccurred()) 1078 1079 Expect(scenario.Resource("some-resource").APIPinnedVersion()).To(Equal(pinnedVersion)) 1080 }) 1081 }) 1082 1083 Context("when requesting schedule for version pinning", func() { 1084 It("requests schedule on all jobs using the resource", func() { 1085 requestedSchedule := scenario.Job("job-using-resource").ScheduleRequestedTime() 1086 1087 found, err := scenario.Resource("some-resource").PinVersion(scenario.ResourceVersion("some-resource", atc.Version{"version": "v1"}).ID()) 1088 Expect(found).To(BeTrue()) 1089 Expect(err).ToNot(HaveOccurred()) 1090 1091 Expect(scenario.Job("job-using-resource").ScheduleRequestedTime()).Should(BeTemporally(">", requestedSchedule)) 1092 }) 1093 1094 It("does not request schedule on jobs that do not use the resource", func() { 1095 requestedSchedule := scenario.Job("not-using-resource").ScheduleRequestedTime() 1096 1097 found, err := scenario.Resource("some-resource").PinVersion(scenario.ResourceVersion("some-resource", atc.Version{"version": "v1"}).ID()) 1098 Expect(found).To(BeTrue()) 1099 Expect(err).ToNot(HaveOccurred()) 1100 1101 Expect(scenario.Job("not-using-resource").ScheduleRequestedTime()).Should(BeTemporally("==", requestedSchedule)) 1102 }) 1103 }) 1104 1105 Context("when we pin a resource to a version", func() { 1106 BeforeEach(func() { 1107 found, err := scenario.Resource("some-resource").PinVersion(scenario.ResourceVersion("some-resource", atc.Version{"version": "v1"}).ID()) 1108 Expect(found).To(BeTrue()) 1109 Expect(err).ToNot(HaveOccurred()) 1110 }) 1111 1112 Context("when the resource is not pinned", func() { 1113 It("sets the api pinned version", func() { 1114 Expect(scenario.Resource("some-resource").APIPinnedVersion()).To(Equal(atc.Version{"version": "v1"})) 1115 Expect(scenario.Resource("some-resource").CurrentPinnedVersion()).To(Equal(scenario.Resource("some-resource").APIPinnedVersion())) 1116 }) 1117 }) 1118 1119 Context("when the resource is pinned by another version already", func() { 1120 BeforeEach(func() { 1121 found, err := scenario.Resource("some-resource").PinVersion(scenario.ResourceVersion("some-resource", atc.Version{"version": "v3"}).ID()) 1122 Expect(found).To(BeTrue()) 1123 Expect(err).ToNot(HaveOccurred()) 1124 }) 1125 1126 It("switch the pin to given version", func() { 1127 Expect(scenario.Resource("some-resource").APIPinnedVersion()).To(Equal(atc.Version{"version": "v3"})) 1128 Expect(scenario.Resource("some-resource").CurrentPinnedVersion()).To(Equal(scenario.Resource("some-resource").APIPinnedVersion())) 1129 }) 1130 }) 1131 1132 Context("when we set the pin comment on a resource", func() { 1133 BeforeEach(func() { 1134 err := scenario.Resource("some-resource").SetPinComment("foo") 1135 Expect(err).ToNot(HaveOccurred()) 1136 }) 1137 1138 It("should set the pin comment", func() { 1139 Expect(scenario.Resource("some-resource").PinComment()).To(Equal("foo")) 1140 }) 1141 }) 1142 1143 Context("when requesting schedule for version unpinning", func() { 1144 It("requests schedule on all jobs using the resource", func() { 1145 requestedSchedule := scenario.Job("job-using-resource").ScheduleRequestedTime() 1146 1147 err := scenario.Resource("some-resource").UnpinVersion() 1148 Expect(err).ToNot(HaveOccurred()) 1149 1150 Expect(scenario.Job("job-using-resource").ScheduleRequestedTime()).Should(BeTemporally(">", requestedSchedule)) 1151 }) 1152 1153 It("does not request schedule on jobs that do not use the resource", func() { 1154 requestedSchedule := scenario.Job("not-using-resource").ScheduleRequestedTime() 1155 1156 err := scenario.Resource("some-resource").UnpinVersion() 1157 Expect(err).ToNot(HaveOccurred()) 1158 1159 Expect(scenario.Job("not-using-resource").ScheduleRequestedTime()).Should(BeTemporally("==", requestedSchedule)) 1160 }) 1161 }) 1162 1163 Context("when we unpin a resource to a version", func() { 1164 BeforeEach(func() { 1165 err := scenario.Resource("some-resource").UnpinVersion() 1166 Expect(err).ToNot(HaveOccurred()) 1167 }) 1168 1169 It("sets the api pinned version to nil", func() { 1170 Expect(scenario.Resource("some-resource").APIPinnedVersion()).To(BeNil()) 1171 Expect(scenario.Resource("some-resource").CurrentPinnedVersion()).To(BeNil()) 1172 }) 1173 1174 It("unsets the pin comment", func() { 1175 Expect(scenario.Resource("some-resource").PinComment()).To(BeEmpty()) 1176 }) 1177 }) 1178 }) 1179 1180 Context("when we pin a resource that is already pinned to a version (through the config)", func() { 1181 BeforeEach(func() { 1182 scenario.Run( 1183 builder.WithPipeline(atc.Config{ 1184 Resources: atc.ResourceConfigs{ 1185 { 1186 Name: "some-resource", 1187 Type: "some-base-resource-type", 1188 Source: atc.Source{"some": "repository"}, 1189 Version: atc.Version{"pinned": "version"}, 1190 }, 1191 }, 1192 }), 1193 builder.WithResourceVersions( 1194 "some-resource", 1195 atc.Version{"version": "v1"}, 1196 atc.Version{"version": "v2"}, 1197 atc.Version{"version": "v3"}, 1198 )) 1199 }) 1200 1201 It("should fail to update the pinned version", func() { 1202 found, err := scenario.Resource("some-resource").PinVersion(scenario.ResourceVersion("some-resource", atc.Version{"version": "v1"}).ID()) 1203 Expect(found).To(BeFalse()) 1204 Expect(err).To(Equal(db.ErrPinnedThroughConfig)) 1205 }) 1206 }) 1207 }) 1208 1209 Describe("Public", func() { 1210 var ( 1211 resource db.Resource 1212 found bool 1213 err error 1214 ) 1215 1216 Context("when public is not set in the config", func() { 1217 BeforeEach(func() { 1218 resource, found, err = pipeline.Resource("some-resource") 1219 Expect(err).ToNot(HaveOccurred()) 1220 Expect(found).To(BeTrue()) 1221 }) 1222 1223 It("returns false", func() { 1224 Expect(resource.Public()).To(BeFalse()) 1225 }) 1226 }) 1227 1228 Context("when public is set to true in the config", func() { 1229 BeforeEach(func() { 1230 resource, found, err = pipeline.Resource("some-other-resource") 1231 Expect(err).ToNot(HaveOccurred()) 1232 Expect(found).To(BeTrue()) 1233 }) 1234 1235 It("returns true", func() { 1236 Expect(resource.Public()).To(BeTrue()) 1237 }) 1238 }) 1239 1240 Context("when public is set to false in the config", func() { 1241 BeforeEach(func() { 1242 resource, found, err = pipeline.Resource("some-secret-resource") 1243 Expect(err).ToNot(HaveOccurred()) 1244 Expect(found).To(BeTrue()) 1245 }) 1246 1247 It("returns false", func() { 1248 Expect(resource.Public()).To(BeFalse()) 1249 }) 1250 }) 1251 }) 1252 })