From bb9904bde148eb9566d2a442f8f9fa1f2372ff9e Mon Sep 17 00:00:00 2001 From: Thomas Gardner Date: Wed, 3 Feb 2016 18:51:32 +1000 Subject: [PATCH 1/4] repo: add `ipfs repo stat` command License: MIT Signed-off-by: Thomas Gardner --- core/commands/repo.go | 69 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/core/commands/repo.go b/core/commands/repo.go index 3b634e6311c..7af24f8bf51 100644 --- a/core/commands/repo.go +++ b/core/commands/repo.go @@ -4,12 +4,20 @@ import ( "bytes" "fmt" "io" + "strings" cmds "github.com/ipfs/go-ipfs/commands" corerepo "github.com/ipfs/go-ipfs/core/corerepo" + fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" ) +type RepoStat struct { + repoPath string + repoSize uint64 // size in bytes + numBlocks uint64 +} + var RepoCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Manipulate the IPFS repo.", @@ -19,7 +27,8 @@ var RepoCmd = &cmds.Command{ }, Subcommands: map[string]*cmds.Command{ - "gc": repoGcCmd, + "gc": repoGcCmd, + "stat": repoStatCmd, }, } @@ -95,3 +104,61 @@ order to reclaim hard disk space. }, }, } + +var repoStatCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Print status of the local repo.", + ShortDescription: ``, + }, + Run: func(req cmds.Request, res cmds.Response) { + ctx := req.Context() + n, err := req.InvocContext().GetNode() + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + usage, err := n.Repo.GetStorageUsage() + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + allKeys, err := n.Blockstore.AllKeysChan(ctx) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + count := uint64(0) + for range allKeys { + count++ + } + + path, err := fsrepo.BestKnownPath() + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + out := &RepoStat{ + repoPath: path, + repoSize: usage, + numBlocks: count, + } + res.SetOutput(out) + }, + Marshalers: cmds.MarshalerMap{ + cmds.Text: func(res cmds.Response) (io.Reader, error) { + stat, ok := res.Output().(*RepoStat) + if !ok { + return nil, u.ErrCast() + } + + out := fmt.Sprintf("Path: %s\nSize: %d bytes\n"+ + "Blocks: %d\n", + stat.repoPath, stat.repoSize, stat.numBlocks) + return strings.NewReader(out), nil + }, + }, +} From f988a54c1bb6a0fbcadaef0a0ef4edee7d26f163 Mon Sep 17 00:00:00 2001 From: Thomas Gardner Date: Fri, 5 Feb 2016 11:22:49 +1000 Subject: [PATCH 2/4] `repo stat`: add Type License: MIT Signed-off-by: Thomas Gardner --- core/commands/repo.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/core/commands/repo.go b/core/commands/repo.go index 7af24f8bf51..445d3a9c659 100644 --- a/core/commands/repo.go +++ b/core/commands/repo.go @@ -141,13 +141,13 @@ var repoStatCmd = &cmds.Command{ return } - out := &RepoStat{ + res.SetOutput(&RepoStat{ repoPath: path, repoSize: usage, numBlocks: count, - } - res.SetOutput(out) + }) }, + Type: RepoStat{}, Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) (io.Reader, error) { stat, ok := res.Output().(*RepoStat) @@ -155,9 +155,10 @@ var repoStatCmd = &cmds.Command{ return nil, u.ErrCast() } - out := fmt.Sprintf("Path: %s\nSize: %d bytes\n"+ - "Blocks: %d\n", + out := fmt.Sprintf( + "Path: %s\nSize: %d bytes\nBlocks: %d\n", stat.repoPath, stat.repoSize, stat.numBlocks) + return strings.NewReader(out), nil }, }, From 288f7bc62143734452a47262dee5c8ce714bf551 Mon Sep 17 00:00:00 2001 From: Thomas Gardner Date: Wed, 10 Feb 2016 20:07:31 +1000 Subject: [PATCH 3/4] Humanize byte size License: MIT Signed-off-by: Thomas Gardner --- core/commands/repo.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/commands/repo.go b/core/commands/repo.go index 445d3a9c659..7054ae9821b 100644 --- a/core/commands/repo.go +++ b/core/commands/repo.go @@ -6,6 +6,7 @@ import ( "io" "strings" + humanize "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/dustin/go-humanize" cmds "github.com/ipfs/go-ipfs/commands" corerepo "github.com/ipfs/go-ipfs/core/corerepo" fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo" @@ -156,8 +157,8 @@ var repoStatCmd = &cmds.Command{ } out := fmt.Sprintf( - "Path: %s\nSize: %d bytes\nBlocks: %d\n", - stat.repoPath, stat.repoSize, stat.numBlocks) + "Path: %s\nSize: %s\nBlocks: %d\n", + stat.repoPath, humanize.Bytes(stat.repoSize), stat.numBlocks) return strings.NewReader(out), nil }, From c10e329b789b931fa384d4cf83ad1d23d5cf1a57 Mon Sep 17 00:00:00 2001 From: David Dias Date: Wed, 2 Mar 2016 08:48:51 +0000 Subject: [PATCH 4/4] repo-stat License: MIT Signed-off-by: David Dias --- core/commands/repo.go | 74 ++++++++++++++++--------------------- core/corerepo/stat.go | 43 +++++++++++++++++++++ test/sharness/t0080-repo.sh | 25 +++++++++++++ 3 files changed, 99 insertions(+), 43 deletions(-) create mode 100644 core/corerepo/stat.go diff --git a/core/commands/repo.go b/core/commands/repo.go index 7054ae9821b..e4ac8807d64 100644 --- a/core/commands/repo.go +++ b/core/commands/repo.go @@ -3,22 +3,12 @@ package commands import ( "bytes" "fmt" - "io" - "strings" - - humanize "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/dustin/go-humanize" cmds "github.com/ipfs/go-ipfs/commands" corerepo "github.com/ipfs/go-ipfs/core/corerepo" - fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo" u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util" + "io" ) -type RepoStat struct { - repoPath string - repoSize uint64 // size in bytes - numBlocks uint64 -} - var RepoCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Manipulate the IPFS repo.", @@ -108,59 +98,57 @@ order to reclaim hard disk space. var repoStatCmd = &cmds.Command{ Helptext: cmds.HelpText{ - Tagline: "Print status of the local repo.", - ShortDescription: ``, + Tagline: "Get stats for the currently used repo.", + ShortDescription: ` +'ipfs repo stat' is a plumbing command that will scan the local +set of stored objects and print repo statistics. It outputs to stdout: +NumObjects int number of objects in the local repo +RepoSize int size in bytes that the repo is currently taking +RepoPath string the path to the repo being currently used +`, }, Run: func(req cmds.Request, res cmds.Response) { - ctx := req.Context() n, err := req.InvocContext().GetNode() if err != nil { res.SetError(err, cmds.ErrNormal) return } - usage, err := n.Repo.GetStorageUsage() - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - allKeys, err := n.Blockstore.AllKeysChan(ctx) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - count := uint64(0) - for range allKeys { - count++ - } - - path, err := fsrepo.BestKnownPath() + stat, err := corerepo.RepoStat(n, req.Context()) if err != nil { res.SetError(err, cmds.ErrNormal) return } - res.SetOutput(&RepoStat{ - repoPath: path, - repoSize: usage, - numBlocks: count, - }) + res.SetOutput(stat) }, - Type: RepoStat{}, + Options: []cmds.Option{ + cmds.BoolOption("human", "Output RepoSize in MiB."), + }, + Type: corerepo.Stat{}, Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) (io.Reader, error) { - stat, ok := res.Output().(*RepoStat) + stat, ok := res.Output().(*corerepo.Stat) if !ok { return nil, u.ErrCast() } - out := fmt.Sprintf( - "Path: %s\nSize: %s\nBlocks: %d\n", - stat.repoPath, humanize.Bytes(stat.repoSize), stat.numBlocks) + human, _, err := res.Request().Option("human").Bool() + if err != nil { + return nil, err + } + + buf := new(bytes.Buffer) + fmt.Fprintf(buf, "NumObjects \t %d\n", stat.NumObjects) + sizeInMiB := stat.RepoSize / (1024 * 1024) + if human && sizeInMiB > 0 { + fmt.Fprintf(buf, "RepoSize (MiB) \t %d\n", sizeInMiB) + } else { + fmt.Fprintf(buf, "RepoSize \t %d\n", stat.RepoSize) + } + fmt.Fprintf(buf, "RepoPath \t %s\n", stat.RepoPath) - return strings.NewReader(out), nil + return buf, nil }, }, } diff --git a/core/corerepo/stat.go b/core/corerepo/stat.go new file mode 100644 index 00000000000..65abf2efd42 --- /dev/null +++ b/core/corerepo/stat.go @@ -0,0 +1,43 @@ +package corerepo + +import ( + "github.com/ipfs/go-ipfs/core" + fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo" + context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context" +) + +type Stat struct { + NumObjects uint64 + RepoSize uint64 // size in bytes + RepoPath string +} + +func RepoStat(n *core.IpfsNode, ctx context.Context) (*Stat, error) { + r := n.Repo + + usage, err := r.GetStorageUsage() + if err != nil { + return nil, err + } + + allKeys, err := n.Blockstore.AllKeysChan(ctx) + if err != nil { + return nil, err + } + + count := uint64(0) + for range allKeys { + count++ + } + + path, err := fsrepo.BestKnownPath() + if err != nil { + return nil, err + } + + return &Stat{ + NumObjects: count, + RepoSize: usage, + RepoPath: path, + }, nil +} diff --git a/test/sharness/t0080-repo.sh b/test/sharness/t0080-repo.sh index 01ef79b0ab3..cbe4e8dcf2d 100755 --- a/test/sharness/t0080-repo.sh +++ b/test/sharness/t0080-repo.sh @@ -219,6 +219,31 @@ test_expect_success "'ipfs refs --unique --recursive (bigger)'" ' test_sort_cmp expected actual || test_fsh cat refs_output ' +get_field_num() { + field=$1 + file=$2 + num=$(grep "$field" "$file" | awk '{ print $2 }') + echo $num +} + +test_expect_success "'ipfs repo stat' succeeds" ' + ipfs repo stat > repo-stats +' +test_expect_success "repo stats came out correct" ' + grep "RepoPath" repo-stats && + grep "RepoSize" repo-stats && + grep "NumObjects" repo-stats +' + +test_expect_success "'ipfs repo stat' after adding a file" ' + ipfs add repo-stats && + ipfs repo stat > repo-stats-2 +' + +test_expect_success "repo stats are updated correctly" ' + test $(get_field_num "RepoSize" repo-stats-2) -ge $(get_field_num "RepoSize" repo-stats) +' + test_kill_ipfs_daemon test_done