Skip to content

Commit

Permalink
add inspector command for system inspection (#2663)
Browse files Browse the repository at this point in the history
* add inspector command and tests
  • Loading branch information
frrist authored May 2, 2019
1 parent b2a1542 commit 92f0b9e
Show file tree
Hide file tree
Showing 9 changed files with 469 additions and 3 deletions.
1 change: 1 addition & 0 deletions commands/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ func runAPIAndWait(ctx context.Context, nd *node.Node, config *config.Config, re
// TODO: should this be the passed in context? Issue 2641
blockMiningAPI: nd.BlockMiningAPI,
ctx: context.Background(),
inspectorAPI: NewInspectorAPI(nd.Repo),
porcelainAPI: nd.PorcelainAPI,
retrievalAPI: nd.RetrievalAPI,
storageAPI: nd.StorageAPI,
Expand Down
13 changes: 10 additions & 3 deletions commands/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Env struct {
porcelainAPI *porcelain.API
retrievalAPI *retrieval.API
storageAPI *storage.API
inspectorAPI *Inspector
}

var _ cmds.Environment = (*Env)(nil)
Expand All @@ -33,20 +34,26 @@ func GetPorcelainAPI(env cmds.Environment) *porcelain.API {
return ce.porcelainAPI
}

// GetBlockAPI returns the block protocol api from the given environment
// GetBlockAPI returns the block protocol api from the given environment.
func GetBlockAPI(env cmds.Environment) *block.MiningAPI {
ce := env.(*Env)
return ce.blockMiningAPI
}

// GetRetrievalAPI returns the retrieval protocol api from the given environment
// GetRetrievalAPI returns the retrieval protocol api from the given environment.
func GetRetrievalAPI(env cmds.Environment) *retrieval.API {
ce := env.(*Env)
return ce.retrievalAPI
}

// GetStorageAPI returns the storage protocol api from the given environment
// GetStorageAPI returns the storage protocol api from the given environment.
func GetStorageAPI(env cmds.Environment) *storage.API {
ce := env.(*Env)
return ce.storageAPI
}

// GetInspectorAPI returns the inspector api from the given environment.
func GetInspectorAPI(env cmds.Environment) *Inspector {
ce := env.(*Env)
return ce.inspectorAPI
}
247 changes: 247 additions & 0 deletions commands/inspector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
package commands

import (
"os"
"runtime"

cmdkit "github.com/ipfs/go-ipfs-cmdkit"
cmds "github.com/ipfs/go-ipfs-cmds"
sysi "github.com/whyrusleeping/go-sysinfo"

"github.com/filecoin-project/go-filecoin/config"
"github.com/filecoin-project/go-filecoin/repo"
)

var inspectCmd = &cmds.Command{
Helptext: cmdkit.HelpText{
Tagline: "Show info about the filecoin node",
},
Subcommands: map[string]*cmds.Command{
"all": allInspectCmd,
"runtime": runtimeInspectCmd,
"disk": diskInspectCmd,
"memory": memoryInspectCmd,
"config": configInspectCmd,
"environment": envInspectCmd,
},
}
var allInspectCmd = &cmds.Command{
Helptext: cmdkit.HelpText{
Tagline: "Print all diagnostic information.",
ShortDescription: `
Prints out information about filecoin process and its environment.
`,
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
var allInfo AllInspectorInfo
allInfo.Runtime = GetInspectorAPI(env).Runtime()

dsk, err := GetInspectorAPI(env).Disk()
if err != nil {
return err
}
allInfo.Disk = dsk

mem, err := GetInspectorAPI(env).Memory()
if err != nil {
return err
}
allInfo.Memory = mem
allInfo.Config = GetInspectorAPI(env).Config()
allInfo.Environment = GetInspectorAPI(env).Environment()
return cmds.EmitOnce(res, allInfo)
},
}

var runtimeInspectCmd = &cmds.Command{
Helptext: cmdkit.HelpText{
Tagline: "Print runtime diagnostic information.",
ShortDescription: `
Prints out information about the golang runtime.
`,
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
out := GetInspectorAPI(env).Runtime()
return cmds.EmitOnce(res, out)
},
}

var diskInspectCmd = &cmds.Command{
Helptext: cmdkit.HelpText{
Tagline: "Print filesystem usage information.",
ShortDescription: `
Prints out information about the filesystem.
`,
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
out, err := GetInspectorAPI(env).Disk()
if err != nil {
return err
}
return cmds.EmitOnce(res, out)
},
}

var memoryInspectCmd = &cmds.Command{
Helptext: cmdkit.HelpText{
Tagline: "Print memory usage information.",
ShortDescription: `
Prints out information about memory usage.
`,
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
out, err := GetInspectorAPI(env).Memory()
if err != nil {
return err
}
return cmds.EmitOnce(res, out)
},
}

var configInspectCmd = &cmds.Command{
Helptext: cmdkit.HelpText{
Tagline: "Print in-memory config information.",
ShortDescription: `
Prints out information about your filecoin nodes config.
`,
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
out := GetInspectorAPI(env).Config()
return cmds.EmitOnce(res, out)
},
}

var envInspectCmd = &cmds.Command{
Helptext: cmdkit.HelpText{
Tagline: "Print filecoin environment information.",
ShortDescription: `
Prints out information about your filecoin nodes environment.
`,
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
out := GetInspectorAPI(env).Environment()
return cmds.EmitOnce(res, out)
},
}

// NewInspectorAPI returns a `Inspector` used to inspect the go-filecoin node.
func NewInspectorAPI(r repo.Repo) *Inspector {
return &Inspector{
repo: r,
}
}

// Inspector contains information used to inspect the go-filecoin node.
type Inspector struct {
repo repo.Repo
}

// AllInspectorInfo contains all information the inspector can gather.
type AllInspectorInfo struct {
Config *config.Config
Runtime *RuntimeInfo
Environment *EnvironmentInfo
Disk *DiskInfo
Memory *MemoryInfo
}

// RuntimeInfo contains information about the golang runtime.
type RuntimeInfo struct {
OS string
Arch string
Version string
Compiler string
NumProc int
GoMaxProcs int
NumGoRoutines int
NumCGoCalls int64
}

// EnvironmentInfo contains information about the environment filecoin is running in.
type EnvironmentInfo struct {
FilAPI string `json:"FIL_API"`
FilPath string `json:"FIL_PATH"`
GoPath string `json:"GOPATH"`
}

// DiskInfo contains information about disk usage and type.
type DiskInfo struct {
Free uint64
Total uint64
FSType string
}

// MemoryInfo contains information about memory usage.
type MemoryInfo struct {
Swap uint64
Virtual uint64
}

// Runtime returns infrormation about the golang runtime.
func (g *Inspector) Runtime() *RuntimeInfo {
return &RuntimeInfo{
OS: runtime.GOOS,
Arch: runtime.GOARCH,
Version: runtime.Version(),
Compiler: runtime.Compiler,
NumProc: runtime.NumCPU(),
GoMaxProcs: runtime.GOMAXPROCS(0),
NumGoRoutines: runtime.NumGoroutine(),
NumCGoCalls: runtime.NumCgoCall(),
}
}

// Environment returns information about the environment filecoin is running in.
func (g *Inspector) Environment() *EnvironmentInfo {
return &EnvironmentInfo{
FilAPI: os.Getenv("FIL_API"),
FilPath: os.Getenv("FIL_PATH"),
GoPath: os.Getenv("GOPATH"),
}
}

// Disk return information about filesystem the filecoin nodes repo is on.
func (g *Inspector) Disk() (*DiskInfo, error) {
fsr, ok := g.repo.(*repo.FSRepo)
if !ok {
// we are using a in memory repo
return &DiskInfo{
Free: 0,
Total: 0,
FSType: "0",
}, nil
}

p, err := fsr.Path()
if err != nil {
return nil, err
}

dinfo, err := sysi.DiskUsage(p)
if err != nil {
return nil, err
}

return &DiskInfo{
Free: dinfo.Free,
Total: dinfo.Total,
FSType: dinfo.FsType,
}, nil
}

// Memory return information about system meory usage.
func (g *Inspector) Memory() (*MemoryInfo, error) {
meminfo, err := sysi.MemoryInfo()
if err != nil {
return nil, err
}
return &MemoryInfo{
Swap: meminfo.Swap,
Virtual: meminfo.Used,
}, nil
}

// Config return the current config values of the filecoin node.
func (g *Inspector) Config() *config.Config {
return g.repo.Config()
}
52 changes: 52 additions & 0 deletions commands/inspector_daemon_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package commands_test

import (
"context"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

tf "github.com/filecoin-project/go-filecoin/testhelpers/testflags"
"github.com/filecoin-project/go-filecoin/tools/fast"
"github.com/filecoin-project/go-filecoin/tools/fast/fastesting"
)

func TestInspectConfig(t *testing.T) {
tf.IntegrationTest(t)

ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Second))
defer cancel()

// Get basic testing environment
ctx, env := fastesting.NewTestEnvironment(ctx, t, fast.EnvironmentOpts{})

// Teardown after test ends
defer func() {
err := env.Teardown(ctx)
require.NoError(t, err)
}()

nd := env.RequireNewNodeStarted()

icfg, err := nd.InspectConfig(ctx)
require.NoError(t, err)

rcfg, err := nd.Config()
require.NoError(t, err)

// API is not the same since the FAST plugin reads the config file from disk,
// this is a limitation of FAST.
// FAST sets API.Address to /ip4/0.0.0.0/0 in the config file to avoid port collisions.
// The Inspector returns an in memory representation of the config that has
// received a port assignment from the kernel, meaning it is no longer /ip4/0.0.0.0/0
assert.Equal(t, rcfg.Bootstrap, icfg.Bootstrap)
assert.Equal(t, rcfg.Datastore, icfg.Datastore)
assert.Equal(t, rcfg.Swarm, icfg.Swarm)
assert.Equal(t, rcfg.Mining, icfg.Mining)
assert.Equal(t, rcfg.Wallet, icfg.Wallet)
assert.Equal(t, rcfg.Heartbeat, icfg.Heartbeat)
assert.Equal(t, rcfg.Net, icfg.Net)
assert.Equal(t, rcfg.Mpool, icfg.Mpool)
}
Loading

0 comments on commit 92f0b9e

Please sign in to comment.