Skip to content

Commit

Permalink
feat(daemon): improve stdout on startup (#10472)
Browse files Browse the repository at this point in the history
  • Loading branch information
lidel authored Aug 14, 2024
1 parent a339e6e commit 0d42831
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 38 deletions.
73 changes: 46 additions & 27 deletions cmd/ipfs/kubo/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import (
"net/http"
_ "net/http/pprof"
"os"
"regexp"
"runtime"
"sort"
"strings"
"sync"
"time"

Expand Down Expand Up @@ -89,7 +91,7 @@ running, calls to 'ipfs' commands will be sent over the network to
the daemon.
`,
LongDescription: `
The daemon will start listening on ports on the network, which are
The Kubo daemon will start listening on ports on the network, which are
documented in (and can be modified through) 'ipfs config Addresses'.
For example, to change the 'Gateway' port:
Expand All @@ -109,11 +111,11 @@ other computers in the network, use 0.0.0.0 as the ip address:
Be careful if you expose the RPC API. It is a security risk, as anyone could
control your node remotely. If you need to control the node remotely,
make sure to protect the port as you would other services or database
(firewall, authenticated proxy, etc).
(firewall, authenticated proxy, etc), or at least set API.Authorizations.
HTTP Headers
ipfs supports passing arbitrary headers to the RPC API and Gateway. You can
Kubo supports passing arbitrary headers to the RPC API and Gateway. You can
do this by setting headers on the API.HTTPHeaders and Gateway.HTTPHeaders
keys:
Expand All @@ -124,7 +126,7 @@ Note that the value of the keys is an _array_ of strings. This is because
headers can have more than one value, and it is convenient to pass through
to other libraries.
CORS Headers (for API)
CORS Headers (for RPC API)
You can setup CORS headers the same way:
Expand All @@ -141,15 +143,15 @@ second signal.
IPFS_PATH environment variable
ipfs uses a repository in the local file system. By default, the repo is
Kubo uses a repository in the local file system. By default, the repo is
located at ~/.ipfs. To change the repo location, set the $IPFS_PATH
environment variable:
export IPFS_PATH=/path/to/ipfsrepo
DEPRECATION NOTICE
Previously, ipfs used an environment variable as seen below:
Previously, Kubo used an environment variable as seen below:
export API_ORIGIN="http://localhost:8888/"
Expand All @@ -160,14 +162,14 @@ Headers.
},

Options: []cmds.Option{
cmds.BoolOption(initOptionKwd, "Initialize ipfs with default settings if not already initialized"),
cmds.BoolOption(initOptionKwd, "Initialize Kubo with default settings if not already initialized"),
cmds.StringOption(initConfigOptionKwd, "Path to existing configuration file to be loaded during --init"),
cmds.StringOption(initProfileOptionKwd, "Configuration profiles to apply for --init. See ipfs init --help for more"),
cmds.StringOption(routingOptionKwd, "Overrides the routing option").WithDefault(routingOptionDefaultKwd),
cmds.BoolOption(mountKwd, "Mounts IPFS to the filesystem using FUSE (experimental)"),
cmds.StringOption(ipfsMountKwd, "Path to the mountpoint for IPFS (if using --mount). Defaults to config setting."),
cmds.StringOption(ipnsMountKwd, "Path to the mountpoint for IPNS (if using --mount). Defaults to config setting."),
cmds.BoolOption(unrestrictedAPIAccessKwd, "Allow API access to unlisted hashes"),
cmds.BoolOption(unrestrictedAPIAccessKwd, "Allow RPC API access to unlisted hashes"),
cmds.BoolOption(unencryptTransportKwd, "Disable transport encryption (for debugging protocols)"),
cmds.BoolOption(enableGCKwd, "Enable automatic periodic repo garbage collection"),
cmds.BoolOption(adjustFDLimitKwd, "Check and raise file descriptor limits if needed").WithDefault(true),
Expand Down Expand Up @@ -373,6 +375,8 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
return err
}

fmt.Printf("PeerID: %s\n", cfg.Identity.PeerID)

if !psSet {
pubsub = cfg.Pubsub.Enabled.WithDefault(false)
}
Expand Down Expand Up @@ -463,7 +467,7 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
log.Fatal("Private network does not work with Routing.Type=auto. Update your config to Routing.Type=dht (or none, and do manual peering)")
}

