From 38d714a28a9c22cdc66bba531adff72a882f0ba2 Mon Sep 17 00:00:00 2001 From: laser Date: Tue, 2 Jun 2020 11:54:24 -0700 Subject: [PATCH] first take at "daemon stop" command Fixes #1827 TODO: plumb the daemon-stopping into lotus-storage-miner, too --- api/api_common.go | 3 +++ api/apistruct/struct.go | 6 ++++++ cmd/lotus-storage-miner/stop.go | 0 cmd/lotus/daemon.go | 19 +++++++++++++++++-- cmd/lotus/rpc.go | 14 ++++++++++---- node/builder.go | 1 + node/impl/common/common.go | 12 +++++++++--- node/modules/dtypes/shutdown.go | 3 +++ 8 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 cmd/lotus-storage-miner/stop.go create mode 100644 node/modules/dtypes/shutdown.go diff --git a/api/api_common.go b/api/api_common.go index a49397247..aac2a61a7 100644 --- a/api/api_common.go +++ b/api/api_common.go @@ -34,6 +34,9 @@ type Common interface { LogList(context.Context) ([]string, error) LogSetLevel(context.Context, string, string) error + + // trigger graceful shutdown + Shutdown(context.Context) error } // Version provides various build-time information diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 5903f8ee0..38fd89279 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -47,6 +47,8 @@ type CommonStruct struct { LogList func(context.Context) ([]string, error) `perm:"write"` LogSetLevel func(context.Context, string, string) error `perm:"write"` + + Shutdown func(context.Context) error `perm:"admin"` } } @@ -293,6 +295,10 @@ func (c *CommonStruct) LogSetLevel(ctx context.Context, group, level string) err return c.Internal.LogSetLevel(ctx, group, level) } +func (c *CommonStruct) Shutdown(ctx context.Context) error { + return c.Internal.Shutdown(ctx) +} + // FullNodeStruct func (c *FullNodeStruct) ClientListImports(ctx context.Context) ([]api.Import, error) { diff --git a/cmd/lotus-storage-miner/stop.go b/cmd/lotus-storage-miner/stop.go new file mode 100644 index 000000000..e69de29bb diff --git a/cmd/lotus/daemon.go b/cmd/lotus/daemon.go index 205a0eaaa..f9522d83e 100644 --- a/cmd/lotus/daemon.go +++ b/cmd/lotus/daemon.go @@ -29,6 +29,7 @@ import ( "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/store" "github.com/filecoin-project/lotus/chain/vm" + lcli "github.com/filecoin-project/lotus/cli" "github.com/filecoin-project/lotus/lib/peermgr" "github.com/filecoin-project/lotus/metrics" "github.com/filecoin-project/lotus/node" @@ -49,7 +50,18 @@ var daemonStopCmd = &cli.Command{ Usage: "Stop a running lotus daemon", Flags: []cli.Flag{}, Action: func(cctx *cli.Context) error { - panic("wombat attack") + api, closer, err := lcli.GetAPI(cctx) + if err != nil { + return err + } + defer closer() + + err = api.Shutdown(lcli.ReqContext(cctx)) + if err != nil { + return err + } + + return nil }, } @@ -179,12 +191,15 @@ var DaemonCmd = &cli.Command{ genesis = node.Override(new(modules.Genesis), testing.MakeGenesis(cctx.String(makeGenFlag), cctx.String(preTemplateFlag))) } + shutdownCh := make(chan struct{}) + var api api.FullNode stop, err := node.New(ctx, node.FullAPI(&api), node.Override(new(dtypes.Bootstrapper), isBootstrapper), + node.Override(new(dtypes.ShutdownCh), shutdownCh), node.Online(), node.Repo(r), @@ -230,7 +245,7 @@ var DaemonCmd = &cli.Command{ } // TODO: properly parse api endpoint (or make it a URL) - return serveRPC(api, stop, endpoint) + return serveRPC(api, stop, endpoint, shutdownCh) }, Subcommands: []*cli.Command{ daemonStopCmd, diff --git a/cmd/lotus/rpc.go b/cmd/lotus/rpc.go index 7b1fc7286..a1cf7c80d 100644 --- a/cmd/lotus/rpc.go +++ b/cmd/lotus/rpc.go @@ -28,7 +28,7 @@ import ( var log = logging.Logger("main") -func serveRPC(a api.FullNode, stop node.StopFunc, addr multiaddr.Multiaddr) error { +func serveRPC(a api.FullNode, stop node.StopFunc, addr multiaddr.Multiaddr, shutdownCh <-chan struct{}) error { rpcServer := jsonrpc.NewServer() rpcServer.Register("Filecoin", apistruct.PermissionedFullAPI(a)) @@ -62,17 +62,23 @@ func serveRPC(a api.FullNode, stop node.StopFunc, addr multiaddr.Multiaddr) erro srv := &http.Server{Handler: http.DefaultServeMux} - sigChan := make(chan os.Signal, 2) + sigCh := make(chan os.Signal, 2) go func() { - <-sigChan + select { + case <-sigCh: + case <-shutdownCh: + } + + log.Warn("Shutting down...") if err := srv.Shutdown(context.TODO()); err != nil { log.Errorf("shutting down RPC server failed: %s", err) } if err := stop(context.TODO()); err != nil { log.Errorf("graceful shutting down failed: %s", err) } + log.Warn("Graceful shutdown successful") }() - signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT) + signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT) return srv.Serve(manet.NetListener(lst)) } diff --git a/node/builder.go b/node/builder.go index d054be7a5..f27bb68b9 100644 --- a/node/builder.go +++ b/node/builder.go @@ -150,6 +150,7 @@ func defaults() []Option { Override(new(helpers.MetricsCtx), context.Background), Override(new(record.Validator), modules.RecordValidator), Override(new(dtypes.Bootstrapper), dtypes.Bootstrapper(false)), + Override(new(dtypes.ShutdownCh), make(chan struct{})), // Filecoin modules diff --git a/node/impl/common/common.go b/node/impl/common/common.go index 5042d2ede..518c734c2 100644 --- a/node/impl/common/common.go +++ b/node/impl/common/common.go @@ -25,9 +25,10 @@ import ( type CommonAPI struct { fx.In - APISecret *dtypes.APIAlg - Host host.Host - Router lp2p.BaseIpfsRouting + APISecret *dtypes.APIAlg + Host host.Host + Router lp2p.BaseIpfsRouting + ShutdownCh dtypes.ShutdownCh } type jwtPayload struct { @@ -115,4 +116,9 @@ func (a *CommonAPI) LogSetLevel(ctx context.Context, subsystem, level string) er return logging.SetLogLevel(subsystem, level) } +func (a *CommonAPI) Shutdown(ctx context.Context) error { + a.ShutdownCh <- struct{}{} + return nil +} + var _ api.Common = &CommonAPI{} diff --git a/node/modules/dtypes/shutdown.go b/node/modules/dtypes/shutdown.go new file mode 100644 index 000000000..385b99381 --- /dev/null +++ b/node/modules/dtypes/shutdown.go @@ -0,0 +1,3 @@ +package dtypes + +type ShutdownCh chan struct{}