github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/engine/build_step_delegate_test.go (about) 1 package engine_test 2 3 import ( 4 "context" 5 "errors" 6 "io" 7 "time" 8 9 . "github.com/onsi/ginkgo" 10 . "github.com/onsi/gomega" 11 12 "code.cloudfoundry.org/clock/fakeclock" 13 "code.cloudfoundry.org/lager" 14 "code.cloudfoundry.org/lager/lagertest" 15 "github.com/pf-qiu/concourse/v6/atc" 16 "github.com/pf-qiu/concourse/v6/atc/db" 17 "github.com/pf-qiu/concourse/v6/atc/db/dbfakes" 18 "github.com/pf-qiu/concourse/v6/atc/engine" 19 "github.com/pf-qiu/concourse/v6/atc/event" 20 "github.com/pf-qiu/concourse/v6/atc/exec" 21 "github.com/pf-qiu/concourse/v6/atc/exec/build" 22 "github.com/pf-qiu/concourse/v6/atc/exec/execfakes" 23 "github.com/pf-qiu/concourse/v6/atc/policy" 24 "github.com/pf-qiu/concourse/v6/atc/policy/policyfakes" 25 "github.com/pf-qiu/concourse/v6/atc/runtime/runtimefakes" 26 "github.com/pf-qiu/concourse/v6/atc/worker" 27 "github.com/pf-qiu/concourse/v6/vars" 28 ) 29 30 var _ = Describe("BuildStepDelegate", func() { 31 var ( 32 logger *lagertest.TestLogger 33 fakeBuild *dbfakes.FakeBuild 34 fakeClock *fakeclock.FakeClock 35 planID atc.PlanID 36 runState *execfakes.FakeRunState 37 fakePolicyChecker *policyfakes.FakeChecker 38 39 credVars vars.StaticVariables 40 41 now = time.Date(1991, 6, 3, 5, 30, 0, 0, time.UTC) 42 43 delegate exec.BuildStepDelegate 44 ) 45 46 BeforeEach(func() { 47 logger = lagertest.NewTestLogger("test") 48 49 fakeBuild = new(dbfakes.FakeBuild) 50 fakeClock = fakeclock.NewFakeClock(now) 51 credVars = vars.StaticVariables{ 52 "source-param": "super-secret-source", 53 "git-key": "{\n123\n456\n789\n}\n", 54 } 55 planID = "some-plan-id" 56 57 runState = new(execfakes.FakeRunState) 58 runState.RedactionEnabledReturns(true) 59 60 repo := build.NewRepository() 61 runState.ArtifactRepositoryReturns(repo) 62 63 fakePolicyChecker = new(policyfakes.FakeChecker) 64 65 delegate = engine.NewBuildStepDelegate(fakeBuild, planID, runState, fakeClock, fakePolicyChecker) 66 }) 67 68 Describe("Initializing", func() { 69 JustBeforeEach(func() { 70 delegate.Initializing(logger) 71 }) 72 73 It("saves an event", func() { 74 Expect(fakeBuild.SaveEventCallCount()).To(Equal(1)) 75 event := fakeBuild.SaveEventArgsForCall(0) 76 Expect(event.EventType()).To(Equal(atc.EventType("initialize"))) 77 }) 78 }) 79 80 Describe("Finished", func() { 81 JustBeforeEach(func() { 82 delegate.Finished(logger, true) 83 }) 84 85 It("saves an event", func() { 86 Expect(fakeBuild.SaveEventCallCount()).To(Equal(1)) 87 event := fakeBuild.SaveEventArgsForCall(0) 88 Expect(event.EventType()).To(Equal(atc.EventType("finish"))) 89 }) 90 }) 91 92 Describe("FetchImage", func() { 93 var expectedCheckPlan, expectedGetPlan atc.Plan 94 var fakeArtifact *runtimefakes.FakeArtifact 95 var fakeResourceCache *dbfakes.FakeUsedResourceCache 96 97 var childState *execfakes.FakeRunState 98 var imageResource atc.ImageResource 99 var types atc.VersionedResourceTypes 100 var privileged bool 101 102 var imageSpec worker.ImageSpec 103 var fetchErr error 104 105 BeforeEach(func() { 106 repo := build.NewRepository() 107 runState.ArtifactRepositoryReturns(repo) 108 109 childState = new(execfakes.FakeRunState) 110 runState.NewLocalScopeReturns(childState) 111 112 fakeArtifact = new(runtimefakes.FakeArtifact) 113 childState.ArtifactRepositoryReturns(repo.NewLocalScope()) 114 childState.ArtifactRepository().RegisterArtifact("image", fakeArtifact) 115 116 runState.GetStub = vars.StaticVariables{ 117 "source-var": "super-secret-source", 118 "params-var": "super-secret-params", 119 }.Get 120 121 imageResource = atc.ImageResource{ 122 Type: "docker", 123 Source: atc.Source{"some": "((source-var))"}, 124 Params: atc.Params{"some": "((params-var))"}, 125 Tags: atc.Tags{"some", "tags"}, 126 } 127 128 types = atc.VersionedResourceTypes{ 129 { 130 ResourceType: atc.ResourceType{ 131 Name: "some-custom-type", 132 Type: "another-custom-type", 133 Source: atc.Source{"some-custom": "((source-var))"}, 134 Params: atc.Params{"some-custom": "((params-var))"}, 135 }, 136 Version: atc.Version{"some-custom": "version"}, 137 }, 138 { 139 ResourceType: atc.ResourceType{ 140 Name: "another-custom-type", 141 Type: "registry-image", 142 Source: atc.Source{"another-custom": "((source-var))"}, 143 Privileged: true, 144 }, 145 Version: atc.Version{"another-custom": "version"}, 146 }, 147 } 148 149 expectedCheckPlan = atc.Plan{ 150 ID: planID + "/image-check", 151 Check: &atc.CheckPlan{ 152 Name: "image", 153 Type: "docker", 154 Source: atc.Source{"some": "((source-var))"}, 155 VersionedResourceTypes: types, 156 Tags: atc.Tags{"some", "tags"}, 157 }, 158 } 159 160 expectedGetPlan = atc.Plan{ 161 ID: planID + "/image-get", 162 Get: &atc.GetPlan{ 163 Name: "image", 164 Type: "docker", 165 Source: atc.Source{"some": "((source-var))"}, 166 Version: &atc.Version{"some": "version"}, 167 Params: atc.Params{"some": "((params-var))"}, 168 VersionedResourceTypes: types, 169 Tags: atc.Tags{"some", "tags"}, 170 }, 171 } 172 173 fakeResourceCache = new(dbfakes.FakeUsedResourceCache) 174 175 childState.ResultStub = func(planID atc.PlanID, to interface{}) bool { 176 switch planID { 177 case expectedCheckPlan.ID: 178 switch x := to.(type) { 179 case *atc.Version: 180 *x = atc.Version{"some": "version"} 181 default: 182 Fail("unexpected target type") 183 } 184 case expectedGetPlan.ID: 185 switch x := to.(type) { 186 case *db.UsedResourceCache: 187 *x = fakeResourceCache 188 default: 189 Fail("unexpected target type") 190 } 191 default: 192 Fail("unknown result key: " + planID.String()) 193 } 194 195 return true 196 } 197 198 privileged = false 199 200 childState.RunReturns(true, nil) 201 }) 202 203 JustBeforeEach(func() { 204 imageSpec, fetchErr = delegate.FetchImage(context.TODO(), imageResource, types, privileged) 205 }) 206 207 It("succeeds", func() { 208 Expect(fetchErr).ToNot(HaveOccurred()) 209 }) 210 211 It("returns an image spec containing the artifact", func() { 212 Expect(imageSpec).To(Equal(worker.ImageSpec{ 213 ImageArtifact: fakeArtifact, 214 Privileged: false, 215 })) 216 }) 217 218 It("runs a CheckPlan to get the image version", func() { 219 Expect(childState.RunCallCount()).To(Equal(2)) 220 221 _, plan := childState.RunArgsForCall(0) 222 Expect(plan).To(Equal(expectedCheckPlan)) 223 224 _, plan = childState.RunArgsForCall(1) 225 Expect(plan).To(Equal(expectedGetPlan)) 226 }) 227 228 It("records the resource cache as an image resource for the build", func() { 229 Expect(fakeBuild.SaveImageResourceVersionCallCount()).To(Equal(1)) 230 Expect(fakeBuild.SaveImageResourceVersionArgsForCall(0)).To(Equal(fakeResourceCache)) 231 }) 232 233 Context("when privileged", func() { 234 BeforeEach(func() { 235 privileged = true 236 }) 237 238 It("returns a privileged image spec", func() { 239 Expect(imageSpec).To(Equal(worker.ImageSpec{ 240 ImageArtifact: fakeArtifact, 241 Privileged: true, 242 })) 243 }) 244 }) 245 246 Describe("policy checking", func() { 247 BeforeEach(func() { 248 fakeBuild.TeamNameReturns("some-team") 249 fakeBuild.PipelineNameReturns("some-pipeline") 250 }) 251 252 Context("when the action does not need to be checked", func() { 253 BeforeEach(func() { 254 fakePolicyChecker.ShouldCheckActionReturns(false) 255 }) 256 257 It("succeeds", func() { 258 Expect(fetchErr).ToNot(HaveOccurred()) 259 }) 260 261 It("checked if ActionUseImage is enabled", func() { 262 Expect(fakePolicyChecker.ShouldCheckActionCallCount()).To(Equal(1)) 263 action := fakePolicyChecker.ShouldCheckActionArgsForCall(0) 264 Expect(action).To(Equal(policy.ActionUseImage)) 265 }) 266 267 It("does not check", func() { 268 Expect(fakePolicyChecker.CheckCallCount()).To(Equal(0)) 269 }) 270 }) 271 272 Context("when the action needs to be checked", func() { 273 BeforeEach(func() { 274 fakePolicyChecker.ShouldCheckActionReturns(true) 275 }) 276 277 Context("when the check is allowed", func() { 278 BeforeEach(func() { 279 fakePolicyChecker.CheckReturns(policy.PolicyCheckOutput{ 280 Allowed: true, 281 }, nil) 282 }) 283 284 It("succeeds", func() { 285 Expect(fetchErr).ToNot(HaveOccurred()) 286 }) 287 288 It("checked with the right values", func() { 289 Expect(fakePolicyChecker.CheckCallCount()).To(Equal(1)) 290 input := fakePolicyChecker.CheckArgsForCall(0) 291 Expect(input).To(Equal(policy.PolicyCheckInput{ 292 Action: policy.ActionUseImage, 293 Team: "some-team", 294 Pipeline: "some-pipeline", 295 Data: map[string]interface{}{ 296 "image_type": "docker", 297 "image_source": atc.Source{"some": "((source-var))"}, 298 "privileged": false, 299 }, 300 })) 301 }) 302 303 Context("when the image source contains credentials", func() { 304 BeforeEach(func() { 305 imageResource.Source = atc.Source{"some": "super-secret-source"} 306 307 runState.IterateInterpolatedCredsStub = func(iter vars.TrackedVarsIterator) { 308 iter.YieldCred("source-var", "super-secret-source") 309 } 310 }) 311 312 It("redacts the value prior to checking", func() { 313 Expect(fakePolicyChecker.CheckCallCount()).To(Equal(1)) 314 input := fakePolicyChecker.CheckArgsForCall(0) 315 Expect(input).To(Equal(policy.PolicyCheckInput{ 316 Action: policy.ActionUseImage, 317 Team: "some-team", 318 Pipeline: "some-pipeline", 319 Data: map[string]interface{}{ 320 "image_type": "docker", 321 "image_source": atc.Source{"some": "((redacted))"}, 322 "privileged": false, 323 }, 324 })) 325 }) 326 }) 327 328 Context("when privileged", func() { 329 BeforeEach(func() { 330 privileged = true 331 }) 332 333 It("checks with privileged", func() { 334 Expect(fakePolicyChecker.CheckCallCount()).To(Equal(1)) 335 input := fakePolicyChecker.CheckArgsForCall(0) 336 Expect(input).To(Equal(policy.PolicyCheckInput{ 337 Action: policy.ActionUseImage, 338 Team: "some-team", 339 Pipeline: "some-pipeline", 340 Data: map[string]interface{}{ 341 "image_type": "docker", 342 "image_source": atc.Source{"some": "((source-var))"}, 343 "privileged": true, 344 }, 345 })) 346 }) 347 }) 348 }) 349 }) 350 }) 351 352 Describe("ordering", func() { 353 BeforeEach(func() { 354 fakeBuild.SaveEventStub = func(ev atc.Event) error { 355 switch ev.(type) { 356 case event.ImageCheck: 357 Expect(childState.RunCallCount()).To(Equal(0)) 358 case event.ImageGet: 359 Expect(childState.RunCallCount()).To(Equal(1)) 360 default: 361 Fail("unknown event type") 362 } 363 return nil 364 } 365 }) 366 367 It("sends events before each run", func() { 368 Expect(fakeBuild.SaveEventCallCount()).To(Equal(2)) 369 e := fakeBuild.SaveEventArgsForCall(0) 370 Expect(e).To(Equal(event.ImageCheck{ 371 Time: 675927000, 372 Origin: event.Origin{ 373 ID: event.OriginID(planID), 374 }, 375 PublicPlan: expectedCheckPlan.Public(), 376 })) 377 378 e = fakeBuild.SaveEventArgsForCall(1) 379 Expect(e).To(Equal(event.ImageGet{ 380 Time: 675927000, 381 Origin: event.Origin{ 382 ID: event.OriginID(planID), 383 }, 384 PublicPlan: expectedGetPlan.Public(), 385 })) 386 }) 387 }) 388 389 Context("when a version is already provided", func() { 390 BeforeEach(func() { 391 imageResource.Version = atc.Version{"some": "version"} 392 }) 393 394 It("does not run a CheckPlan", func() { 395 Expect(childState.RunCallCount()).To(Equal(1)) 396 _, plan := childState.RunArgsForCall(0) 397 Expect(plan).To(Equal(expectedGetPlan)) 398 399 Expect(childState.ResultCallCount()).To(Equal(1)) 400 planID, _ := childState.ResultArgsForCall(0) 401 Expect(planID).To(Equal(expectedGetPlan.ID)) 402 }) 403 404 It("only saves an ImageGet event", func() { 405 Expect(fakeBuild.SaveEventCallCount()).To(Equal(1)) 406 e := fakeBuild.SaveEventArgsForCall(0) 407 Expect(e).To(Equal(event.ImageGet{ 408 Time: 675927000, 409 Origin: event.Origin{ 410 ID: event.OriginID(planID), 411 }, 412 PublicPlan: expectedGetPlan.Public(), 413 })) 414 }) 415 }) 416 417 Context("when an image name is provided", func() { 418 var namedArtifact *runtimefakes.FakeArtifact 419 420 BeforeEach(func() { 421 imageResource.Name = "some-name" 422 expectedCheckPlan.Check.Name = "some-name" 423 expectedGetPlan.Get.Name = "some-name" 424 425 namedArtifact = new(runtimefakes.FakeArtifact) 426 childState.ArtifactRepositoryReturns(runState.ArtifactRepository().NewLocalScope()) 427 childState.ArtifactRepository().RegisterArtifact("some-name", namedArtifact) 428 }) 429 430 It("uses it for the step names", func() { 431 Expect(childState.RunCallCount()).To(Equal(2)) 432 _, plan := childState.RunArgsForCall(0) 433 Expect(plan.Check.Name).To(Equal("some-name")) 434 _, plan = childState.RunArgsForCall(1) 435 Expect(plan.Get.Name).To(Equal("some-name")) 436 437 Expect(imageSpec.ImageArtifact).To(Equal(namedArtifact)) 438 }) 439 }) 440 441 Context("when checking the image fails", func() { 442 BeforeEach(func() { 443 childState.RunStub = func(ctx context.Context, plan atc.Plan) (bool, error) { 444 if plan.ID == expectedCheckPlan.ID { 445 return false, nil 446 } 447 448 return true, nil 449 } 450 }) 451 452 It("errors", func() { 453 Expect(fetchErr).To(MatchError("image check failed")) 454 }) 455 }) 456 457 Context("when no version is returned by the check", func() { 458 BeforeEach(func() { 459 childState.ResultReturns(false) 460 }) 461 462 It("errors", func() { 463 Expect(fetchErr).To(MatchError("check did not return a version")) 464 }) 465 }) 466 }) 467 468 Describe("Stdout", func() { 469 var writer io.Writer 470 471 BeforeEach(func() { 472 writer = delegate.Stdout() 473 }) 474 475 Describe("writing to the writer", func() { 476 var writtenBytes int 477 var writeErr error 478 479 JustBeforeEach(func() { 480 writtenBytes, writeErr = writer.Write([]byte("hello\nworld")) 481 writer.(io.Closer).Close() 482 }) 483 484 Context("when saving the event succeeds", func() { 485 BeforeEach(func() { 486 fakeBuild.SaveEventReturns(nil) 487 }) 488 489 It("returns the length of the string, and no error", func() { 490 Expect(writtenBytes).To(Equal(len("hello\nworld"))) 491 Expect(writeErr).ToNot(HaveOccurred()) 492 }) 493 494 It("saves a log event", func() { 495 Expect(fakeBuild.SaveEventCallCount()).To(Equal(2)) 496 Expect(fakeBuild.SaveEventArgsForCall(0)).To(Equal(event.Log{ 497 Time: now.Unix(), 498 Payload: "hello\n", 499 Origin: event.Origin{ 500 Source: event.OriginSourceStdout, 501 ID: "some-plan-id", 502 }, 503 })) 504 Expect(fakeBuild.SaveEventArgsForCall(1)).To(Equal(event.Log{ 505 Time: now.Unix(), 506 Payload: "world", 507 Origin: event.Origin{ 508 Source: event.OriginSourceStdout, 509 ID: "some-plan-id", 510 }, 511 })) 512 }) 513 }) 514 515 Context("when saving the event fails", func() { 516 disaster := errors.New("nope") 517 518 BeforeEach(func() { 519 fakeBuild.SaveEventReturns(disaster) 520 }) 521 522 It("returns 0 length, and the error", func() { 523 Expect(writtenBytes).To(Equal(0)) 524 Expect(writeErr).To(Equal(disaster)) 525 }) 526 }) 527 }) 528 }) 529 530 Describe("Stderr", func() { 531 var writer io.Writer 532 533 BeforeEach(func() { 534 writer = delegate.Stderr() 535 }) 536 537 Describe("writing to the writer", func() { 538 var writtenBytes int 539 var writeErr error 540 541 JustBeforeEach(func() { 542 writtenBytes, writeErr = writer.Write([]byte("hello\n")) 543 writer.(io.Closer).Close() 544 }) 545 546 Context("when saving the event succeeds", func() { 547 BeforeEach(func() { 548 fakeBuild.SaveEventReturns(nil) 549 }) 550 551 It("returns the length of the string, and no error", func() { 552 Expect(writtenBytes).To(Equal(len("hello\n"))) 553 Expect(writeErr).ToNot(HaveOccurred()) 554 }) 555 556 It("saves a log event", func() { 557 Expect(fakeBuild.SaveEventCallCount()).To(Equal(1)) 558 Expect(fakeBuild.SaveEventArgsForCall(0)).To(Equal(event.Log{ 559 Time: now.Unix(), 560 Payload: "hello\n", 561 Origin: event.Origin{ 562 Source: event.OriginSourceStderr, 563 ID: "some-plan-id", 564 }, 565 })) 566 }) 567 }) 568 569 Context("when saving the event fails", func() { 570 disaster := errors.New("nope") 571 572 BeforeEach(func() { 573 fakeBuild.SaveEventReturns(disaster) 574 }) 575 576 It("returns 0 length, and the error", func() { 577 Expect(writtenBytes).To(Equal(0)) 578 Expect(writeErr).To(Equal(disaster)) 579 }) 580 }) 581 }) 582 }) 583 584 Describe("Errored", func() { 585 JustBeforeEach(func() { 586 delegate.Errored(logger, "fake error message") 587 }) 588 589 Context("when saving the event succeeds", func() { 590 BeforeEach(func() { 591 fakeBuild.SaveEventReturns(nil) 592 }) 593 594 It("saves it with the current time", func() { 595 Expect(fakeBuild.SaveEventCallCount()).To(Equal(1)) 596 Expect(fakeBuild.SaveEventArgsForCall(0)).To(Equal(event.Error{ 597 Time: now.Unix(), 598 Message: "fake error message", 599 Origin: event.Origin{ 600 ID: "some-plan-id", 601 }, 602 })) 603 }) 604 }) 605 606 Context("when saving the event fails", func() { 607 disaster := errors.New("nope") 608 609 BeforeEach(func() { 610 fakeBuild.SaveEventReturns(disaster) 611 }) 612 613 It("logs an error", func() { 614 logs := logger.Logs() 615 Expect(len(logs)).To(Equal(1)) 616 Expect(logs[0].Message).To(Equal("test.failed-to-save-error-event")) 617 Expect(logs[0].Data).To(Equal(lager.Data{"error": "nope"})) 618 }) 619 }) 620 }) 621 622 Describe("No line buffer without secrets redaction", func() { 623 var runState exec.RunState 624 625 BeforeEach(func() { 626 credVars := vars.StaticVariables{} 627 runState = exec.NewRunState(noopStepper, credVars, false) 628 delegate = engine.NewBuildStepDelegate(fakeBuild, "some-plan-id", runState, fakeClock, fakePolicyChecker) 629 }) 630 631 Context("Stdout", func() { 632 It("should not buffer lines", func() { 633 writer := delegate.Stdout() 634 writtenBytes, writeErr := writer.Write([]byte("1\r")) 635 Expect(writeErr).To(BeNil()) 636 Expect(writtenBytes).To(Equal(len("1\r"))) 637 writtenBytes, writeErr = writer.Write([]byte("2\r")) 638 Expect(writeErr).To(BeNil()) 639 Expect(writtenBytes).To(Equal(len("2\r"))) 640 writtenBytes, writeErr = writer.Write([]byte("3\r")) 641 Expect(writeErr).To(BeNil()) 642 Expect(writtenBytes).To(Equal(len("3\r"))) 643 writeErr = writer.(io.Closer).Close() 644 Expect(writeErr).To(BeNil()) 645 646 Expect(fakeBuild.SaveEventCallCount()).To(Equal(3)) 647 Expect(fakeBuild.SaveEventArgsForCall(0)).To(Equal(event.Log{ 648 Time: now.Unix(), 649 Payload: "1\r", 650 Origin: event.Origin{ 651 Source: event.OriginSourceStdout, 652 ID: "some-plan-id", 653 }, 654 })) 655 Expect(fakeBuild.SaveEventArgsForCall(1)).To(Equal(event.Log{ 656 Time: now.Unix(), 657 Payload: "2\r", 658 Origin: event.Origin{ 659 Source: event.OriginSourceStdout, 660 ID: "some-plan-id", 661 }, 662 })) 663 Expect(fakeBuild.SaveEventArgsForCall(2)).To(Equal(event.Log{ 664 Time: now.Unix(), 665 Payload: "3\r", 666 Origin: event.Origin{ 667 Source: event.OriginSourceStdout, 668 ID: "some-plan-id", 669 }, 670 })) 671 }) 672 }) 673 674 Context("Stderr", func() { 675 It("should not buffer lines", func() { 676 writer := delegate.Stderr() 677 writtenBytes, writeErr := writer.Write([]byte("1\r")) 678 Expect(writeErr).To(BeNil()) 679 Expect(writtenBytes).To(Equal(len("1\r"))) 680 writtenBytes, writeErr = writer.Write([]byte("2\r")) 681 Expect(writeErr).To(BeNil()) 682 Expect(writtenBytes).To(Equal(len("2\r"))) 683 writtenBytes, writeErr = writer.Write([]byte("3\r")) 684 Expect(writeErr).To(BeNil()) 685 Expect(writtenBytes).To(Equal(len("3\r"))) 686 writeErr = writer.(io.Closer).Close() 687 Expect(writeErr).To(BeNil()) 688 689 Expect(fakeBuild.SaveEventCallCount()).To(Equal(3)) 690 Expect(fakeBuild.SaveEventArgsForCall(0)).To(Equal(event.Log{ 691 Time: now.Unix(), 692 Payload: "1\r", 693 Origin: event.Origin{ 694 Source: event.OriginSourceStderr, 695 ID: "some-plan-id", 696 }, 697 })) 698 Expect(fakeBuild.SaveEventArgsForCall(1)).To(Equal(event.Log{ 699 Time: now.Unix(), 700 Payload: "2\r", 701 Origin: event.Origin{ 702 Source: event.OriginSourceStderr, 703 ID: "some-plan-id", 704 }, 705 })) 706 Expect(fakeBuild.SaveEventArgsForCall(2)).To(Equal(event.Log{ 707 Time: now.Unix(), 708 Payload: "3\r", 709 Origin: event.Origin{ 710 Source: event.OriginSourceStderr, 711 ID: "some-plan-id", 712 }, 713 })) 714 }) 715 }) 716 }) 717 718 Describe("Secrets redaction", func() { 719 var ( 720 runState exec.RunState 721 writer io.Writer 722 writtenBytes int 723 writeErr error 724 ) 725 726 BeforeEach(func() { 727 runState = exec.NewRunState(noopStepper, credVars, true) 728 delegate = engine.NewBuildStepDelegate(fakeBuild, "some-plan-id", runState, fakeClock, fakePolicyChecker) 729 730 runState.Get(vars.Reference{Path: "source-param"}) 731 runState.Get(vars.Reference{Path: "git-key"}) 732 }) 733 734 Context("Stdout", func() { 735 Context("single-line secret", func() { 736 JustBeforeEach(func() { 737 writer = delegate.Stdout() 738 writtenBytes, writeErr = writer.Write([]byte("ok super-secret-source ok")) 739 writer.(io.Closer).Close() 740 }) 741 742 It("should be redacted", func() { 743 Expect(writeErr).To(BeNil()) 744 Expect(writtenBytes).To(Equal(len("ok super-secret-source ok"))) 745 Expect(fakeBuild.SaveEventCallCount()).To(Equal(1)) 746 Expect(fakeBuild.SaveEventArgsForCall(0)).To(Equal(event.Log{ 747 Time: now.Unix(), 748 Payload: "ok ((redacted)) ok", 749 Origin: event.Origin{ 750 Source: event.OriginSourceStdout, 751 ID: "some-plan-id", 752 }, 753 })) 754 }) 755 }) 756 757 Context("multi-line secret", func() { 758 var logLines string 759 760 JustBeforeEach(func() { 761 logLines = "ok123ok\nok456ok\nok789ok\n" 762 writer = delegate.Stdout() 763 writtenBytes, writeErr = writer.Write([]byte(logLines)) 764 writer.(io.Closer).Close() 765 }) 766 767 It("should be redacted", func() { 768 Expect(writeErr).To(BeNil()) 769 Expect(writtenBytes).To(Equal(len(logLines))) 770 Expect(fakeBuild.SaveEventCallCount()).To(Equal(1)) 771 Expect(fakeBuild.SaveEventArgsForCall(0)).To(Equal(event.Log{ 772 Time: now.Unix(), 773 Payload: "ok((redacted))ok\nok((redacted))ok\nok((redacted))ok\n", 774 Origin: event.Origin{ 775 Source: event.OriginSourceStdout, 776 ID: "some-plan-id", 777 }, 778 })) 779 }) 780 }) 781 782 Context("multi-line secret with random log chunk", func() { 783 JustBeforeEach(func() { 784 writer = delegate.Stdout() 785 writtenBytes, writeErr = writer.Write([]byte("ok123ok\nok4")) 786 writtenBytes, writeErr = writer.Write([]byte("56ok\nok789ok\n")) 787 writer.(io.Closer).Close() 788 }) 789 790 It("should be redacted", func() { 791 Expect(fakeBuild.SaveEventCallCount()).To(Equal(2)) 792 Expect(fakeBuild.SaveEventArgsForCall(0)).To(Equal(event.Log{ 793 Time: now.Unix(), 794 Payload: "ok((redacted))ok\n", 795 Origin: event.Origin{ 796 Source: event.OriginSourceStdout, 797 ID: "some-plan-id", 798 }, 799 })) 800 Expect(fakeBuild.SaveEventArgsForCall(1)).To(Equal(event.Log{ 801 Time: now.Unix(), 802 Payload: "ok((redacted))ok\nok((redacted))ok\n", 803 Origin: event.Origin{ 804 Source: event.OriginSourceStdout, 805 ID: "some-plan-id", 806 }, 807 })) 808 }) 809 }) 810 }) 811 812 Context("Stderr", func() { 813 Context("single-line secret", func() { 814 JustBeforeEach(func() { 815 writer = delegate.Stderr() 816 writtenBytes, writeErr = writer.Write([]byte("ok super-secret-source ok")) 817 writer.(io.Closer).Close() 818 }) 819 820 It("should be redacted", func() { 821 Expect(writeErr).To(BeNil()) 822 Expect(writtenBytes).To(Equal(len("ok super-secret-source ok"))) 823 Expect(fakeBuild.SaveEventCallCount()).To(Equal(1)) 824 Expect(fakeBuild.SaveEventArgsForCall(0)).To(Equal(event.Log{ 825 Time: now.Unix(), 826 Payload: "ok ((redacted)) ok", 827 Origin: event.Origin{ 828 Source: event.OriginSourceStderr, 829 ID: "some-plan-id", 830 }, 831 })) 832 }) 833 }) 834 835 Context("multi-line secret", func() { 836 var logLines string 837 838 JustBeforeEach(func() { 839 logLines = "{\nok123ok\nok456ok\nok789ok\n}\n" 840 writer = delegate.Stderr() 841 writtenBytes, writeErr = writer.Write([]byte(logLines)) 842 writer.(io.Closer).Close() 843 }) 844 845 It("should be redacted", func() { 846 Expect(writeErr).To(BeNil()) 847 Expect(writtenBytes).To(Equal(len(logLines))) 848 Expect(fakeBuild.SaveEventCallCount()).To(Equal(1)) 849 Expect(fakeBuild.SaveEventArgsForCall(0)).To(Equal(event.Log{ 850 Time: now.Unix(), 851 Payload: "{\nok((redacted))ok\nok((redacted))ok\nok((redacted))ok\n}\n", 852 Origin: event.Origin{ 853 Source: event.OriginSourceStderr, 854 ID: "some-plan-id", 855 }, 856 })) 857 }) 858 }) 859 860 Context("multi-line secret with random log chunk", func() { 861 JustBeforeEach(func() { 862 writer = delegate.Stderr() 863 writtenBytes, writeErr = writer.Write([]byte("ok123ok\nok4")) 864 writtenBytes, writeErr = writer.Write([]byte("56ok\nok789ok\n")) 865 writer.(io.Closer).Close() 866 }) 867 868 It("should be redacted", func() { 869 Expect(fakeBuild.SaveEventCallCount()).To(Equal(2)) 870 Expect(fakeBuild.SaveEventArgsForCall(0)).To(Equal(event.Log{ 871 Time: now.Unix(), 872 Payload: "ok((redacted))ok\n", 873 Origin: event.Origin{ 874 Source: event.OriginSourceStderr, 875 ID: "some-plan-id", 876 }, 877 })) 878 Expect(fakeBuild.SaveEventArgsForCall(1)).To(Equal(event.Log{ 879 Time: now.Unix(), 880 Payload: "ok((redacted))ok\nok((redacted))ok\n", 881 Origin: event.Origin{ 882 Source: event.OriginSourceStderr, 883 ID: "some-plan-id", 884 }, 885 })) 886 }) 887 }) 888 }) 889 }) 890 })