-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a shutdown signal handler #7999
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,113 @@ | ||||||||
package shutdown | ||||||||
|
||||||||
import ( | ||||||||
"os" | ||||||||
"os/signal" | ||||||||
"sync" | ||||||||
"syscall" | ||||||||
|
||||||||
"github.com/pkg/errors" | ||||||||
"github.com/sirupsen/logrus" | ||||||||
) | ||||||||
|
||||||||
var ( | ||||||||
stopped bool | ||||||||
sigChan chan os.Signal | ||||||||
cancelChan chan bool | ||||||||
handlers map[string]func() error | ||||||||
shutdownInhibit sync.RWMutex | ||||||||
) | ||||||||
|
||||||||
// Start begins handling SIGTERM and SIGINT and will run the given on-signal | ||||||||
// handlers when one is called. This can be cancelled by calling Stop(). | ||||||||
func Start() error { | ||||||||
if sigChan != nil && !stopped { | ||||||||
// Already running, do nothing. | ||||||||
return nil | ||||||||
} | ||||||||
|
||||||||
sigChan = make(chan os.Signal, 1) | ||||||||
cancelChan = make(chan bool, 1) | ||||||||
stopped = false | ||||||||
|
||||||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) | ||||||||
|
||||||||
go func() { | ||||||||
select { | ||||||||
case <-cancelChan: | ||||||||
signal.Stop(sigChan) | ||||||||
close(sigChan) | ||||||||
close(cancelChan) | ||||||||
stopped = true | ||||||||
return | ||||||||
case sig := <-sigChan: | ||||||||
logrus.Infof("Received shutdown signal %v, terminating!", sig) | ||||||||
shutdownInhibit.Lock() | ||||||||
for name, handler := range handlers { | ||||||||
logrus.Infof("Invoking shutdown handler %s", name) | ||||||||
if err := handler(); err != nil { | ||||||||
logrus.Errorf("Error running shutdown handler %s: %v", name, err) | ||||||||
} | ||||||||
} | ||||||||
shutdownInhibit.Unlock() | ||||||||
return | ||||||||
} | ||||||||
}() | ||||||||
|
||||||||
return nil | ||||||||
} | ||||||||
|
||||||||
// Stop the shutdown signal handler. | ||||||||
func Stop() error { | ||||||||
if cancelChan == nil { | ||||||||
return errors.New("shutdown signal handler has not yet been started") | ||||||||
} | ||||||||
if stopped { | ||||||||
return nil | ||||||||
} | ||||||||
|
||||||||
cancelChan <- true | ||||||||
|
||||||||
return nil | ||||||||
} | ||||||||
|
||||||||
// Temporarily inhibit signals from shutting down Libpod. | ||||||||
func Inhibit() { | ||||||||
shutdownInhibit.RLock() | ||||||||
} | ||||||||
|
||||||||
// Stop inhibiting signals from shutting down Libpod. | ||||||||
func Uninhibit() { | ||||||||
shutdownInhibit.RUnlock() | ||||||||
} | ||||||||
|
||||||||
// Register registers a function that will be executed when Podman is terminated | ||||||||
// by a signal. | ||||||||
func Register(name string, handler func() error) error { | ||||||||
if handlers == nil { | ||||||||
handlers = make(map[string]func() error) | ||||||||
} | ||||||||
|
||||||||
if _, ok := handlers[name]; ok { | ||||||||
return errors.Errorf("handler with name %s already exists", name) | ||||||||
} | ||||||||
Comment on lines
+100
to
+102
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Why not just reset handler to new func? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to allow multiple handlers at the same time - theoretically the Libpod handler could run after the server handler and do any extra shutdown needed there. |
||||||||
|
||||||||
handlers[name] = handler | ||||||||
|
||||||||
return nil | ||||||||
} | ||||||||
|
||||||||
// Unregister un-registers a given shutdown handler. | ||||||||
func Unregister(name string) error { | ||||||||
if handlers == nil { | ||||||||
handlers = make(map[string]func() error) | ||||||||
} | ||||||||
|
||||||||
if _, ok := handlers[name]; !ok { | ||||||||
return errors.Errorf("no handler with name %s found", name) | ||||||||
} | ||||||||
jwhonce marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
|
||||||||
delete(handlers, name) | ||||||||
|
||||||||
return nil | ||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,14 +7,14 @@ import ( | |
"net" | ||
"net/http" | ||
"os" | ||
"os/signal" | ||
goRuntime "runtime" | ||
"strings" | ||
"sync" | ||
"syscall" | ||
"time" | ||
|
||
"github.com/containers/podman/v2/libpod" | ||
"github.com/containers/podman/v2/libpod/shutdown" | ||
"github.com/containers/podman/v2/pkg/api/handlers" | ||
"github.com/containers/podman/v2/pkg/api/server/idle" | ||
"github.com/coreos/go-systemd/v22/activation" | ||
|
@@ -180,8 +180,20 @@ func setupSystemd() { | |
// Serve starts responding to HTTP requests. | ||
func (s *APIServer) Serve() error { | ||
setupSystemd() | ||
sigChan := make(chan os.Signal, 1) | ||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) | ||
|
||
// Start the shutdown signal handler. | ||
if err := shutdown.Start(); err != nil { | ||
return err | ||
} | ||
if err := shutdown.Register("server", func() error { | ||
return s.Shutdown() | ||
}); err != nil { | ||
return err | ||
} | ||
// Unregister the libpod handler, which just calls exit(1). | ||
// Ignore errors if it doesn't exist. | ||
_ = shutdown.Unregister("libpod") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems smelly to remove someone elses handler by name... |
||
|
||
errChan := make(chan error, 1) | ||
|
||
go func() { | ||
|
@@ -217,14 +229,7 @@ func (s *APIServer) Serve() error { | |
errChan <- nil | ||
}() | ||
|
||
select { | ||
case err := <-errChan: | ||
return err | ||
case sig := <-sigChan: | ||
logrus.Infof("APIServer terminated by signal %v", sig) | ||
} | ||
|
||
return nil | ||
return <-errChan | ||
} | ||
|
||
// Shutdown is a clean shutdown waiting on existing clients | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would appear to be a race on reading/setting
stopped
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me just remove that, it's not important that we be able to restart the handler right now.