It("runs the ensured hook even if the step errors", func() { step.RunReturns(errors.New("disaster")) process := ifrit.Background(ensureStep) Eventually(step.RunCallCount).Should(Equal(1)) Eventually(process.Wait()).Should(Receive(errorMatching(ContainSubstring("disaster")))) Expect(hook.RunCallCount()).To(Equal(1)) }) It("propagates signals to the first step when first step is running", func() { step.RunStub = func(signals <-chan os.Signal, ready chan<- struct{}) error { close(ready) <-signals return errors.New("interrupted") } process := ifrit.Background(ensureStep) process.Signal(os.Kill) Eventually(step.RunCallCount).Should(Equal(1)) Eventually(process.Wait()).Should(Receive(errorMatching(ContainSubstring("interrupted")))) Expect(hook.RunCallCount()).To(Equal(1)) }) It("propagates signals to the hook when the hook is running", func() { hook.RunStub = func(signals <-chan os.Signal, ready chan<- struct{}) error {
var foo interface{} destination := &foo Expect(step.Result(destination)).To(BeTrue()) Expect(attempt2Step.ResultCallCount()).To(Equal(2)) Expect(attempt2Step.ResultArgsForCall(1)).To(Equal(destination)) }) }) }) }) Context("when attempt 1 errors, and attempt 2 is interrupted", func() { BeforeEach(func() { attempt1Step.RunStub = func(signals <-chan os.Signal, ready chan<- struct{}) error { close(ready) return errors.New("nope") } attempt2Step.RunStub = func(signals <-chan os.Signal, ready chan<- struct{}) error { close(ready) <-signals return ErrInterrupted } }) Describe("Run", func() { var process ifrit.Process JustBeforeEach(func() { process = ifrit.Invoke(step) process.Signal(os.Interrupt)
JustBeforeEach(func() { timeout = Timeout(fakeStepFactoryStep, timeoutDuration) step = timeout.Using(nil, nil) process = ifrit.Background(step) }) Context("when the process goes beyond the duration", func() { BeforeEach(func() { runStep.ResultStub = successResult(true) timeoutDuration = atc.Duration(1 * time.Second) runStep.RunStub = func(signals <-chan os.Signal, ready chan<- struct{}) error { close(ready) select { case <-startStep: return nil case <-signals: return ErrInterrupted } } }) It("should interrupt after timeout duration", func() { Eventually(runStep.RunCallCount).Should(Equal(1)) var receivedError error Eventually(process.Wait(), 3*time.Second).Should(Receive(&receivedError)) Ω(receivedError).Should(Equal(ErrStepTimedOut)) }) Context("when the process is signaled", func() {
_, _, location := fakeDelegate.InputDelegateArgsForCall(1) Ω(location).ShouldNot(BeNil()) }) }) Context("when the steps complete", func() { BeforeEach(func() { assertNotReleased := func(signals <-chan os.Signal, ready chan<- struct{}) error { defer GinkgoRecover() Consistently(inputStep.ReleaseCallCount).Should(BeZero()) Consistently(taskStep.ReleaseCallCount).Should(BeZero()) Consistently(outputStep.ReleaseCallCount).Should(BeZero()) return nil } inputStep.RunStub = assertNotReleased taskStep.RunStub = assertNotReleased outputStep.RunStub = assertNotReleased }) It("releases all sources", func() { Ω(inputStep.ReleaseCallCount()).Should(Equal(1)) Ω(taskStep.ReleaseCallCount()).Should(Equal(1)) Ω(outputStep.ReleaseCallCount()).Should(BeNumerically(">", 0)) }) }) Context("when the task is privileged", func() { BeforeEach(func() { privileged = true })
Ω(step).Should(Equal(inStep)) Ω(repo).Should(Equal(repo)) }) It("exits successfully", func() { Eventually(process.Wait()).Should(Receive(BeNil())) }) Describe("executing each source", func() { BeforeEach(func() { wg := new(sync.WaitGroup) wg.Add(2) outStepA.RunStub = func(signals <-chan os.Signal, ready chan<- struct{}) error { wg.Done() wg.Wait() close(ready) return nil } outStepB.RunStub = func(signals <-chan os.Signal, ready chan<- struct{}) error { wg.Done() wg.Wait() close(ready) return nil } }) It("happens concurrently", func() { Ω(outStepA.RunCallCount()).Should(Equal(1)) Ω(outStepB.RunCallCount()).Should(Equal(1)) })
Type: worker.ContainerTypeGet, Name: "some-input", })) Ω(delegate).Should(Equal(fakeInputDelegate)) _, _, location := fakeDelegate.InputDelegateArgsForCall(0) Ω(location).ShouldNot(BeNil()) }) }) Context("when the step times out", func() { BeforeEach(func() { inputStep.RunStub = func(signals <-chan os.Signal, ready chan<- struct{}) error { close(ready) time.Sleep(4 * time.Second) return nil } }) It("does not run the next step", func() { plan := atc.Plan{ OnSuccess: &atc.OnSuccessPlan{ Step: atc.Plan{ Timeout: &atc.TimeoutPlan{ Duration: "2s", Step: atc.Plan{ Location: &atc.Location{}, Get: &atc.GetPlan{ Name: "some-input",
finishStep = make(chan error, 1) startNextStep = make(chan error, 1) finishNextStep = make(chan error, 1) outStep.ResultStub = successResult(true) outStep.RunStub = func(signals <-chan os.Signal, ready chan<- struct{}) error { select { case err := <-startStep: if err != nil { return err } case <-signals: return ErrInterrupted } close(ready) select { case <-signals: return ErrInterrupted case err := <-finishStep: return err } } nextStep.RunStub = func(signals <-chan os.Signal, ready chan<- struct{}) error { select { case err := <-startNextStep: if err != nil { return err
Expect(delegate).To(Equal(fakeInputDelegate)) _, _, location := fakeDelegate.InputDelegateArgsForCall(0) Expect(location).NotTo(BeNil()) Expect(location.ID).To(Equal(uint(145))) Expect(location.ParentID).To(Equal(uint(1))) Expect(location.ParallelGroup).To(Equal(uint(1234))) Expect(location.SerialGroup).To(Equal(uint(5678))) Expect(location.Hook).To(Equal("boring input hook")) }) It("releases inputs correctly", func() { inputStep.RunStub = func(signals <-chan os.Signal, ready chan<- struct{}) error { defer GinkgoRecover() Consistently(inputStep.ReleaseCallCount).Should(BeZero()) return nil } var err error build, err = execEngine.CreateBuild(logger, buildModel, plan) Expect(err).NotTo(HaveOccurred()) build.Resume(logger) Expect(inputStep.ReleaseCallCount()).To(Equal(1)) }) }) Context("that contains tasks", func() { privileged = false taskConfig = &atc.TaskConfig{
startA = make(chan error, 1) finishA = make(chan error, 1) startB = make(chan error, 1) finishB = make(chan error, 1) outStepA.RunStub = func(signals <-chan os.Signal, ready chan<- struct{}) error { select { case err := <-startA: if err != nil { return err } case <-signals: return ErrInterrupted } close(ready) select { case <-signals: return ErrInterrupted case err := <-finishA: return err } } outStepB.RunStub = func(signals <-chan os.Signal, ready chan<- struct{}) error { select { case err := <-startB: if err != nil { return err