package shutdown import ( "context" "net/http" "os" "os/signal" "time" syscall "golang.org/x/sys/unix" "edge-infra.dev/pkg/lib/logging" ) type Graceful struct { term chan os.Signal fail chan error logger *logging.EdgeLogger } func NewGraceful() *Graceful { return &Graceful{ term: make(chan os.Signal), fail: make(chan error), logger: logging.NewLogger(), } } // Serve graceful func (g *Graceful) Serve(doWork func() error, teardown func(context.Context) error) error { go func() { signal.Notify(g.term, syscall.SIGINT, syscall.SIGTERM) <-g.term // waits for termination signal // context with 30s timeout ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() // all teardown process must complete within 30 seconds g.fail <- teardown(ctx) }() // doWork blocks our code from exit, but will produce ErrServerClosed when stopped if err := doWork(); err != nil && err != http.ErrServerClosed { g.logger.Error(err, "doWork had an error") return err } // after server gracefully stopped, code proceeds here and waits for any error produced by teardown() process return <-g.fail }