Beispiel #1
0
// Terminate kills the application
func (a *App) Terminate() {
	a.signalLock.Lock()
	defer a.signalLock.Unlock()
	a.stopPolling()
	a.forAllServices(deregisterService)

	// Run and wait for preStop command to exit (continues
	// unconditionally so we don't worry about returned errors here)
	commands.RunAndWait(a.PreStopCmd, log.Fields{"process": "PreStop"})
	if a.Command == nil || a.Command.Cmd == nil ||
		a.Command.Cmd.Process == nil {
		// Not managing the process, so don't do anything
		return
	}
	cmd := a.Command.Cmd // get the underlying process
	if a.StopTimeout > 0 {
		if err := cmd.Process.Signal(syscall.SIGTERM); err != nil {
			log.Warnf("Error sending SIGTERM to application: %s", err)
		} else {
			time.AfterFunc(time.Duration(a.StopTimeout)*time.Second, func() {
				log.Infof("Killing Process %#v", cmd.Process)
				cmd.Process.Kill()
			})
			return
		}
	}
	log.Infof("Killing Process %#v", a.Command.Cmd.Process)
	cmd.Process.Kill()
}
Beispiel #2
0
// 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())
	cmd, err := commands.NewCommand(args, "0")
	if err != nil {
		log.Errorf("Unable to parse command arguments: %v", err)
	}
	cmd.Name = "APP"
	a.Command = cmd

	a.handleSignals()

	if a.PreStartCmd != nil {
		// Run the preStart handler, if any, and exit if it returns an error
		fields := log.Fields{"process": "PreStart"}
		if code, err := commands.RunAndWait(a.PreStartCmd, fields); err != nil {
			os.Exit(code)
		}
	}
	a.handleCoprocesses()
	a.handlePolling()

	if a.Command != nil {
		// 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.
		code, err := commands.RunAndWait(a.Command, nil)
		if err != nil {
			log.Println(err)
		}
		// Run the PostStop handler, if any, and exit if it returns an error
		if a.PostStopCmd != nil {
			fields := log.Fields{"process": "PostStop"}
			if postStopCode, err := commands.RunAndWait(a.PostStopCmd, fields); 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 {}
}
Beispiel #3
0
// 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, _ := commands.RunAndWait(app.Command, nil); 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())
	}
}
Beispiel #4
0
// Start runs the coprocess
func (c *Coprocess) Start() {
	log.Debugf("coprocess[%s].Start", c.Name)
	fields := log.Fields{"process": "coprocess", "coprocess": c.Name}

	// always reset restartsRemain when we load the config
	c.restartsRemain = c.restartLimit
	for {
		if c.restartLimit != unlimitedRestarts &&
			c.restartsRemain <= haltRestarts {
			break
		}
		if code, err := commands.RunAndWait(c.cmd, fields); err != nil {
			log.Errorf("coprocess[%s] exited (%s): %s", c.Name, code, err)
		}
		log.Debugf("coprocess[%s] exited", c.Name)
		if !c.restart {
			break
		}
		c.restartsRemain--
	}
}