Skip to content

Commit

Permalink
feat(cmds): add version check command
Browse files Browse the repository at this point in the history
  • Loading branch information
schomatis authored and lidel committed Jul 4, 2023
1 parent 895963b commit 8077843
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 5 deletions.
150 changes: 145 additions & 5 deletions core/commands/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,24 @@ import (
"fmt"
"io"
"runtime/debug"
"strings"

version "github.com/ipfs/kubo"
"github.com/ipfs/kubo/core/commands/cmdenv"

cmds "github.com/ipfs/go-ipfs-cmds"

versioncmp "github.com/hashicorp/go-version"
"github.com/libp2p/go-libp2p-kad-dht/fullrt"
pstore "github.com/libp2p/go-libp2p/core/peerstore"
)

const (
versionNumberOptionName = "number"
versionCommitOptionName = "commit"
versionRepoOptionName = "repo"
versionAllOptionName = "all"
versionNumberOptionName = "number"
versionCommitOptionName = "commit"
versionRepoOptionName = "repo"
versionAllOptionName = "all"
versionCompareNewFractionOptionName = "--newer-fraction"
)

var VersionCmd = &cmds.Command{
Expand All @@ -24,7 +31,8 @@ var VersionCmd = &cmds.Command{
ShortDescription: "Returns the current version of IPFS and exits.",
},
Subcommands: map[string]*cmds.Command{
"deps": depsVersionCommand,
"deps": depsVersionCommand,
"check": checkVersionCommand,
},

Options: []cmds.Option{
Expand Down Expand Up @@ -130,3 +138,135 @@ Print out all dependencies and their versions.`,
}),
},
}

type CheckOutput struct {
PeersCounted int
GreatestVersion string
OldVersion bool
}

var checkVersionCommand = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Checks IPFS version against network (online only).",
ShortDescription: `
Checks node versions in our DHT to compare if we're running an older version.`,
},
Options: []cmds.Option{
cmds.FloatOption(versionCompareNewFractionOptionName, "f", "Fraction of peers with new version to generate update warning.").WithDefault(0.5),
},
Type: CheckOutput{},

Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
nd, err := cmdenv.GetNode(env)
if err != nil {
return err
}

if !nd.IsOnline {
return ErrNotOnline
}

if nd.DHT == nil {
return ErrNotDHT
}

ourVersion, err := versioncmp.NewVersion(strings.Replace(version.CurrentVersionNumber, "-dev", "", -1))
if err != nil {
return fmt.Errorf("could not parse our own version %s: %w",
version.CurrentVersionNumber, err)
}

greatestVersionSeen := ourVersion
totalPeersCounted := 1 // Us (and to avoid division-by-zero edge case).
withGreaterVersion := 0

recordPeerVersion := func(agentVersion string) {
// We process the version as is it assembled in GetUserAgentVersion.
segments := strings.Split(agentVersion, "/")
if len(segments) < 2 {
return
}
if segments[0] != "go-ipfs" {
return
}
versionNumber := segments[1] // As in our CurrentVersionNumber.

// Ignore development releases.
if strings.Contains(versionNumber, "-dev") {
return
}
if strings.Contains(versionNumber, "-rc") {
return
}

peerVersion, err := versioncmp.NewVersion(versionNumber)
if err != nil {
// Do not error on invalid remote versions, just ignore.
return
}

// Valid peer version number.
totalPeersCounted += 1
if ourVersion.LessThan(peerVersion) {
withGreaterVersion += 1
}
if peerVersion.GreaterThan(greatestVersionSeen) {
greatestVersionSeen = peerVersion
}
}

// Logic taken from `ipfs stats dht` command.
if nd.DHTClient != nd.DHT {
client, ok := nd.DHTClient.(*fullrt.FullRT)
if !ok {
return cmds.Errorf(cmds.ErrClient, "could not generate stats for the WAN DHT client type")
}
for _, p := range client.Stat() {
if ver, err := nd.Peerstore.Get(p, "AgentVersion"); err == nil {
recordPeerVersion(ver.(string))
} else if err == pstore.ErrNotFound {
// ignore
} else {
// this is a bug, usually.
log.Errorw(
"failed to get agent version from peerstore",
"error", err,
)
}
}
} else {
for _, pi := range nd.DHT.WAN.RoutingTable().GetPeerInfos() {
if ver, err := nd.Peerstore.Get(pi.Id, "AgentVersion"); err == nil {
recordPeerVersion(ver.(string))
} else if err == pstore.ErrNotFound {
// ignore
} else {
// this is a bug, usually.
log.Errorw(
"failed to get agent version from peerstore",
"error", err,
)
}
}
}

newerFraction, _ := req.Options[versionCompareNewFractionOptionName].(float64)
if err := cmds.EmitOnce(res, CheckOutput{
PeersCounted: totalPeersCounted,
GreatestVersion: greatestVersionSeen.String(),
OldVersion: (float64(withGreaterVersion) / float64(totalPeersCounted)) > newerFraction,
}); err != nil {
return err
}
return nil
},
Encoders: cmds.EncoderMap{
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, checkOutput CheckOutput) error {
if checkOutput.OldVersion {
fmt.Fprintf(w, "WARNING: we're running an outdated version compared to our peers (%d discovered), update to %s\n",
checkOutput.PeersCounted, checkOutput.GreatestVersion)
}
return nil
}),
},
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/fsnotify/fsnotify v1.6.0
github.com/google/uuid v1.3.0
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-version v1.6.0
github.com/ipfs/boxo v0.10.2-0.20230629140307-fdad9f921191
github.com/ipfs/go-block-format v0.1.2
github.com/ipfs/go-cid v0.4.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
Expand Down

0 comments on commit 8077843

Please sign in to comment.