func (r Restarter) Run(signals <-chan os.Signal, ready chan<- struct{}) error { if r.Load == nil { return ErrNoLoadCallback } process := ifrit.Background(r.Runner) processReady := process.Ready() exit := process.Wait() signaled := false for { select { case signal := <-signals: process.Signal(signal) signaled = true case <-processReady: close(ready) processReady = nil case err := <-exit: if signaled { return err } r.Runner = r.Load(r.Runner, err) if r.Runner == nil { return err } process = ifrit.Background(r.Runner) exit = process.Wait() } } }
func New(proxySignals <-chan os.Signal, runner ifrit.Runner) ifrit.Runner { return ifrit.RunFunc(func(signals <-chan os.Signal, ready chan<- struct{}) error { process := ifrit.Background(runner) <-process.Ready() close(ready) go forwardSignals(proxySignals, process) go forwardSignals(signals, process) return <-process.Wait() }) }
func Invoke(runner ifrit.Runner) ifrit.Process { process := ifrit.Background(runner) select { case <-process.Ready(): case err := <-process.Wait(): ginkgo.Fail(fmt.Sprintf("process failed to start: %s", err)) } return process }
func (g *orderedGroup) orderedStart(signals <-chan os.Signal) (os.Signal, ErrorTrace) { for _, member := range g.members { p := ifrit.Background(member) select { case <-p.Ready(): g.pool[member.Name] = p case err := <-p.Wait(): return nil, ErrorTrace{ ExitEvent{Member: member, Err: err}, } case signal := <-signals: return signal, nil } } return nil, nil }
func (g *parallelGroup) parallelStart(signals <-chan os.Signal) (os.Signal, ErrorTrace) { numMembers := len(g.members) cases := make([]reflect.SelectCase, 2*numMembers+1) for i, member := range g.members { process := ifrit.Background(member) g.pool[member.Name] = process cases[2*i] = reflect.SelectCase{ Dir: reflect.SelectRecv, Chan: reflect.ValueOf(process.Wait()), } cases[2*i+1] = reflect.SelectCase{ Dir: reflect.SelectRecv, Chan: reflect.ValueOf(process.Ready()), } } cases[2*numMembers] = reflect.SelectCase{ Dir: reflect.SelectRecv, Chan: reflect.ValueOf(signals), } numReady := 0 for { chosen, recv, _ := reflect.Select(cases) switch { case chosen == 2*numMembers: return recv.Interface().(os.Signal), nil case chosen%2 == 0: recvError, _ := recv.Interface().(error) return nil, ErrorTrace{ExitEvent{Member: g.members[chosen/2], Err: recvError}} default: cases[chosen].Chan = reflect.Zero(cases[chosen].Chan.Type()) numReady++ if numReady == numMembers { return nil, nil } } } }
var ( lifecycle ifrit.Process fakeDeamonRunner func(signals <-chan os.Signal, ready chan<- struct{}) error ) BeforeEach(func() { builder := main.Builder{ RepoName: "ubuntu", Tag: "latest", OutputFilename: "/tmp/result/result.json", DockerDaemonTimeout: 300 * time.Millisecond, CacheDockerImage: true, } lifecycle = ifrit.Background(grouper.NewParallel(os.Interrupt, grouper.Members{ {"builder", ifrit.RunFunc(builder.Run)}, {"fake_docker_daemon", ifrit.RunFunc(fakeDeamonRunner)}, })) }) AfterEach(func() { ginkgomon.Interrupt(lifecycle) }) Context("when the daemon won't start", func() { fakeDeamonRunner = func(signals <-chan os.Signal, ready chan<- struct{}) error { close(ready) select { case signal := <-signals: return errors.New(signal.String()) case <-time.After(1 * time.Second): // Daemon "crashes" after a while
var testRunner *fake_runner.TestRunner var restarter restart.Restarter var process ifrit.Process BeforeEach(func() { testRunner = fake_runner.NewTestRunner() restarter = restart.Restarter{ Runner: testRunner, Load: func(runner ifrit.Runner, err error) ifrit.Runner { return nil }, } }) JustBeforeEach(func() { process = ifrit.Background(restarter) }) AfterEach(func() { process.Signal(os.Kill) testRunner.EnsureExit() Eventually(process.Wait()).Should(Receive()) }) Describe("Process Behavior", func() { It("waits for the internal runner to be ready", func() { Consistently(process.Ready()).ShouldNot(BeClosed()) testRunner.TriggerReady() Eventually(process.Ready()).Should(BeClosed()) })
} groupRunner = grouper.NewParallel(os.Interrupt, members) }) AfterEach(func() { childRunner1.EnsureExit() childRunner2.EnsureExit() childRunner3.EnsureExit() ginkgomon.Kill(groupProcess) }) Describe("Start", func() { BeforeEach(func() { groupProcess = ifrit.Background(groupRunner) }) It("runs all runners at the same time", func() { Eventually(childRunner1.RunCallCount).Should(Equal(1)) Eventually(childRunner2.RunCallCount).Should(Equal(1)) Eventually(childRunner3.RunCallCount).Should(Equal(1)) Consistently(groupProcess.Ready()).ShouldNot(BeClosed()) childRunner1.TriggerReady() childRunner2.TriggerReady() childRunner3.TriggerReady() Eventually(groupProcess.Ready()).Should(BeClosed()) })
"github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/tedsuo/ifrit/proxy" . "github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/ginkgo" . "github.com/cloudfoundry-incubator/docker_app_lifecycle/Godeps/_workspace/src/github.com/onsi/gomega" ) var _ = Describe("Proxy", func() { var testRunner *fake_runner.TestRunner var process ifrit.Process var proxySignals chan os.Signal var receivedSignals <-chan os.Signal BeforeEach(func() { proxySignals = make(chan os.Signal, 1) testRunner = fake_runner.NewTestRunner() process = ifrit.Background(proxy.New(proxySignals, testRunner)) receivedSignals = testRunner.WaitForCall() testRunner.TriggerReady() }) It("sends the proxied signals to the embedded runner", func() { proxySignals <- os.Interrupt Eventually(receivedSignals).Should(Receive(Equal(os.Interrupt))) }) It("sends the process signals to the embedded runner", func() { process.Signal(os.Interrupt) Eventually(receivedSignals).Should(Receive(Equal(os.Interrupt))) }) })