diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index 12b3f4d9ccc..e819436c5a1 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -30,6 +30,7 @@ import ( fsrepo "github.com/ipfs/kubo/repo/fsrepo" "github.com/ipfs/kubo/repo/fsrepo/migrations" "github.com/ipfs/kubo/repo/fsrepo/migrations/ipfsfetcher" + dht "github.com/libp2p/go-libp2p-kad-dht" p2pcrypto "github.com/libp2p/go-libp2p/core/crypto" pnet "github.com/libp2p/go-libp2p/core/pnet" sockets "github.com/libp2p/go-socket-activation" @@ -408,14 +409,36 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment } } + // Check limits to see if we have enough to run DHT as a server. + var sysConnInbound int + var sysStreamInbound int + var dhtStreamsInbound int + if cfg.Swarm.ResourceMgr.Limits != nil { + sysConnInbound = cfg.Swarm.ResourceMgr.Limits.System.ConnsInbound + sysStreamInbound = cfg.Swarm.ResourceMgr.Limits.System.StreamsInbound + dhtLimits, ok := cfg.Swarm.ResourceMgr.Limits.Protocol[dht.ProtocolDHT] + if ok { + dhtStreamsInbound = dhtLimits.StreamsInbound + } + } + switch routingOption { case routingOptionSupernodeKwd: return errors.New("supernode routing was never fully implemented and has been removed") case routingOptionDefaultKwd, routingOptionAutoKwd: + mode := dht.ModeAuto + if (sysConnInbound != 0 && sysConnInbound <= config.DefaultResourceMgrMinInboundConns) || + (sysStreamInbound != 0 && sysStreamInbound <= config.DefaultResourceMgrMinInboundConns) || + (dhtStreamsInbound != 0 && dhtStreamsInbound <= config.DefaultResourceMgrMinInboundConns) { + mode = dht.ModeClient + fmt.Println("You don't have enough resources to run as a DHT server. Running as a DHT client instead.") + } + ncfg.Routing = libp2p.ConstructDefaultRouting( cfg.Identity.PeerID, cfg.Addresses.Swarm, cfg.Identity.PrivKey, + mode, ) case routingOptionDHTClientKwd: ncfg.Routing = libp2p.DHTClientOption diff --git a/core/node/libp2p/routingopt.go b/core/node/libp2p/routingopt.go index bfb45971cc9..ebb257e5070 100644 --- a/core/node/libp2p/routingopt.go +++ b/core/node/libp2p/routingopt.go @@ -40,7 +40,7 @@ func init() { } // ConstructDefaultRouting returns routers used when Routing.Type is unset or set to "auto" -func ConstructDefaultRouting(peerID string, addrs []string, privKey string) func( +func ConstructDefaultRouting(peerID string, addrs []string, privKey string, mode dht.ModeOpt) func( ctx context.Context, host host.Host, dstore datastore.Batching, @@ -59,7 +59,7 @@ func ConstructDefaultRouting(peerID string, addrs []string, privKey string) func var routers []*routinghelpers.ParallelRouter // Run the default DHT routing (same as Routing.Type = "dht") - dhtRouting, err := DHTOption(ctx, host, dstore, validator, bootstrapPeers...) + dhtRouting, err := constructDHTRouting(mode)(ctx, host, dstore, validator) if err != nil { return nil, err } diff --git a/test/cli/delegated_routing_http_test.go b/test/cli/delegated_routing_http_test.go index 0b39a9b12e6..9739baae6dd 100644 --- a/test/cli/delegated_routing_http_test.go +++ b/test/cli/delegated_routing_http_test.go @@ -3,14 +3,67 @@ package cli import ( "net/http" "net/http/httptest" + "os/exec" + "strings" "testing" + "time" "github.com/ipfs/kubo/config" "github.com/ipfs/kubo/test/cli/harness" . "github.com/ipfs/kubo/test/cli/testutils" + dht "github.com/libp2p/go-libp2p-kad-dht" + "github.com/libp2p/go-libp2p/core/protocol" + rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) +func TestDelegatedRoutingLowResources(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + + node.UpdateConfig(func(cfg *config.Config) { + cfg.Swarm.ResourceMgr.Enabled = config.True + cfg.Swarm.ResourceMgr.Limits = &rcmgr.LimitConfig{ + System: rcmgr.BaseLimit{ + ConnsInbound: 10, + StreamsInbound: 10, + }, + Protocol: map[protocol.ID]rcmgr.BaseLimit{ + dht.ProtocolDHT: { + StreamsInbound: 10, + }, + }, + } + }) + + var cfgVal int + node.GetIPFSConfig("Swarm.ResourceMgr.Limits.System.ConnsInbound", &cfgVal) + require.Equal(t, 10, cfgVal) + + res := node.Runner.MustRun(harness.RunRequest{ + Path: node.IPFSBin, + RunFunc: (*exec.Cmd).Start, + Args: []string{"daemon"}, + }) + + var checks int + for { + if checks == 20 { + require.Fail(t, "expected string not found") + } + + for _, s := range res.Stdout.Lines() { + if strings.EqualFold(s, "You don't have enough resources to run as a DHT server. Running as a DHT client instead.") { + return + } + } + + checks++ + time.Sleep(1 * time.Second) + } +} + func TestHTTPDelegatedRouting(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() @@ -117,5 +170,4 @@ func TestHTTPDelegatedRouting(t *testing.T) { res = node.IPFS("routing", "findprovs", findProvsCID) assert.Equal(t, prov, res.Stdout.Trimmed()) }) - }