printSwarmAddrs(node)
printLibp2pPorts(node)

if node.PrivateKey.Type() == p2pcrypto.RSA {
fmt.Print(`
Expand Down Expand Up @@ -563,7 +567,7 @@ take effect.
// Add ipfs version info to prometheus metrics
ipfsInfoMetric := promauto.NewGaugeVec(prometheus.GaugeOpts{
Name: "ipfs_info",
Help: "IPFS version information.",
Help: "Kubo IPFS version information.",
}, []string{"version", "commit"})

// Setting to 1 lets us multiply it with other stats to add the version labels
Expand Down Expand Up @@ -779,8 +783,8 @@ func rewriteMaddrToUseLocalhostIfItsAny(maddr ma.Multiaddr) ma.Multiaddr {
}
}

// printSwarmAddrs prints the addresses of the host.
func printSwarmAddrs(node *core.IpfsNode) {
// printLibp2pPorts prints which ports are opened to facilitate swarm connectivity.
func printLibp2pPorts(node *core.IpfsNode) {
if !node.IsOnline {
fmt.Println("Swarm not listening, running in offline mode.")
return
Expand All @@ -790,24 +794,39 @@ func printSwarmAddrs(node *core.IpfsNode) {
if err != nil {
log.Errorf("failed to read listening addresses: %s", err)
}
lisAddrs := make([]string, len(ifaceAddrs))
for i, addr := range ifaceAddrs {
lisAddrs[i] = addr.String()
}
sort.Strings(lisAddrs)
for _, addr := range lisAddrs {
fmt.Printf("Swarm listening on %s\n", addr)
}

nodePhostAddrs := node.PeerHost.Addrs()
addrs := make([]string, len(nodePhostAddrs))
for i, addr := range nodePhostAddrs {
addrs[i] = addr.String()
// Multiple libp2p transports can use same port.
// Deduplicate all listeners and collect unique IP:port (udp|tcp) combinations
// which is useful information for operator deploying Kubo in TCP/IP infra.
addrMap := make(map[string]map[string]struct{})
re := regexp.MustCompile(`^/(?:ip[46]|dns(?:[46])?)/([^/]+)/(tcp|udp)/(\d+)(/.*)?$`)
for _, addr := range ifaceAddrs {
matches := re.FindStringSubmatch(addr.String())
if matches != nil {
hostname := matches[1]
protocol := strings.ToUpper(matches[2])
port := matches[3]
var host string
if matches[0][:4] == "/ip6" {
host = fmt.Sprintf("[%s]:%s", hostname, port)
} else {
host = fmt.Sprintf("%s:%s", hostname, port)
}
if _, ok := addrMap[host]; !ok {
addrMap[host] = make(map[string]struct{})
}
addrMap[host][protocol] = struct{}{}
}
}
sort.Strings(addrs)
for _, addr := range addrs {
fmt.Printf("Swarm announcing %s\n", addr)
for host, protocolsSet := range addrMap {
protocols := make([]string, 0, len(protocolsSet))
for protocol := range protocolsSet {
protocols = append(protocols, protocol)
}
sort.Strings(protocols)
fmt.Printf("Swarm listening on %s (%s)\n", host, strings.Join(protocols, "+"))
}
fmt.Printf("Run 'ipfs id' to inspect announced and discovered multiaddrs of this node.\n")
}

// serveHTTPGateway collects options, creates listener, prints status message and starts serving requests.
Expand Down
29 changes: 29 additions & 0 deletions docs/changelogs/v0.30.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- [AutoNAT V2 Service Introduced Alongside V1](#autonat-v2-service-introduced-alongside-v1)
- [Automated `ipfs version check`](#automated-ipfs-version-check)
- [Version Suffix Configuration](#version-suffix-configuration)
- [Cleaned Up `ipfs daemon` Startup Log](#cleaned-up-ipfs-daemon-startup-log)
- [📝 Changelog](#-changelog)
- [👨‍👩‍👧‍👦 Contributors](#-contributors)

Expand Down Expand Up @@ -48,6 +49,34 @@ Defining the optional agent version suffix is now simpler. The [`Version.AgentSu

> [!NOTE]
> Setting a custom version suffix helps with ecosystem analysis, such as Amino DHT reports published at https://stats.ipfs.network
>
#### Cleaned Up `ipfs daemon` Startup Log

The `ipfs daemon` startup output has been streamlined to enhance clarity and usability:

```console
$ ipfs daemon
Initializing daemon...
Kubo version: 0.30.0
Repo version: 16
System version: amd64/linux
Golang version: go1.22.5
PeerID: 12D3KooWQ73s1CQsm4jWwQvdCAtc5w8LatyQt7QLQARk5xdhK9CE
Swarm listening on 127.0.0.1:4001 (TCP+UDP)
Swarm listening on 192.0.2.10:4001 (TCP+UDP)
Swarm listening on [::1]:4001 (TCP+UDP)
Swarm listening on [2001:0db8::10]:4001 (TCP+UDP)
Run 'ipfs id' to inspect announced and discovered multiaddrs of this node.
RPC API server listening on /ip4/127.0.0.1/tcp/5001
WebUI: http://127.0.0.1:5001/webui
Gateway server listening on /ip4/127.0.0.1/tcp/8080
Daemon is ready
```

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.
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.
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.

### 📝 Changelog

Expand Down
21 changes: 10 additions & 11 deletions test/sharness/t0060-daemon.sh
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,16 @@ test_expect_success "ipfs gateway works with the correct allowed origin port" '
curl -s -X POST -H "Origin:http://localhost:$GWAY_PORT" -I "http://$GWAY_ADDR/api/v0/version"
'

test_expect_success "ipfs daemon output looks good" '
STARTFILE="ipfs cat /ipfs/$HASH_WELCOME_DOCS/readme" &&
echo "Initializing daemon..." >expected_daemon &&
ipfs version --all >> expected_daemon &&
sed "s/^/Swarm listening on /" listen_addrs >>expected_daemon &&
sed "s/^/Swarm announcing /" local_addrs >>expected_daemon &&
echo "RPC API server listening on '$API_MADDR'" >>expected_daemon &&
echo "WebUI: http://'$API_ADDR'/webui" >>expected_daemon &&
echo "Gateway server listening on '$GWAY_MADDR'" >>expected_daemon &&
echo "Daemon is ready" >>expected_daemon &&
test_cmp expected_daemon actual_daemon
test_expect_success "ipfs daemon output includes looks good" '
test_should_contain "Initializing daemon..." actual_daemon &&
test_should_contain "$(ipfs version --all)" actual_daemon &&
test_should_contain "PeerID: $(ipfs config Identity.PeerID)" actual_daemon &&
test_should_contain "Swarm listening on 127.0.0.1:" actual_daemon &&
test_should_contain "RPC API server listening on '$API_MADDR'" actual_daemon &&
test_should_contain "WebUI: http://'$API_ADDR'/webui" actual_daemon &&
test_should_contain "Gateway server listening on '$GWAY_MADDR'" actual_daemon &&
test_should_contain "Daemon is ready" actual_daemon &&
cat actual_daemon
'

test_expect_success ".ipfs/ has been created" '
Expand Down

0 comments on commit 0d42831

Please sign in to comment.