github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/exec/check_step_test.go (about) 1 package exec_test 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "io" 8 "time" 9 10 "github.com/pf-qiu/concourse/v6/atc" 11 "github.com/pf-qiu/concourse/v6/atc/db" 12 "github.com/pf-qiu/concourse/v6/atc/db/dbfakes" 13 "github.com/pf-qiu/concourse/v6/atc/db/lock/lockfakes" 14 "github.com/pf-qiu/concourse/v6/atc/exec" 15 "github.com/pf-qiu/concourse/v6/atc/exec/execfakes" 16 "github.com/pf-qiu/concourse/v6/atc/resource" 17 "github.com/pf-qiu/concourse/v6/atc/resource/resourcefakes" 18 "github.com/pf-qiu/concourse/v6/atc/runtime" 19 "github.com/pf-qiu/concourse/v6/atc/runtime/runtimefakes" 20 "github.com/pf-qiu/concourse/v6/atc/worker" 21 "github.com/pf-qiu/concourse/v6/atc/worker/workerfakes" 22 "github.com/pf-qiu/concourse/v6/tracing" 23 "github.com/pf-qiu/concourse/v6/vars" 24 "go.opentelemetry.io/otel/api/trace" 25 "go.opentelemetry.io/otel/api/trace/tracetest" 26 27 . "github.com/onsi/ginkgo" 28 . "github.com/onsi/gomega" 29 ) 30 31 var _ = Describe("CheckStep", func() { 32 var ( 33 ctx context.Context 34 cancel context.CancelFunc 35 36 planID atc.PlanID 37 fakeRunState *execfakes.FakeRunState 38 fakeResourceFactory *resourcefakes.FakeResourceFactory 39 fakeResource *resourcefakes.FakeResource 40 fakeResourceConfigFactory *dbfakes.FakeResourceConfigFactory 41 fakeResourceConfig *dbfakes.FakeResourceConfig 42 fakeResourceConfigScope *dbfakes.FakeResourceConfigScope 43 fakePool *workerfakes.FakePool 44 fakeStrategy *workerfakes.FakeContainerPlacementStrategy 45 fakeDelegate *execfakes.FakeCheckDelegate 46 fakeDelegateFactory *execfakes.FakeCheckDelegateFactory 47 spanCtx context.Context 48 fakeClient *workerfakes.FakeClient 49 defaultTimeout = time.Hour 50 51 fakeStdout, fakeStderr io.Writer 52 53 stepMetadata exec.StepMetadata 54 checkStep exec.Step 55 checkPlan atc.CheckPlan 56 containerMetadata db.ContainerMetadata 57 58 stepOk bool 59 stepErr error 60 ) 61 62 BeforeEach(func() { 63 ctx, cancel = context.WithCancel(context.Background()) 64 65 planID = "some-plan-id" 66 67 fakeRunState = new(execfakes.FakeRunState) 68 fakeResourceFactory = new(resourcefakes.FakeResourceFactory) 69 fakeResource = new(resourcefakes.FakeResource) 70 fakePool = new(workerfakes.FakePool) 71 fakeStrategy = new(workerfakes.FakeContainerPlacementStrategy) 72 fakeDelegateFactory = new(execfakes.FakeCheckDelegateFactory) 73 fakeDelegate = new(execfakes.FakeCheckDelegate) 74 fakeClient = new(workerfakes.FakeClient) 75 76 spanCtx = context.Background() 77 fakeDelegate.StartSpanReturns(spanCtx, trace.NoopSpan{}) 78 79 fakeStdout = bytes.NewBufferString("out") 80 fakeDelegate.StdoutReturns(fakeStdout) 81 82 fakeStderr = bytes.NewBufferString("err") 83 fakeDelegate.StderrReturns(fakeStderr) 84 85 stepMetadata = exec.StepMetadata{} 86 containerMetadata = db.ContainerMetadata{} 87 88 fakeResourceFactory.NewResourceReturns(fakeResource) 89 90 fakeResourceConfigFactory = new(dbfakes.FakeResourceConfigFactory) 91 fakeResourceConfig = new(dbfakes.FakeResourceConfig) 92 fakeResourceConfig.IDReturns(501) 93 fakeResourceConfig.OriginBaseResourceTypeReturns(&db.UsedBaseResourceType{ 94 ID: 502, 95 Name: "some-base-type", 96 }) 97 fakeResourceConfigFactory.FindOrCreateResourceConfigReturns(fakeResourceConfig, nil) 98 99 fakeResourceConfigScope = new(dbfakes.FakeResourceConfigScope) 100 fakeDelegate.FindOrCreateScopeReturns(fakeResourceConfigScope, nil) 101 102 fakeDelegateFactory.CheckDelegateReturns(fakeDelegate) 103 104 checkPlan = atc.CheckPlan{ 105 Name: "some-name", 106 Type: "some-base-type", 107 Source: atc.Source{"some": "((source-var))"}, 108 Timeout: "10s", 109 VersionedResourceTypes: atc.VersionedResourceTypes{ 110 { 111 ResourceType: atc.ResourceType{ 112 Name: "some-custom-type", 113 Type: "another-custom-type", 114 Source: atc.Source{"some-custom": "((source-var))"}, 115 Params: atc.Params{"some-custom": "((params-var))"}, 116 }, 117 Version: atc.Version{"some-custom": "version"}, 118 }, 119 { 120 ResourceType: atc.ResourceType{ 121 Name: "another-custom-type", 122 Type: "registry-image", 123 Source: atc.Source{"another-custom": "((source-var))"}, 124 Privileged: true, 125 }, 126 Version: atc.Version{"another-custom": "version"}, 127 }, 128 }, 129 } 130 131 containerMetadata = db.ContainerMetadata{ 132 User: "test-user", 133 } 134 135 stepMetadata = exec.StepMetadata{ 136 TeamID: 345, 137 BuildID: 678, 138 } 139 140 fakeRunState.GetStub = vars.StaticVariables{"source-var": "super-secret-source"}.Get 141 }) 142 143 AfterEach(func() { 144 cancel() 145 }) 146 147 JustBeforeEach(func() { 148 checkStep = exec.NewCheckStep( 149 planID, 150 checkPlan, 151 stepMetadata, 152 fakeResourceFactory, 153 fakeResourceConfigFactory, 154 containerMetadata, 155 fakeStrategy, 156 fakePool, 157 fakeDelegateFactory, 158 fakeClient, 159 defaultTimeout, 160 ) 161 162 stepOk, stepErr = checkStep.Run(ctx, fakeRunState) 163 }) 164 165 Context("with a reasonable configuration", func() { 166 BeforeEach(func() { 167 }) 168 169 It("emits an Initializing event", func() { 170 Expect(fakeDelegate.InitializingCallCount()).To(Equal(1)) 171 }) 172 173 Context("when not running", func() { 174 BeforeEach(func() { 175 fakeDelegate.WaitToRunReturns(nil, false, nil) 176 }) 177 178 It("does not run the check step", func() { 179 Expect(fakeClient.RunCheckStepCallCount()).To(Equal(0)) 180 }) 181 182 It("succeeds", func() { 183 Expect(stepOk).To(BeTrue()) 184 }) 185 186 Context("when there is a latest version", func() { 187 BeforeEach(func() { 188 fakeVersion := new(dbfakes.FakeResourceConfigVersion) 189 fakeVersion.VersionReturns(db.Version{"some": "latest-version"}) 190 fakeResourceConfigScope.LatestVersionReturns(fakeVersion, true, nil) 191 }) 192 193 It("stores the latest version as the step result", func() { 194 Expect(fakeRunState.StoreResultCallCount()).To(Equal(1)) 195 id, val := fakeRunState.StoreResultArgsForCall(0) 196 Expect(id).To(Equal(atc.PlanID("some-plan-id"))) 197 Expect(val).To(Equal(atc.Version{"some": "latest-version"})) 198 }) 199 }) 200 201 Context("when there is no version", func() { 202 BeforeEach(func() { 203 fakeResourceConfigScope.LatestVersionReturns(nil, false, nil) 204 }) 205 206 It("does not store a version", func() { 207 Expect(fakeRunState.StoreResultCallCount()).To(Equal(0)) 208 }) 209 }) 210 }) 211 212 Context("running", func() { 213 var fakeLock *lockfakes.FakeLock 214 215 BeforeEach(func() { 216 fakeLock = new(lockfakes.FakeLock) 217 fakeDelegate.WaitToRunReturns(fakeLock, true, nil) 218 }) 219 220 Context("when given a from version", func() { 221 BeforeEach(func() { 222 checkPlan.FromVersion = atc.Version{"from": "version"} 223 }) 224 225 It("constructs the resource with the version", func() { 226 Expect(fakeResourceFactory.NewResourceCallCount()).To(Equal(1)) 227 _, _, fromVersion := fakeResourceFactory.NewResourceArgsForCall(0) 228 Expect(fromVersion).To(Equal(checkPlan.FromVersion)) 229 }) 230 }) 231 232 Context("when not given a from version", func() { 233 var fakeVersion *dbfakes.FakeResourceConfigVersion 234 235 BeforeEach(func() { 236 checkPlan.FromVersion = nil 237 238 fakeVersion = new(dbfakes.FakeResourceConfigVersion) 239 fakeVersion.VersionReturns(db.Version{"latest": "version"}) 240 fakeResourceConfigScope.LatestVersionStub = func() (db.ResourceConfigVersion, bool, error) { 241 Expect(fakeDelegate.WaitToRunCallCount()).To( 242 Equal(1), 243 "should have gotten latest version after waiting, not before", 244 ) 245 246 return fakeVersion, true, nil 247 } 248 }) 249 250 It("finds the latest version itself - it's a strong, independent check step who dont need no plan", func() { 251 Expect(fakeResourceFactory.NewResourceCallCount()).To(Equal(1)) 252 _, _, fromVersion := fakeResourceFactory.NewResourceArgsForCall(0) 253 Expect(fromVersion).To(Equal(atc.Version{"latest": "version"})) 254 }) 255 }) 256 257 Describe("running the check step", func() { 258 var runCtx context.Context 259 var owner db.ContainerOwner 260 var containerSpec worker.ContainerSpec 261 var workerSpec worker.WorkerSpec 262 var strategy worker.ContainerPlacementStrategy 263 var metadata db.ContainerMetadata 264 var processSpec runtime.ProcessSpec 265 var startEventDelegate runtime.StartingEventDelegate 266 var resource resource.Resource 267 var timeout time.Duration 268 269 JustBeforeEach(func() { 270 Expect(fakeClient.RunCheckStepCallCount()).To(Equal(1), "check step should have run") 271 runCtx, _, owner, containerSpec, workerSpec, strategy, metadata, processSpec, startEventDelegate, resource, timeout = fakeClient.RunCheckStepArgsForCall(0) 272 }) 273 274 It("uses ResourceConfigCheckSessionOwner", func() { 275 expected := db.NewBuildStepContainerOwner( 276 678, 277 planID, 278 345, 279 ) 280 281 Expect(owner).To(Equal(expected)) 282 }) 283 284 Context("when the plan is for a resource", func() { 285 BeforeEach(func() { 286 checkPlan.Resource = "some-resource" 287 }) 288 289 It("uses ResourceConfigCheckSessionOwner", func() { 290 expected := db.NewResourceConfigCheckSessionContainerOwner( 291 501, 292 502, 293 db.ContainerOwnerExpiries{Min: 5 * time.Minute, Max: 1 * time.Hour}, 294 ) 295 296 Expect(owner).To(Equal(expected)) 297 }) 298 }) 299 300 It("passes the process spec", func() { 301 Expect(processSpec).To(Equal(runtime.ProcessSpec{ 302 Path: "/opt/resource/check", 303 StdoutWriter: fakeStdout, 304 StderrWriter: fakeStderr, 305 })) 306 }) 307 308 It("passes the delegate as the start event delegate", func() { 309 Expect(startEventDelegate).To(Equal(fakeDelegate)) 310 }) 311 312 Context("uses containerspec", func() { 313 It("with certs volume mount", func() { 314 Expect(containerSpec.BindMounts).To(HaveLen(1)) 315 mount := containerSpec.BindMounts[0] 316 317 _, ok := mount.(*worker.CertsVolumeMount) 318 Expect(ok).To(BeTrue()) 319 }) 320 321 It("uses base type for image", func() { 322 Expect(containerSpec.ImageSpec).To(Equal(worker.ImageSpec{ 323 ResourceType: "some-base-type", 324 })) 325 }) 326 327 It("with teamid set", func() { 328 Expect(containerSpec.TeamID).To(Equal(345)) 329 }) 330 331 It("with env vars", func() { 332 Expect(containerSpec.Env).To(ContainElement("BUILD_TEAM_ID=345")) 333 }) 334 335 Context("when tracing is enabled", func() { 336 var buildSpan trace.Span 337 338 BeforeEach(func() { 339 tracing.ConfigureTraceProvider(tracetest.NewProvider()) 340 341 spanCtx, buildSpan = tracing.StartSpan(ctx, "build", nil) 342 fakeDelegate.StartSpanReturns(spanCtx, buildSpan) 343 }) 344 345 AfterEach(func() { 346 tracing.Configured = false 347 }) 348 349 It("propagates span context to the worker client", func() { 350 Expect(runCtx).To(Equal(spanCtx)) 351 }) 352 353 It("populates the TRACEPARENT env var", func() { 354 Expect(containerSpec.Env).To(ContainElement(MatchRegexp(`TRACEPARENT=.+`))) 355 }) 356 }) 357 }) 358 359 Context("uses workerspec", func() { 360 It("with resource type", func() { 361 Expect(workerSpec.ResourceType).To(Equal("some-base-type")) 362 }) 363 364 It("with teamid", func() { 365 Expect(workerSpec.TeamID).To(Equal(345)) 366 }) 367 368 Context("when the plan specifies tags", func() { 369 BeforeEach(func() { 370 checkPlan.Tags = atc.Tags{"some", "tags"} 371 }) 372 373 It("sets them in the WorkerSpec", func() { 374 Expect(workerSpec.Tags).To(Equal([]string{"some", "tags"})) 375 }) 376 }) 377 }) 378 379 It("uses container placement strategy", func() { 380 Expect(strategy).To(Equal(fakeStrategy)) 381 }) 382 383 It("uses container metadata", func() { 384 Expect(metadata).To(Equal(containerMetadata)) 385 }) 386 387 It("uses the timeout parsed", func() { 388 Expect(timeout).To(Equal(10 * time.Second)) 389 }) 390 391 It("uses the resource created", func() { 392 Expect(resource).To(Equal(fakeResource)) 393 }) 394 395 Context("when no timeout is given on the plan", func() { 396 BeforeEach(func() { 397 checkPlan.Timeout = "" 398 }) 399 400 It("uses the default timeout", func() { 401 Expect(timeout).To(Equal(time.Hour)) 402 }) 403 }) 404 405 Context("when using a custom resource type", func() { 406 var fakeImageSpec worker.ImageSpec 407 408 BeforeEach(func() { 409 checkPlan.Type = "some-custom-type" 410 411 fakeImageSpec = worker.ImageSpec{ 412 ImageArtifact: new(runtimefakes.FakeArtifact), 413 } 414 415 fakeDelegate.FetchImageReturns(fakeImageSpec, nil) 416 }) 417 418 It("fetches the resource type image and uses it for the container", func() { 419 Expect(fakeDelegate.FetchImageCallCount()).To(Equal(1)) 420 421 _, imageResource, types, privileged := fakeDelegate.FetchImageArgsForCall(0) 422 423 By("fetching the type image") 424 Expect(imageResource).To(Equal(atc.ImageResource{ 425 Name: "some-custom-type", 426 Type: "another-custom-type", 427 Source: atc.Source{"some-custom": "((source-var))"}, 428 Params: atc.Params{"some-custom": "((params-var))"}, 429 Version: atc.Version{"some-custom": "version"}, 430 })) 431 432 By("excluding the type from the FetchImage call") 433 Expect(types).To(Equal(atc.VersionedResourceTypes{ 434 { 435 ResourceType: atc.ResourceType{ 436 Name: "another-custom-type", 437 Type: "registry-image", 438 Source: atc.Source{"another-custom": "((source-var))"}, 439 Privileged: true, 440 }, 441 Version: atc.Version{"another-custom": "version"}, 442 }, 443 })) 444 445 By("not being privileged") 446 Expect(privileged).To(BeFalse()) 447 }) 448 449 It("sets the bottom-most type in the worker spec", func() { 450 Expect(workerSpec).To(Equal(worker.WorkerSpec{ 451 TeamID: stepMetadata.TeamID, 452 ResourceType: "registry-image", 453 })) 454 }) 455 456 It("sets the image spec in the container spec", func() { 457 Expect(containerSpec.ImageSpec).To(Equal(fakeImageSpec)) 458 }) 459 460 Context("when the resource type is privileged", func() { 461 BeforeEach(func() { 462 checkPlan.Type = "another-custom-type" 463 }) 464 465 It("fetches the image with privileged", func() { 466 Expect(fakeDelegate.FetchImageCallCount()).To(Equal(1)) 467 _, _, _, privileged := fakeDelegate.FetchImageArgsForCall(0) 468 Expect(privileged).To(BeTrue()) 469 }) 470 }) 471 472 Context("when the plan configures tags", func() { 473 BeforeEach(func() { 474 checkPlan.Tags = atc.Tags{"plan", "tags"} 475 }) 476 477 It("fetches using the tags", func() { 478 Expect(fakeDelegate.FetchImageCallCount()).To(Equal(1)) 479 _, imageResource, _, _ := fakeDelegate.FetchImageArgsForCall(0) 480 Expect(imageResource.Tags).To(Equal(atc.Tags{"plan", "tags"})) 481 }) 482 }) 483 484 Context("when the resource type configures tags", func() { 485 BeforeEach(func() { 486 taggedType, found := checkPlan.VersionedResourceTypes.Lookup("some-custom-type") 487 Expect(found).To(BeTrue()) 488 489 taggedType.Tags = atc.Tags{"type", "tags"} 490 491 newTypes := checkPlan.VersionedResourceTypes.Without("some-custom-type") 492 newTypes = append(newTypes, taggedType) 493 494 checkPlan.VersionedResourceTypes = newTypes 495 }) 496 497 It("fetches using the type tags", func() { 498 Expect(fakeDelegate.FetchImageCallCount()).To(Equal(1)) 499 _, imageResource, _, _ := fakeDelegate.FetchImageArgsForCall(0) 500 Expect(imageResource.Tags).To(Equal(atc.Tags{"type", "tags"})) 501 }) 502 503 Context("when the plan ALSO configures tags", func() { 504 BeforeEach(func() { 505 checkPlan.Tags = atc.Tags{"plan", "tags"} 506 }) 507 508 It("fetches using only the type tags", func() { 509 Expect(fakeDelegate.FetchImageCallCount()).To(Equal(1)) 510 _, imageResource, _, _ := fakeDelegate.FetchImageArgsForCall(0) 511 Expect(imageResource.Tags).To(Equal(atc.Tags{"type", "tags"})) 512 }) 513 }) 514 }) 515 }) 516 }) 517 518 Context("with tracing configured", func() { 519 var buildSpan trace.Span 520 521 BeforeEach(func() { 522 tracing.ConfigureTraceProvider(tracetest.NewProvider()) 523 524 spanCtx, buildSpan = tracing.StartSpan(context.Background(), "fake-operation", nil) 525 fakeDelegate.StartSpanReturns(spanCtx, buildSpan) 526 }) 527 528 AfterEach(func() { 529 tracing.Configured = false 530 }) 531 532 It("propagates span context to scope", func() { 533 Expect(fakeResourceConfigScope.SaveVersionsCallCount()).To(Equal(1)) 534 spanContext, _ := fakeResourceConfigScope.SaveVersionsArgsForCall(0) 535 traceID := buildSpan.SpanContext().TraceID.String() 536 traceParent := spanContext.Get("traceparent") 537 Expect(traceParent).To(ContainSubstring(traceID)) 538 }) 539 }) 540 541 Context("having RunCheckStep succeed", func() { 542 BeforeEach(func() { 543 fakeClient.RunCheckStepReturns(worker.CheckResult{ 544 Versions: []atc.Version{ 545 {"version": "1"}, 546 {"version": "2"}, 547 }, 548 }, nil) 549 }) 550 551 It("succeeds", func() { 552 Expect(stepOk).To(BeTrue()) 553 }) 554 555 It("saves the versions to the config scope", func() { 556 Expect(fakeResourceConfigFactory.FindOrCreateResourceConfigCallCount()).To(Equal(1)) 557 type_, source, types := fakeResourceConfigFactory.FindOrCreateResourceConfigArgsForCall(0) 558 Expect(type_).To(Equal("some-base-type")) 559 Expect(source).To(Equal(atc.Source{"some": "super-secret-source"})) 560 Expect(types).To(Equal(atc.VersionedResourceTypes{ 561 { 562 ResourceType: atc.ResourceType{ 563 Name: "some-custom-type", 564 Type: "another-custom-type", 565 Source: atc.Source{"some-custom": "super-secret-source"}, 566 567 // params don't need to be interpolated because it's used for 568 // fetching, not constructing the resource config 569 Params: atc.Params{"some-custom": "((params-var))"}, 570 }, 571 Version: atc.Version{"some-custom": "version"}, 572 }, 573 { 574 ResourceType: atc.ResourceType{ 575 Name: "another-custom-type", 576 Type: "registry-image", 577 Source: atc.Source{"another-custom": "super-secret-source"}, 578 Privileged: true, 579 }, 580 Version: atc.Version{"another-custom": "version"}, 581 }, 582 })) 583 584 Expect(fakeDelegate.FindOrCreateScopeCallCount()).To(Equal(1)) 585 config := fakeDelegate.FindOrCreateScopeArgsForCall(0) 586 Expect(config).To(Equal(fakeResourceConfig)) 587 588 spanContext, versions := fakeResourceConfigScope.SaveVersionsArgsForCall(0) 589 Expect(spanContext).To(Equal(db.SpanContext{})) 590 Expect(versions).To(Equal([]atc.Version{ 591 {"version": "1"}, 592 {"version": "2"}, 593 })) 594 }) 595 596 It("stores the latest version as the step result", func() { 597 Expect(fakeRunState.StoreResultCallCount()).To(Equal(1)) 598 id, val := fakeRunState.StoreResultArgsForCall(0) 599 Expect(id).To(Equal(atc.PlanID("some-plan-id"))) 600 Expect(val).To(Equal(atc.Version{"version": "2"})) 601 }) 602 603 It("emits a successful Finished event", func() { 604 Expect(fakeDelegate.FinishedCallCount()).To(Equal(1)) 605 _, succeeded := fakeDelegate.FinishedArgsForCall(0) 606 Expect(succeeded).To(BeTrue()) 607 }) 608 609 Context("when no versions are returned", func() { 610 BeforeEach(func() { 611 fakeClient.RunCheckStepReturns(worker.CheckResult{Versions: []atc.Version{}}, nil) 612 }) 613 614 It("succeeds", func() { 615 Expect(stepErr).ToNot(HaveOccurred()) 616 Expect(stepOk).To(BeTrue()) 617 }) 618 619 It("does not store a version", func() { 620 Expect(fakeRunState.StoreResultCallCount()).To(Equal(0)) 621 }) 622 }) 623 624 Context("before running the check", func() { 625 BeforeEach(func() { 626 fakeResourceConfigScope.UpdateLastCheckStartTimeStub = func() (bool, error) { 627 Expect(fakeClient.RunCheckStepCallCount()).To(Equal(0)) 628 return true, nil 629 } 630 }) 631 632 It("updates the scope's last check start time", func() { 633 Expect(fakeResourceConfigScope.UpdateLastCheckStartTimeCallCount()).To(Equal(1)) 634 Expect(fakeClient.RunCheckStepCallCount()).To(Equal(1)) 635 }) 636 }) 637 638 Context("after saving", func() { 639 BeforeEach(func() { 640 fakeResourceConfigScope.SaveVersionsStub = func(db.SpanContext, []atc.Version) error { 641 Expect(fakeDelegate.PointToCheckedConfigCallCount()).To(BeZero()) 642 Expect(fakeResourceConfigScope.UpdateLastCheckEndTimeCallCount()).To(Equal(0)) 643 return nil 644 } 645 }) 646 647 It("updates the scope's last check end time", func() { 648 Expect(fakeResourceConfigScope.UpdateLastCheckEndTimeCallCount()).To(Equal(1)) 649 }) 650 651 It("points the resource or resource type to the scope", func() { 652 Expect(fakeResourceConfigScope.SaveVersionsCallCount()).To(Equal(1)) 653 Expect(fakeDelegate.PointToCheckedConfigCallCount()).To(Equal(1)) 654 scope := fakeDelegate.PointToCheckedConfigArgsForCall(0) 655 Expect(scope).To(Equal(fakeResourceConfigScope)) 656 }) 657 }) 658 659 Context("after pointing the resource type to the scope", func() { 660 BeforeEach(func() { 661 fakeDelegate.PointToCheckedConfigStub = func(db.ResourceConfigScope) error { 662 Expect(fakeLock.ReleaseCallCount()).To(Equal(0)) 663 return nil 664 } 665 }) 666 667 It("releases the lock", func() { 668 Expect(fakeDelegate.PointToCheckedConfigCallCount()).To(Equal(1)) 669 Expect(fakeLock.ReleaseCallCount()).To(Equal(1)) 670 }) 671 }) 672 }) 673 674 Context("having RunCheckStep erroring", func() { 675 var expectedErr error 676 677 BeforeEach(func() { 678 expectedErr = errors.New("run-check-step-err") 679 fakeClient.RunCheckStepReturns(worker.CheckResult{}, expectedErr) 680 }) 681 682 It("errors", func() { 683 Expect(stepErr).To(HaveOccurred()) 684 Expect(errors.Is(stepErr, expectedErr)).To(BeTrue()) 685 }) 686 687 It("points the resource or resource type to the scope", func() { 688 // even though we failed to check, we should still point to the new 689 // scope; it'd be kind of weird leave the resource pointing to the old 690 // scope for a substantial config change that also happens to be 691 // broken. 692 Expect(fakeDelegate.PointToCheckedConfigCallCount()).To(Equal(1)) 693 scope := fakeDelegate.PointToCheckedConfigArgsForCall(0) 694 Expect(scope).To(Equal(fakeResourceConfigScope)) 695 }) 696 697 It("updates the scope's last check end time", func() { 698 Expect(fakeResourceConfigScope.UpdateLastCheckEndTimeCallCount()).To(Equal(1)) 699 }) 700 701 // Finished is for script success/failure, whereas this is an error 702 It("does not emit a Finished event", func() { 703 Expect(fakeDelegate.FinishedCallCount()).To(Equal(0)) 704 }) 705 706 Context("with a script failure", func() { 707 BeforeEach(func() { 708 fakeClient.RunCheckStepReturns(worker.CheckResult{}, runtime.ErrResourceScriptFailed{ 709 ExitStatus: 42, 710 }) 711 }) 712 713 It("does not error", func() { 714 // don't return an error - the script output has already been 715 // printed, and emitting an errored event would double it up 716 Expect(stepErr).ToNot(HaveOccurred()) 717 }) 718 719 It("updates the scope's last check end time", func() { 720 Expect(fakeResourceConfigScope.UpdateLastCheckEndTimeCallCount()).To(Equal(1)) 721 }) 722 723 It("emits a failed Finished event", func() { 724 Expect(fakeDelegate.FinishedCallCount()).To(Equal(1)) 725 _, succeeded := fakeDelegate.FinishedArgsForCall(0) 726 Expect(succeeded).To(BeFalse()) 727 }) 728 }) 729 }) 730 731 Context("having SaveVersions failing", func() { 732 var expectedErr error 733 734 BeforeEach(func() { 735 expectedErr = errors.New("save-versions-err") 736 737 fakeResourceConfigScope.SaveVersionsReturns(expectedErr) 738 }) 739 740 It("errors", func() { 741 Expect(stepErr).To(HaveOccurred()) 742 Expect(errors.Is(stepErr, expectedErr)).To(BeTrue()) 743 }) 744 }) 745 }) 746 }) 747 748 Context("having credentials in the config", func() { 749 BeforeEach(func() { 750 checkPlan.Source = atc.Source{"some": "((super-secret-source))"} 751 }) 752 753 Context("having cred evaluation failing", func() { 754 var expectedErr error 755 756 BeforeEach(func() { 757 expectedErr = errors.New("creds-err") 758 759 fakeRunState.GetReturns(nil, false, expectedErr) 760 }) 761 762 It("errors", func() { 763 Expect(stepErr).To(HaveOccurred()) 764 Expect(errors.Is(stepErr, expectedErr)).To(BeTrue()) 765 }) 766 }) 767 }) 768 769 Context("having credentials in a resource type", func() { 770 BeforeEach(func() { 771 resTypes := atc.VersionedResourceTypes{ 772 { 773 ResourceType: atc.ResourceType{ 774 Source: atc.Source{ 775 "some-custom": "((super-secret-source))", 776 }, 777 }, 778 }, 779 } 780 781 checkPlan.VersionedResourceTypes = resTypes 782 }) 783 784 Context("having cred evaluation failing", func() { 785 var expectedErr error 786 787 BeforeEach(func() { 788 expectedErr = errors.New("creds-err") 789 790 fakeRunState.GetReturns(nil, false, expectedErr) 791 }) 792 793 It("errors", func() { 794 Expect(stepErr).To(HaveOccurred()) 795 Expect(errors.Is(stepErr, expectedErr)).To(BeTrue()) 796 }) 797 }) 798 }) 799 800 Context("having a timeout that fails parsing", func() { 801 BeforeEach(func() { 802 checkPlan.Timeout = "bogus" 803 }) 804 805 It("errors", func() { 806 Expect(stepErr).To(HaveOccurred()) 807 Expect(stepErr.Error()).To(ContainSubstring("invalid duration")) 808 }) 809 }) 810 })