feat(daemon): improve stdout on startup (#10472)
Marcin Rataj committed
Aug 14, 2024 at 16:42 UTC
0d428310b28c663c473d684011780929b145d4f2
3 files changed
+85
-38
cmd/ipfs/kubo/daemon.go
+46
-27
@@ -10,8 +10,10 @@ import (
10
"net/http"
11
_ "net/http/pprof"
12
"os"
13
+ "regexp"
14
"runtime"
15
"sort"
16
+ "strings"
17
"sync"
18
"time"
19
@@ -89,7 +91,7 @@ running, calls to 'ipfs' commands will be sent over the network to
91
the daemon.
92
`,
93
LongDescription: `
92
-The daemon will start listening on ports on the network, which are
94
+The Kubo daemon will start listening on ports on the network, which are
95
documented in (and can be modified through) 'ipfs config Addresses'.
96
For example, to change the 'Gateway' port:
97
@@ -109,11 +111,11 @@ other computers in the network, use 0.0.0.0 as the ip address:
111
Be careful if you expose the RPC API. It is a security risk, as anyone could
112
control your node remotely. If you need to control the node remotely,
113
make sure to protect the port as you would other services or database
112
-(firewall, authenticated proxy, etc).
114
+(firewall, authenticated proxy, etc), or at least set API.Authorizations.
115
116
HTTP Headers
117
116
-ipfs supports passing arbitrary headers to the RPC API and Gateway. You can
118
+Kubo supports passing arbitrary headers to the RPC API and Gateway. You can
119
do this by setting headers on the API.HTTPHeaders and Gateway.HTTPHeaders
120
keys:
121
@@ -124,7 +126,7 @@ Note that the value of the keys is an _array_ of strings. This is because
126
headers can have more than one value, and it is convenient to pass through
127
to other libraries.
128
127
-CORS Headers (for API)
129
+CORS Headers (for RPC API)
130
131
You can setup CORS headers the same way:
132
@@ -141,7 +143,7 @@ second signal.
143
144
IPFS_PATH environment variable
145
144
-ipfs uses a repository in the local file system. By default, the repo is
146
+Kubo uses a repository in the local file system. By default, the repo is
147
located at ~/.ipfs. To change the repo location, set the $IPFS_PATH
148
environment variable:
149
@@ -149,7 +151,7 @@ environment variable:
151
152
DEPRECATION NOTICE
153
152
-Previously, ipfs used an environment variable as seen below:
154
+Previously, Kubo used an environment variable as seen below:
155
156
export API_ORIGIN="http://localhost:8888/"
157
@@ -160,14 +162,14 @@ Headers.
162
},
163
164
Options: []cmds.Option{
163
- cmds.BoolOption(initOptionKwd, "Initialize ipfs with default settings if not already initialized"),
165
+ cmds.BoolOption(initOptionKwd, "Initialize Kubo with default settings if not already initialized"),
166
cmds.StringOption(initConfigOptionKwd, "Path to existing configuration file to be loaded during --init"),
167
cmds.StringOption(initProfileOptionKwd, "Configuration profiles to apply for --init. See ipfs init --help for more"),
168
cmds.StringOption(routingOptionKwd, "Overrides the routing option").WithDefault(routingOptionDefaultKwd),
169
cmds.BoolOption(mountKwd, "Mounts IPFS to the filesystem using FUSE (experimental)"),
170
cmds.StringOption(ipfsMountKwd, "Path to the mountpoint for IPFS (if using --mount). Defaults to config setting."),
171
cmds.StringOption(ipnsMountKwd, "Path to the mountpoint for IPNS (if using --mount). Defaults to config setting."),
170
- cmds.BoolOption(unrestrictedAPIAccessKwd, "Allow API access to unlisted hashes"),
172
+ cmds.BoolOption(unrestrictedAPIAccessKwd, "Allow RPC API access to unlisted hashes"),
173
cmds.BoolOption(unencryptTransportKwd, "Disable transport encryption (for debugging protocols)"),
174
cmds.BoolOption(enableGCKwd, "Enable automatic periodic repo garbage collection"),
175
cmds.BoolOption(adjustFDLimitKwd, "Check and raise file descriptor limits if needed").WithDefault(true),
@@ -373,6 +375,8 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
375
return err
376
}
377
378
+ fmt.Printf("PeerID: %s\n", cfg.Identity.PeerID)
379
+
380
if !psSet {
381
pubsub = cfg.Pubsub.Enabled.WithDefault(false)
382
}
@@ -463,7 +467,7 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
467
log.Fatal("Private network does not work with Routing.Type=auto. Update your config to Routing.Type=dht (or none, and do manual peering)")
468
}
469
466
- printSwarmAddrs(node)
470
+ printLibp2pPorts(node)
471
472
if node.PrivateKey.Type() == p2pcrypto.RSA {
473
fmt.Print(`
@@ -563,7 +567,7 @@ take effect.
567
// Add ipfs version info to prometheus metrics
568
ipfsInfoMetric := promauto.NewGaugeVec(prometheus.GaugeOpts{
569
Name: "ipfs_info",
566
- Help: "IPFS version information.",
570
+ Help: "Kubo IPFS version information.",
571
}, []string{"version", "commit"})
572
573
// Setting to 1 lets us multiply it with other stats to add the version labels
@@ -779,8 +783,8 @@ func rewriteMaddrToUseLocalhostIfItsAny(maddr ma.Multiaddr) ma.Multiaddr {
783
}
784
}
785
782
-// printSwarmAddrs prints the addresses of the host.
783
-func printSwarmAddrs(node *core.IpfsNode) {
786
+// printLibp2pPorts prints which ports are opened to facilitate swarm connectivity.
787
+func printLibp2pPorts(node *core.IpfsNode) {
788
if !node.IsOnline {
789
fmt.Println("Swarm not listening, running in offline mode.")
790
return
@@ -790,24 +794,39 @@ func printSwarmAddrs(node *core.IpfsNode) {
794
if err != nil {
795
log.Errorf("failed to read listening addresses: %s", err)
796
}
793
- lisAddrs := make([]string, len(ifaceAddrs))
794
- for i, addr := range ifaceAddrs {
795
- lisAddrs[i] = addr.String()
796
- }
797
- sort.Strings(lisAddrs)
798
- for _, addr := range lisAddrs {
799
- fmt.Printf("Swarm listening on %s\n", addr)
800
- }
797
802
- nodePhostAddrs := node.PeerHost.Addrs()
803
- addrs := make([]string, len(nodePhostAddrs))
804
- for i, addr := range nodePhostAddrs {
805
- addrs[i] = addr.String()
798
+ // Multiple libp2p transports can use same port.
799
+ // Deduplicate all listeners and collect unique IP:port (udp|tcp) combinations
800
+ // which is useful information for operator deploying Kubo in TCP/IP infra.
801
+ addrMap := make(map[string]map[string]struct{})
802
+ re := regexp.MustCompile(`^/(?:ip[46]|dns(?:[46])?)/([^/]+)/(tcp|udp)/(\d+)(/.*)?$`)
803
+ for _, addr := range ifaceAddrs {
804
+ matches := re.FindStringSubmatch(addr.String())
805
+ if matches != nil {
806
+ hostname := matches[1]
807
+ protocol := strings.ToUpper(matches[2])
808
+ port := matches[3]
809
+ var host string
810
+ if matches[0][:4] == "/ip6" {
811
+ host = fmt.Sprintf("[%s]:%s", hostname, port)
812
+ } else {
813
+ host = fmt.Sprintf("%s:%s", hostname, port)
814
+ }
815
+ if _, ok := addrMap[host]; !ok {
816
+ addrMap[host] = make(map[string]struct{})
817
+ }
818
+ addrMap[host][protocol] = struct{}{}
819
+ }
820
}
807
- sort.Strings(addrs)
808
- for _, addr := range addrs {
809
- fmt.Printf("Swarm announcing %s\n", addr)
821
+ for host, protocolsSet := range addrMap {
822
+ protocols := make([]string, 0, len(protocolsSet))
823
+ for protocol := range protocolsSet {
824
+ protocols = append(protocols, protocol)
825
+ }
826
+ sort.Strings(protocols)
827
+ fmt.Printf("Swarm listening on %s (%s)\n", host, strings.Join(protocols, "+"))
828
}
829
+ fmt.Printf("Run 'ipfs id' to inspect announced and discovered multiaddrs of this node.\n")
830
}
831
832
// serveHTTPGateway collects options, creates listener, prints status message and starts serving requests.
docs/changelogs/v0.30.md
+29
@@ -10,6 +10,7 @@
10
- [AutoNAT V2 Service Introduced Alongside V1](#autonat-v2-service-introduced-alongside-v1)
11
- [Automated `ipfs version check`](#automated-ipfs-version-check)
12
- [Version Suffix Configuration](#version-suffix-configuration)
13
+ - [Cleaned Up `ipfs daemon` Startup Log](#cleaned-up-ipfs-daemon-startup-log)
14
- [📝 Changelog](#-changelog)
15
- [👨👩👧👦 Contributors](#-contributors)
16
@@ -48,6 +49,34 @@ Defining the optional agent version suffix is now simpler. The [`Version.AgentSu
49
50
> [!NOTE]
51
> Setting a custom version suffix helps with ecosystem analysis, such as Amino DHT reports published at https://stats.ipfs.network
52
+>
53
+
54
+#### Cleaned Up `ipfs daemon` Startup Log
55
+
56
+The `ipfs daemon` startup output has been streamlined to enhance clarity and usability:
57
+
58
+```console
59
+$ ipfs daemon
60
+Initializing daemon...
61
+Kubo version: 0.30.0
62
+Repo version: 16
63
+System version: amd64/linux
64
+Golang version: go1.22.5
65
+PeerID: 12D3KooWQ73s1CQsm4jWwQvdCAtc5w8LatyQt7QLQARk5xdhK9CE
66
+Swarm listening on 127.0.0.1:4001 (TCP+UDP)
67
+Swarm listening on 192.0.2.10:4001 (TCP+UDP)
68
+Swarm listening on [::1]:4001 (TCP+UDP)
69
+Swarm listening on [2001:0db8::10]:4001 (TCP+UDP)
70
+Run 'ipfs id' to inspect announced and discovered multiaddrs of this node.
71
+RPC API server listening on /ip4/127.0.0.1/tcp/5001
72
+WebUI: http://127.0.0.1:5001/webui
73
+Gateway server listening on /ip4/127.0.0.1/tcp/8080
74
+Daemon is ready
75
+```
76
+
77
+The previous lengthy listing of all listener and announced multiaddrs has been removed due to its complexity, especially with modern libp2p nodes sharing multiple transports and long lists of `/webtransport` and `/webrtc-direct` certhashes.
78
+The output now features a simplified list of swarm listeners, displayed in the format `host:port (TCP+UDP)`, which provides essential information for debugging connectivity issues, particularly related to port forwarding.
79
+Announced libp2p addresses are no longer printed on startup, because libp2p may change or augument them based on AutoNAT, relay, and UPnP state. Instead, users are prompted to run `ipfs id` to obtain up-to-date list of listeners and announced multiaddrs in libp2p format.
80
81
### 📝 Changelog
82
test/sharness/t0060-daemon.sh
+10
-11
@@ -76,17 +76,16 @@ test_expect_success "ipfs gateway works with the correct allowed origin port" '
76
curl -s -X POST -H "Origin:http://localhost:$GWAY_PORT" -I "http://$GWAY_ADDR/api/v0/version"
77
'
78
79
-test_expect_success "ipfs daemon output looks good" '
80
- STARTFILE="ipfs cat /ipfs/$HASH_WELCOME_DOCS/readme" &&
81
- echo "Initializing daemon..." >expected_daemon &&
82
- ipfs version --all >> expected_daemon &&
83
- sed "s/^/Swarm listening on /" listen_addrs >>expected_daemon &&
84
- sed "s/^/Swarm announcing /" local_addrs >>expected_daemon &&
85
- echo "RPC API server listening on '$API_MADDR'" >>expected_daemon &&
86
- echo "WebUI: http://'$API_ADDR'/webui" >>expected_daemon &&
87
- echo "Gateway server listening on '$GWAY_MADDR'" >>expected_daemon &&
88
- echo "Daemon is ready" >>expected_daemon &&
89
- test_cmp expected_daemon actual_daemon
79
+test_expect_success "ipfs daemon output includes looks good" '
80
+ test_should_contain "Initializing daemon..." actual_daemon &&
81
+ test_should_contain "$(ipfs version --all)" actual_daemon &&
82
+ test_should_contain "PeerID: $(ipfs config Identity.PeerID)" actual_daemon &&
83
+ test_should_contain "Swarm listening on 127.0.0.1:" actual_daemon &&
84
+ test_should_contain "RPC API server listening on '$API_MADDR'" actual_daemon &&
85
+ test_should_contain "WebUI: http://'$API_ADDR'/webui" actual_daemon &&
86
+ test_should_contain "Gateway server listening on '$GWAY_MADDR'" actual_daemon &&
87
+ test_should_contain "Daemon is ready" actual_daemon &&
88
+ cat actual_daemon
89
'
90
91
test_expect_success ".ipfs/ has been created" '