From fd61b1d3f211086ed70af3320fc4473b37cdcbfb Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Fri, 12 Jan 2024 16:21:02 +0100 Subject: [PATCH 1/3] feat: add shutdown endpoint to internal keyper API --- rolling-shutter/keyper/kprapi/http.go | 7 +++ rolling-shutter/keyper/kprapi/kprapi.go | 34 +++++++++++++-- rolling-shutter/keyper/kproapi/oapi.gen.go | 50 ++++++++++++++++------ rolling-shutter/keyper/kproapi/oapi.yaml | 7 +++ 4 files changed, 81 insertions(+), 17 deletions(-) diff --git a/rolling-shutter/keyper/kprapi/http.go b/rolling-shutter/keyper/kprapi/http.go index 04c9b7ec..7d2507f7 100644 --- a/rolling-shutter/keyper/kprapi/http.go +++ b/rolling-shutter/keyper/kprapi/http.go @@ -31,6 +31,13 @@ func (srv *server) Ping(w http.ResponseWriter, _ *http.Request) { _, _ = w.Write([]byte("pong")) } +func (srv *server) Shutdown(_ http.ResponseWriter, _ *http.Request) { + srv.shutdownSig <- struct{}{} + // We still want to return here and thus return 200 to the caller after this. + // Not immediately closing open connctions is taken care + // of by the graceful shutdown of the http server. +} + func (srv *server) GetDecryptionKey(w http.ResponseWriter, r *http.Request, eon int, epochID kproapi.EpochID) { ctx := r.Context() db := database.New(srv.dbpool) diff --git a/rolling-shutter/keyper/kprapi/kprapi.go b/rolling-shutter/keyper/kprapi/kprapi.go index b94625ce..b57d1295 100644 --- a/rolling-shutter/keyper/kprapi/kprapi.go +++ b/rolling-shutter/keyper/kprapi/kprapi.go @@ -3,6 +3,7 @@ package kprapi import ( "context" "encoding/json" + "errors" "net/http" "os" "time" @@ -33,11 +34,14 @@ type Config interface { } type server struct { - dbpool *pgxpool.Pool - config Config - p2p P2PMessageSender + dbpool *pgxpool.Pool + config Config + p2p P2PMessageSender + shutdownSig chan struct{} } +var ErrShutdownRequested = errors.New("shutdown requested from API") + func NewHTTPService(dbpool *pgxpool.Pool, config Config, p2p P2PMessageSender) service.Service { return &server{ dbpool: dbpool, @@ -90,7 +94,13 @@ func (srv *server) Start(ctx context.Context, runner service.Runner) error { Handler: srv.setupRouter(), ReadHeaderTimeout: 5 * time.Second, } + srv.shutdownSig = make(chan struct{}) + runner.Defer(func() { close(srv.shutdownSig) }) + runner.Go(httpServer.ListenAndServe) + runner.Go(func() error { + return srv.waitShutdown(ctx) + }) runner.Go(func() error { <-ctx.Done() shutdownCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second) @@ -100,6 +110,24 @@ func (srv *server) Start(ctx context.Context, runner service.Runner) error { return nil } +func (srv *server) waitShutdown(ctx context.Context) error { + for { + select { + case _, ok := <-srv.shutdownSig: + if !ok { + // channel close without a send + // means we want to stop the shutdown waiter + // but not stop execution + return nil + } + return ErrShutdownRequested + case <-ctx.Done(): + // we canceled somewhere else + return nil + } + } +} + func (srv *server) setupAPIRouter(swagger *openapi3.T) http.Handler { router := chi.NewRouter() diff --git a/rolling-shutter/keyper/kproapi/oapi.gen.go b/rolling-shutter/keyper/kproapi/oapi.gen.go index 945bc32a..6253542a 100644 --- a/rolling-shutter/keyper/kproapi/oapi.gen.go +++ b/rolling-shutter/keyper/kproapi/oapi.gen.go @@ -68,6 +68,9 @@ type ServerInterface interface { // (GET /ping) Ping(w http.ResponseWriter, r *http.Request) + + // (POST /shutdown) + Shutdown(w http.ResponseWriter, r *http.Request) } // ServerInterfaceWrapper converts contexts to parameters. @@ -159,6 +162,21 @@ func (siw *ServerInterfaceWrapper) Ping(w http.ResponseWriter, r *http.Request) handler(w, r.WithContext(ctx)) } +// Shutdown operation middleware +func (siw *ServerInterfaceWrapper) Shutdown(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var handler = func(w http.ResponseWriter, r *http.Request) { + siw.Handler.Shutdown(w, r) + } + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler(w, r.WithContext(ctx)) +} + type UnescapedCookieParamError struct { ParamName string Err error @@ -284,6 +302,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Get(options.BaseURL+"/ping", wrapper.Ping) }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/shutdown", wrapper.Shutdown) + }) return r } @@ -291,20 +312,21 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "y/GjCwAA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/rolling-shutter/keyper/kproapi/oapi.yaml b/rolling-shutter/keyper/kproapi/oapi.yaml index 902f5f66..e494a6bb 100644 --- a/rolling-shutter/keyper/kproapi/oapi.yaml +++ b/rolling-shutter/keyper/kproapi/oapi.yaml @@ -16,6 +16,13 @@ paths: operationId: ping parameters: + /shutdown: + post: + description: | + Shut down the served + operationId: shutdown + parameters: + /eons: get: description: | From bcf1ea28450f27425ecd1987b30871538224df75 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Fri, 12 Jan 2024 17:16:44 +0100 Subject: [PATCH 2/3] chore: use sig 0 for user shutdown --- rolling-shutter/keyper/kprapi/kprapi.go | 14 ++++++-------- rolling-shutter/medley/medley.go | 2 ++ rolling-shutter/medley/service/service.go | 8 ++++++++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/rolling-shutter/keyper/kprapi/kprapi.go b/rolling-shutter/keyper/kprapi/kprapi.go index b57d1295..80bed172 100644 --- a/rolling-shutter/keyper/kprapi/kprapi.go +++ b/rolling-shutter/keyper/kprapi/kprapi.go @@ -3,7 +3,6 @@ package kprapi import ( "context" "encoding/json" - "errors" "net/http" "os" "time" @@ -18,6 +17,7 @@ import ( "github.com/rs/zerolog/log" "github.com/shutter-network/rolling-shutter/rolling-shutter/keyper/kproapi" + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/retry" "github.com/shutter-network/rolling-shutter/rolling-shutter/medley/service" "github.com/shutter-network/rolling-shutter/rolling-shutter/p2pmsg" @@ -40,13 +40,12 @@ type server struct { shutdownSig chan struct{} } -var ErrShutdownRequested = errors.New("shutdown requested from API") - func NewHTTPService(dbpool *pgxpool.Pool, config Config, p2p P2PMessageSender) service.Service { return &server{ - dbpool: dbpool, - config: config, - p2p: p2p, + dbpool: dbpool, + config: config, + p2p: p2p, + shutdownSig: make(chan struct{}), } } @@ -94,7 +93,6 @@ func (srv *server) Start(ctx context.Context, runner service.Runner) error { Handler: srv.setupRouter(), ReadHeaderTimeout: 5 * time.Second, } - srv.shutdownSig = make(chan struct{}) runner.Defer(func() { close(srv.shutdownSig) }) runner.Go(httpServer.ListenAndServe) @@ -120,7 +118,7 @@ func (srv *server) waitShutdown(ctx context.Context) error { // but not stop execution return nil } - return ErrShutdownRequested + return medley.ErrShutdownRequested case <-ctx.Done(): // we canceled somewhere else return nil diff --git a/rolling-shutter/medley/medley.go b/rolling-shutter/medley/medley.go index f0ef15b6..a73fa9b2 100644 --- a/rolling-shutter/medley/medley.go +++ b/rolling-shutter/medley/medley.go @@ -26,6 +26,8 @@ const receiptPollInterval = 500 * time.Millisecond var errAddressNotFound = errors.New("address not found") +var ErrShutdownRequested = errors.New("shutdown requested from user") + // FindAddressIndex returns the index of the given address inside the slice of addresses or returns // an error, if the slice does not contain the given address. func FindAddressIndex(addresses []common.Address, addr common.Address) (int, error) { diff --git a/rolling-shutter/medley/service/service.go b/rolling-shutter/medley/service/service.go index 68841283..bd13adb8 100644 --- a/rolling-shutter/medley/service/service.go +++ b/rolling-shutter/medley/service/service.go @@ -3,6 +3,7 @@ package service import ( "context" + "errors" "os" "os/signal" "sync" @@ -10,6 +11,8 @@ import ( "github.com/rs/zerolog/log" "golang.org/x/sync/errgroup" + + "github.com/shutter-network/rolling-shutter/rolling-shutter/medley" ) type Runner interface { @@ -100,6 +103,11 @@ func RunWithSighandler(ctx context.Context, services ...Service) error { log.Info().Msg("bye") return nil } + if errors.Is(err, medley.ErrShutdownRequested) { + log.Info().Msg("user shut down service") + log.Info().Msg("bye") + return nil + } return err } From b8336a51104eedd890e9f98890a76fdb6518a053 Mon Sep 17 00:00:00 2001 From: Maximilian Langenfeld Date: Wed, 17 Jan 2024 19:01:31 +0100 Subject: [PATCH 3/3] fix: typo Co-authored-by: jannikluhn --- rolling-shutter/keyper/kproapi/oapi.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rolling-shutter/keyper/kproapi/oapi.yaml b/rolling-shutter/keyper/kproapi/oapi.yaml index e494a6bb..cdeb1a56 100644 --- a/rolling-shutter/keyper/kproapi/oapi.yaml +++ b/rolling-shutter/keyper/kproapi/oapi.yaml @@ -19,7 +19,7 @@ paths: /shutdown: post: description: | - Shut down the served + Shut down the server operationId: shutdown parameters: