mirror of
https://github.com/ipfs/kubo.git
synced 2025-06-26 23:53:19 +08:00
Merge pull request #5322 from ipfs/fix/shutdown-error
wait for all connections to close before exiting on shutdown.
This commit is contained in:
@ -138,7 +138,7 @@ var rootSubcommands = map[string]*cmds.Command{
|
|||||||
"update": lgc.NewCommand(ExternalBinary()),
|
"update": lgc.NewCommand(ExternalBinary()),
|
||||||
"urlstore": urlStoreCmd,
|
"urlstore": urlStoreCmd,
|
||||||
"version": lgc.NewCommand(VersionCmd),
|
"version": lgc.NewCommand(VersionCmd),
|
||||||
"shutdown": lgc.NewCommand(daemonShutdownCmd),
|
"shutdown": daemonShutdownCmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
// RootRO is the readonly version of Root
|
// RootRO is the readonly version of Root
|
||||||
|
@ -3,31 +3,28 @@ package commands
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
cmds "gx/ipfs/QmNueRyPRQiV7PUEpnP4GgGLuK1rKQLaRW7sfPvUetYig1/go-ipfs-cmds"
|
||||||
"gx/ipfs/QmdE4gMduCKCGAcczM2F5ioYDfdeKuPix138wrES1YSr7f/go-ipfs-cmdkit"
|
"gx/ipfs/QmdE4gMduCKCGAcczM2F5ioYDfdeKuPix138wrES1YSr7f/go-ipfs-cmdkit"
|
||||||
|
|
||||||
cmds "github.com/ipfs/go-ipfs/commands"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var daemonShutdownCmd = &cmds.Command{
|
var daemonShutdownCmd = &cmds.Command{
|
||||||
Helptext: cmdkit.HelpText{
|
Helptext: cmdkit.HelpText{
|
||||||
Tagline: "Shut down the ipfs daemon",
|
Tagline: "Shut down the ipfs daemon",
|
||||||
},
|
},
|
||||||
Run: func(req cmds.Request, res cmds.Response) {
|
Run: func(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment) {
|
||||||
nd, err := req.InvocContext().GetNode()
|
nd, err := GetNode(env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.SetError(err, cmdkit.ErrNormal)
|
re.SetError(err, cmdkit.ErrNormal)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if nd.LocalMode() {
|
if nd.LocalMode() {
|
||||||
res.SetError(fmt.Errorf("daemon not running"), cmdkit.ErrClient)
|
re.SetError(fmt.Errorf("daemon not running"), cmdkit.ErrClient)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := nd.Process().Close(); err != nil {
|
if err := nd.Process().Close(); err != nil {
|
||||||
log.Error("error while shutting down ipfs daemon:", err)
|
log.Error("error while shutting down ipfs daemon:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res.SetOutput(nil)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ high-level HTTP interfaces to IPFS.
|
|||||||
package corehttp
|
package corehttp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -12,6 +13,7 @@ import (
|
|||||||
|
|
||||||
core "github.com/ipfs/go-ipfs/core"
|
core "github.com/ipfs/go-ipfs/core"
|
||||||
"gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess"
|
"gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess"
|
||||||
|
periodicproc "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/periodic"
|
||||||
manet "gx/ipfs/QmV6FjemM1K8oXjrvuq3wuVWWoU2TLDPmNnKrxHzY3v6Ai/go-multiaddr-net"
|
manet "gx/ipfs/QmV6FjemM1K8oXjrvuq3wuVWWoU2TLDPmNnKrxHzY3v6Ai/go-multiaddr-net"
|
||||||
ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr"
|
ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr"
|
||||||
logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log"
|
logging "gx/ipfs/QmcVVHfdyv15GVPk7NrxdWjh2hLVccXnoD8j2tyQShiXJb/go-log"
|
||||||
@ -19,6 +21,10 @@ import (
|
|||||||
|
|
||||||
var log = logging.Logger("core/server")
|
var log = logging.Logger("core/server")
|
||||||
|
|
||||||
|
// shutdownTimeout is the timeout after which we'll stop waiting for hung
|
||||||
|
// commands to return on shutdown.
|
||||||
|
const shutdownTimeout = 30 * time.Second
|
||||||
|
|
||||||
// ServeOption registers any HTTP handlers it provides on the given mux.
|
// ServeOption registers any HTTP handlers it provides on the given mux.
|
||||||
// It returns the mux to expose to future options, which may be a new mux if it
|
// It returns the mux to expose to future options, which may be a new mux if it
|
||||||
// is interested in mediating requests to future options, or the same mux
|
// is interested in mediating requests to future options, or the same mux
|
||||||
@ -65,6 +71,9 @@ func ListenAndServe(n *core.IpfsNode, listeningMultiAddr string, options ...Serv
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Serve(node *core.IpfsNode, lis net.Listener, options ...ServeOption) error {
|
func Serve(node *core.IpfsNode, lis net.Listener, options ...ServeOption) error {
|
||||||
|
// make sure we close this no matter what.
|
||||||
|
defer lis.Close()
|
||||||
|
|
||||||
handler, err := makeHandler(node, lis, options...)
|
handler, err := makeHandler(node, lis, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -75,43 +84,44 @@ func Serve(node *core.IpfsNode, lis net.Listener, options ...ServeOption) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the server exits beforehand
|
|
||||||
var serverError error
|
|
||||||
serverExited := make(chan struct{})
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-node.Process().Closing():
|
case <-node.Process().Closing():
|
||||||
return fmt.Errorf("failed to start server, process closing")
|
return fmt.Errorf("failed to start server, process closing")
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
node.Process().Go(func(p goprocess.Process) {
|
server := &http.Server{
|
||||||
serverError = http.Serve(lis, handler)
|
Handler: handler,
|
||||||
close(serverExited)
|
}
|
||||||
|
|
||||||
|
var serverError error
|
||||||
|
serverProc := node.Process().Go(func(p goprocess.Process) {
|
||||||
|
serverError = server.Serve(lis)
|
||||||
})
|
})
|
||||||
|
|
||||||
// wait for server to exit.
|
// wait for server to exit.
|
||||||
select {
|
select {
|
||||||
case <-serverExited:
|
case <-serverProc.Closed():
|
||||||
|
|
||||||
// if node being closed before server exits, close server
|
// if node being closed before server exits, close server
|
||||||
case <-node.Process().Closing():
|
case <-node.Process().Closing():
|
||||||
log.Infof("server at %s terminating...", addr)
|
log.Infof("server at %s terminating...", addr)
|
||||||
|
|
||||||
lis.Close()
|
warnProc := periodicproc.Tick(5*time.Second, func(_ goprocess.Process) {
|
||||||
|
|
||||||
outer:
|
|
||||||
for {
|
|
||||||
// wait until server exits
|
|
||||||
select {
|
|
||||||
case <-serverExited:
|
|
||||||
// if the server exited as we are closing, we really dont care about errors
|
|
||||||
serverError = nil
|
|
||||||
break outer
|
|
||||||
case <-time.After(5 * time.Second):
|
|
||||||
log.Infof("waiting for server at %s to terminate...", addr)
|
log.Infof("waiting for server at %s to terminate...", addr)
|
||||||
}
|
})
|
||||||
}
|
|
||||||
|
// This timeout shouldn't be necessary if all of our commands
|
||||||
|
// are obeying their contexts but we should have *some* timeout.
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
|
||||||
|
defer cancel()
|
||||||
|
err := server.Shutdown(ctx)
|
||||||
|
|
||||||
|
// Should have already closed but we still need to wait for it
|
||||||
|
// to set the error.
|
||||||
|
<-serverProc.Closed()
|
||||||
|
serverError = err
|
||||||
|
|
||||||
|
warnProc.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("server at %s terminated", addr)
|
log.Infof("server at %s terminated", addr)
|
||||||
|
@ -13,7 +13,7 @@ test_init_ipfs
|
|||||||
test_launch_ipfs_daemon
|
test_launch_ipfs_daemon
|
||||||
|
|
||||||
test_expect_success "shutdown succeeds" '
|
test_expect_success "shutdown succeeds" '
|
||||||
ipfs shutdown || true # bug: https://github.com/ipfs/go-ipfs/issues/4055
|
ipfs shutdown
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success "daemon no longer running" '
|
test_expect_success "daemon no longer running" '
|
||||||
@ -27,7 +27,7 @@ test_expect_success "daemon no longer running" '
|
|||||||
test_launch_ipfs_daemon --offline
|
test_launch_ipfs_daemon --offline
|
||||||
|
|
||||||
test_expect_success "shutdown succeeds" '
|
test_expect_success "shutdown succeeds" '
|
||||||
ipfs shutdown || true # bug: https://github.com/ipfs/go-ipfs/issues/4055
|
ipfs shutdown
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success "daemon no longer running" '
|
test_expect_success "daemon no longer running" '
|
||||||
|
Reference in New Issue
Block a user