// Start runs the coprocess func (coprocess *Coprocess) Start() { log.Debugf("coprocess[%s].Start", coprocess.Name) fields := log.Fields{"process": "coprocess", "coprocess": coprocess.Name} stdout := utils.NewLogWriter(fields, log.InfoLevel) stderr := utils.NewLogWriter(fields, log.DebugLevel) coprocess.logWriters = []io.WriteCloser{stdout, stderr} defer coprocess.closeLogs() // always reset restartsRemain when we load the config coprocess.restartsRemain = coprocess.restartLimit for { if coprocess.restartLimit != unlimitedRestarts && coprocess.restartsRemain <= haltRestarts { break } cmd := utils.ArgsToCmd(coprocess.Args) coprocess.cmd = cmd cmd.Stdout = stdout cmd.Stderr = stderr if _, err := utils.ExecuteAndWait(cmd); err != nil { log.Errorf("coprocess[%s] exited: %s", coprocess.Name, err) } log.Debugf("coprocess[%s] exited", coprocess.Name) if !coprocess.restart { break } coprocess.restartsRemain-- } }
// Test handler for SIGTERM. Note that the SIGCHLD handler is fired // by this same test, but that we don't have a separate unit test // because they'll interfere with each other's state. func TestTerminateSignal(t *testing.T) { app := getSignalTestConfig() startTime := time.Now() go func() { if exitCode, _ := utils.ExecuteAndWait(app.Command); exitCode != 2 { t.Fatalf("Expected exit code 2 but got %d", exitCode) } }() // we need time for the forked process to start up and this is async runtime.Gosched() time.Sleep(10 * time.Millisecond) app.Terminate() elapsed := time.Since(startTime) if elapsed.Seconds() > float64(app.StopTimeout) { t.Fatalf("Expected elapsed time <= %d seconds, but was %.2f", app.StopTimeout, elapsed.Seconds()) } }
// Run starts the application and blocks until finished func (a *App) Run() { // Set up handlers for polling and to accept signal interrupts if 1 == os.Getpid() { reapChildren() } args := getArgs(flag.Args()) command, err := utils.ParseCommandArgs(args) if err != nil { log.Errorf("Unable to parse command arguments: %v", err) } a.handleSignals() // Run the preStart handler, if any, and exit if it returns an error if preStartCode, err := utils.RunWithFields(a.PreStartCmd, log.Fields{"process": "PreStart"}); err != nil { os.Exit(preStartCode) } a.handleCoprocesses() a.handlePolling() if len(args) != 0 { // Run our main application and capture its stdout/stderr. // This will block until the main application exits and then os.Exit // with the exit code of that application. a.Command = command command.Stderr = os.Stderr command.Stdout = os.Stdout code, err := utils.ExecuteAndWait(command) if err != nil { log.Println(err) } // Run the PostStop handler, if any, and exit if it returns an error if postStopCode, err := utils.RunWithFields(a.PostStopCmd, log.Fields{"process": "PostStop"}); err != nil { os.Exit(postStopCode) } os.Exit(code) } // block forever, as we're polling in the two polling functions and // did not os.Exit by waiting on an external application. select {} }