mirror of
https://github.com/ipfs/kubo.git
synced 2025-05-21 00:47:22 +08:00

* Adds repo fsck subcommand Fixes #2457 License: MIT Signed-off-by: Mike Pfister <pfista@gmail.com> * Checks for error on file deletion License: MIT Signed-off-by: Mike Pfister <pfista@gmail.com> * Checks if node is online License: MIT Signed-off-by: Mike Pfister <pfista@gmail.com> * Update error checking License: MIT Signed-off-by: Michael Pfister <pfista@gmail.com> * Prevents command from running while daemon is running License: MIT Signed-off-by: Michael Pfister <pfista@gmail.com> * Add newline to command output message License: MIT Signed-off-by: Michael Pfister <pfista@gmail.com> * removing superfluous error License: MIT Signed-off-by: Michael Pfister <pfista@gmail.com> * Adds sharness test for repo fsck command License: MIT Signed-off-by: Michael Pfister <pfista@gmail.com> * Ignore warning if file doesn't exist License: MIT Signed-off-by: Michael Pfister <pfista@gmail.com> * Updating message output License: MIT Signed-off-by: Michael Pfister <pfista@gmail.com> * adding debug statements License: MIT Signed-off-by: Michael Pfister <pfista@gmail.com> * update and add fsck sharness tests License: MIT Signed-off-by: Michael Pfister <pfista@gmail.com> * updating comments License: MIT Signed-off-by: Michael Pfister <pfista@gmail.com> * Use printf in test Using printf prevents a newline from being printed to the api test file. When the newline was present, multiaddr threw errors trying to parse the api address to an integer since the newline character was present. License: MIT Signed-off-by: Michael Pfister <pfista@gmail.com> * updating tests License: MIT Signed-off-by: Michael Pfister <pfista@gmail.com> * removing commented code License: MIT Signed-off-by: Michael Pfister <pfista@gmail.com>
211 lines
5.2 KiB
Go
211 lines
5.2 KiB
Go
package commands
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
cmds "github.com/ipfs/go-ipfs/commands"
|
|
corerepo "github.com/ipfs/go-ipfs/core/corerepo"
|
|
config "github.com/ipfs/go-ipfs/repo/config"
|
|
lockfile "github.com/ipfs/go-ipfs/repo/fsrepo/lock"
|
|
u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
var RepoCmd = &cmds.Command{
|
|
Helptext: cmds.HelpText{
|
|
Tagline: "Manipulate the IPFS repo.",
|
|
ShortDescription: `
|
|
'ipfs repo' is a plumbing command used to manipulate the repo.
|
|
`,
|
|
},
|
|
|
|
Subcommands: map[string]*cmds.Command{
|
|
"gc": repoGcCmd,
|
|
"stat": repoStatCmd,
|
|
"fsck": RepoFsckCmd,
|
|
},
|
|
}
|
|
|
|
var repoGcCmd = &cmds.Command{
|
|
Helptext: cmds.HelpText{
|
|
Tagline: "Perform a garbage collection sweep on the repo.",
|
|
ShortDescription: `
|
|
'ipfs repo gc' is a plumbing command that will sweep the local
|
|
set of stored objects and remove ones that are not pinned in
|
|
order to reclaim hard disk space.
|
|
`,
|
|
},
|
|
Options: []cmds.Option{
|
|
cmds.BoolOption("quiet", "q", "Write minimal output."),
|
|
},
|
|
Run: func(req cmds.Request, res cmds.Response) {
|
|
n, err := req.InvocContext().GetNode()
|
|
if err != nil {
|
|
res.SetError(err, cmds.ErrNormal)
|
|
return
|
|
}
|
|
|
|
gcOutChan, err := corerepo.GarbageCollectAsync(n, req.Context())
|
|
if err != nil {
|
|
res.SetError(err, cmds.ErrNormal)
|
|
return
|
|
}
|
|
|
|
outChan := make(chan interface{})
|
|
res.SetOutput((<-chan interface{})(outChan))
|
|
|
|
go func() {
|
|
defer close(outChan)
|
|
for k := range gcOutChan {
|
|
outChan <- k
|
|
}
|
|
}()
|
|
},
|
|
Type: corerepo.KeyRemoved{},
|
|
Marshalers: cmds.MarshalerMap{
|
|
cmds.Text: func(res cmds.Response) (io.Reader, error) {
|
|
outChan, ok := res.Output().(<-chan interface{})
|
|
if !ok {
|
|
return nil, u.ErrCast()
|
|
}
|
|
|
|
quiet, _, err := res.Request().Option("quiet").Bool()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
marshal := func(v interface{}) (io.Reader, error) {
|
|
obj, ok := v.(*corerepo.KeyRemoved)
|
|
if !ok {
|
|
return nil, u.ErrCast()
|
|
}
|
|
|
|
buf := new(bytes.Buffer)
|
|
if quiet {
|
|
buf = bytes.NewBufferString(string(obj.Key) + "\n")
|
|
} else {
|
|
buf = bytes.NewBufferString(fmt.Sprintf("removed %s\n", obj.Key))
|
|
}
|
|
return buf, nil
|
|
}
|
|
|
|
return &cmds.ChannelMarshaler{
|
|
Channel: outChan,
|
|
Marshaler: marshal,
|
|
Res: res,
|
|
}, nil
|
|
},
|
|
},
|
|
}
|
|
|
|
var repoStatCmd = &cmds.Command{
|
|
Helptext: cmds.HelpText{
|
|
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.
|
|
RepoPath string The path to the repo being currently used.
|
|
RepoSize int Size in bytes that the repo is currently taking.
|
|
`,
|
|
},
|
|
Run: func(req cmds.Request, res cmds.Response) {
|
|
n, err := req.InvocContext().GetNode()
|
|
if err != nil {
|
|
res.SetError(err, cmds.ErrNormal)
|
|
return
|
|
}
|
|
|
|
stat, err := corerepo.RepoStat(n, req.Context())
|
|
if err != nil {
|
|
res.SetError(err, cmds.ErrNormal)
|
|
return
|
|
}
|
|
|
|
res.SetOutput(stat)
|
|
},
|
|
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().(*corerepo.Stat)
|
|
if !ok {
|
|
return nil, u.ErrCast()
|
|
}
|
|
|
|
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 buf, nil
|
|
},
|
|
},
|
|
}
|
|
|
|
var RepoFsckCmd = &cmds.Command{
|
|
Helptext: cmds.HelpText{
|
|
Tagline: "Removes repo lockfiles",
|
|
ShortDescription: `
|
|
'ipfs repo fsck' is a plumbing command that will remove repo and level db
|
|
lockfiles, as well as the api file. This command can only run when no ipfs
|
|
daemons are running.
|
|
`,
|
|
},
|
|
Run: func(req cmds.Request, res cmds.Response) {
|
|
configRoot := req.InvocContext().ConfigRoot
|
|
|
|
dsPath, err := config.DataStorePath(configRoot)
|
|
if err != nil {
|
|
res.SetError(err, cmds.ErrNormal)
|
|
return
|
|
}
|
|
|
|
dsLockFile := filepath.Join(dsPath, "LOCK") // TODO: get this lockfile programmatically
|
|
repoLockFile := filepath.Join(configRoot, lockfile.LockFile)
|
|
apiFile := filepath.Join(configRoot, "api") // TODO: get this programmatically
|
|
|
|
log.Infof("Removing repo lockfile: %s", repoLockFile)
|
|
log.Infof("Removing datastore lockfile: %s", dsLockFile)
|
|
log.Infof("Removing api file: %s", apiFile)
|
|
|
|
err = os.Remove(repoLockFile)
|
|
if err != nil && !os.IsNotExist(err) {
|
|
res.SetError(err, cmds.ErrNormal)
|
|
return
|
|
}
|
|
err = os.Remove(dsLockFile)
|
|
if err != nil && !os.IsNotExist(err) {
|
|
res.SetError(err, cmds.ErrNormal)
|
|
return
|
|
}
|
|
err = os.Remove(apiFile)
|
|
if err != nil && !os.IsNotExist(err) {
|
|
res.SetError(err, cmds.ErrNormal)
|
|
return
|
|
}
|
|
|
|
s := "Lockfiles have been removed."
|
|
log.Info(s)
|
|
res.SetOutput(&MessageOutput{s + "\n"})
|
|
},
|
|
Type: MessageOutput{},
|
|
Marshalers: cmds.MarshalerMap{
|
|
cmds.Text: MessageTextMarshaler,
|
|
},
|
|
}
|