提交 856b1b42 作者: Kevin Atkinson 提交者: Steven Allen

Implement "repo rm-root" command to unlike the files API root.

Closes #3934.

License: MIT
Signed-off-by: 's avatarKevin Atkinson <k@kevina.org>
上级 71d7cf7b
...@@ -84,13 +84,14 @@ func (d *cmdDetails) usesRepo() bool { return !d.doesNotUseRepo } ...@@ -84,13 +84,14 @@ func (d *cmdDetails) usesRepo() bool { return !d.doesNotUseRepo }
// properties so that other code can make decisions about whether to invoke a // properties so that other code can make decisions about whether to invoke a
// command or return an error to the user. // command or return an error to the user.
var cmdDetailsMap = map[string]cmdDetails{ var cmdDetailsMap = map[string]cmdDetails{
"init": {doesNotUseConfigAsInput: true, cannotRunOnDaemon: true, doesNotUseRepo: true}, "init": {doesNotUseConfigAsInput: true, cannotRunOnDaemon: true, doesNotUseRepo: true},
"daemon": {doesNotUseConfigAsInput: true, cannotRunOnDaemon: true}, "daemon": {doesNotUseConfigAsInput: true, cannotRunOnDaemon: true},
"commands": {doesNotUseRepo: true}, "commands": {doesNotUseRepo: true},
"version": {doesNotUseConfigAsInput: true, doesNotUseRepo: true}, // must be permitted to run before init "version": {doesNotUseConfigAsInput: true, doesNotUseRepo: true}, // must be permitted to run before init
"log": {cannotRunOnClient: true}, "log": {cannotRunOnClient: true},
"diag/cmds": {cannotRunOnClient: true}, "diag/cmds": {cannotRunOnClient: true},
"repo/fsck": {cannotRunOnDaemon: true}, "repo/fsck": {cannotRunOnDaemon: true},
"config/edit": {cannotRunOnDaemon: true, doesNotUseRepo: true}, "repo/rm-root": {cannotRunOnDaemon: true},
"cid": {doesNotUseRepo: true}, "config/edit": {cannotRunOnDaemon: true, doesNotUseRepo: true},
"cid": {doesNotUseRepo: true},
} }
...@@ -188,6 +188,7 @@ func TestCommands(t *testing.T) { ...@@ -188,6 +188,7 @@ func TestCommands(t *testing.T) {
"/repo/stat", "/repo/stat",
"/repo/verify", "/repo/verify",
"/repo/version", "/repo/version",
"/repo/rm-root",
"/resolve", "/resolve",
"/shutdown", "/shutdown",
"/stats", "/stats",
......
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"sync" "sync"
"text/tabwriter" "text/tabwriter"
core "github.com/ipfs/go-ipfs/core"
cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv" cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
corerepo "github.com/ipfs/go-ipfs/core/corerepo" corerepo "github.com/ipfs/go-ipfs/core/corerepo"
fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo" fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
...@@ -19,6 +20,8 @@ import ( ...@@ -19,6 +20,8 @@ import (
cmds "gx/ipfs/QmQkW9fnCsg9SLHdViiAh6qfBppodsPZVpU92dZLqYtEfs/go-ipfs-cmds" cmds "gx/ipfs/QmQkW9fnCsg9SLHdViiAh6qfBppodsPZVpU92dZLqYtEfs/go-ipfs-cmds"
cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid"
config "gx/ipfs/QmUAuYuiafnJRZxDDX7MuruMNsicYNuyub5vUeAcupUBNs/go-ipfs-config" config "gx/ipfs/QmUAuYuiafnJRZxDDX7MuruMNsicYNuyub5vUeAcupUBNs/go-ipfs-config"
ds "gx/ipfs/QmUadX5EcvrBmxAV9sE7wUWtWSqxns5K84qKJBixmcT1w9/go-datastore"
b58 "gx/ipfs/QmWFAMPqsEyUX7gDUsRVmMWz59FxSpJ1b2v6bJ1yYzo7jY/go-base58-fast/base58"
bstore "gx/ipfs/QmXjKkjMDTtXAiLBwstVexofB8LeruZmE2eBd85GwGFFLA/go-ipfs-blockstore" bstore "gx/ipfs/QmXjKkjMDTtXAiLBwstVexofB8LeruZmE2eBd85GwGFFLA/go-ipfs-blockstore"
cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit" cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
) )
...@@ -41,6 +44,7 @@ var RepoCmd = &cmds.Command{ ...@@ -41,6 +44,7 @@ var RepoCmd = &cmds.Command{
"fsck": repoFsckCmd, "fsck": repoFsckCmd,
"version": repoVersionCmd, "version": repoVersionCmd,
"verify": repoVerifyCmd, "verify": repoVerifyCmd,
"rm-root": repoRmRootCmd,
}, },
} }
...@@ -262,6 +266,92 @@ daemons are running. ...@@ -262,6 +266,92 @@ daemons are running.
}, },
} }
var repoRmRootCmd = &cmds.Command{
Helptext: cmdkit.HelpText{
Tagline: "Unlink the root used by the files API.",
ShortDescription: `
'ipfs repo rm-root' will unlink the root used by the files API ('ipfs
files' commands) without trying to read the root itself. The root and
its children will be removed the next time the garbage collector runs,
unless pinned.
This command is designed to recover form the situation when the root
becomes unavailable and recovering it (such as recreating it, or
fetching it from the network) is not possible. This command should
only be used as a last resort as using this command could lead to data
loss if there are unpinned nodes connected to the root.
This command can only run when the ipfs daemon is not running.
`,
},
Options: []cmdkit.Option{
cmdkit.BoolOption("confirm", "Really perform operation."),
cmdkit.BoolOption("remove-local-root", "Remove even if the root exists locally."),
},
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
confirm, _ := req.Options["confirm"].(bool)
removeLocalRoot, _ := req.Options["remove-local-root"].(bool)
if !confirm {
return fmt.Errorf("this is a potentially dangerous operation please pass --confirm to proceed")
}
configRoot, err := cmdenv.GetConfigRoot(env)
if err != nil {
return err
}
// Can't use a full node as that interferes with the removal
// of the files root, so open the repo directly
repo, err := fsrepo.Open(configRoot)
if err != nil {
return err
}
defer repo.Close()
bs := bstore.NewBlockstore(repo.Datastore())
// Get the old root and display it to the user so that they can
// can do something to prevent from being garbage collected,
// such as pin it
dsk := core.FilesRootKey()
val, err := repo.Datastore().Get(dsk)
if err == ds.ErrNotFound || val == nil {
return cmds.EmitOnce(res, &MessageOutput{"Files API root not found.\n"})
}
var cidStr string
var have bool
c, err := cid.Cast(val)
if err == nil {
cidStr = c.String()
have, _ = bs.Has(c)
} else {
cidStr = b58.Encode(val)
}
if have && !removeLocalRoot {
return fmt.Errorf("root %s exists locally. Are you sure you want to unlink this? Pass --remove-local-root to continue", cidStr)
} else if !have && removeLocalRoot {
return fmt.Errorf("root does not %s exists locally. Please remove --remove-local-root to continue", cidStr)
}
err = repo.Datastore().Delete(dsk)
if err != nil {
return fmt.Errorf("unable to remove API root: %s. Root hash was %s", err.Error(), cidStr)
}
return cmds.EmitOnce(res, &MessageOutput{
fmt.Sprintf("Unlinked files API root. Root hash was %s.\n", cidStr),
})
},
Type: MessageOutput{},
Encoders: cmds.EncoderMap{
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, msg *MessageOutput) error {
_, err := fmt.Fprintf(w, "%s", msg.Message)
return err
}),
},
}
type VerifyProgress struct { type VerifyProgress struct {
Msg string Msg string
Progress int Progress int
......
...@@ -855,8 +855,11 @@ func (n *IpfsNode) loadBootstrapPeers() ([]pstore.PeerInfo, error) { ...@@ -855,8 +855,11 @@ func (n *IpfsNode) loadBootstrapPeers() ([]pstore.PeerInfo, error) {
return toPeerInfos(parsed), nil return toPeerInfos(parsed), nil
} }
// FilesRootKey returns the datastore key for the files root
func FilesRootKey() ds.Key { return ds.NewKey("/local/filesroot") }
func (n *IpfsNode) loadFilesRoot() error { func (n *IpfsNode) loadFilesRoot() error {
dsk := ds.NewKey("/local/filesroot") dsk := FilesRootKey()
pf := func(ctx context.Context, c cid.Cid) error { pf := func(ctx context.Context, c cid.Cid) error {
return n.Repo.Datastore().Put(dsk, c.Bytes()) return n.Repo.Datastore().Put(dsk, c.Bytes())
} }
......
#!/usr/bin/env bash
#
# Copyright (c) 2016 Jeromy Johnson
# MIT Licensed; see the LICENSE file in this repository.
#
test_description="Test ipfs repo fsck"
. lib/test-lib.sh
test_init_ipfs
ROOT_HASH=QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn
test_expect_success "ipfs repo rm-root fails without --confirm" '
test_must_fail ipfs repo rm-root 2> err &&
cat err &&
fgrep -q "please pass --confirm to proceed" err
'
test_expect_success "ipfs repo rm-root fails to remove existing root without --remove-local-root" '
test_must_fail ipfs repo rm-root --confirm 2> err &&
cat err &&
fgrep -q "Are you sure you want to unlink this?" err
'
test_expect_success "ipfs repo rm-root" '
ipfs repo rm-root --confirm --remove-local-root | tee rm-root.actual &&
echo "Unlinked files API root. Root hash was $ROOT_HASH." > rm-root.expected &&
test_cmp rm-root.expected rm-root.actual
'
test_expect_success "files api root really removed" '
ipfs repo rm-root --confirm | tee rm-root-post.actual &&
echo "Files API root not found." > rm-root-post.expected &&
test_cmp rm-root-post.expected rm-root-post.actual
'
test_launch_ipfs_daemon
test_expect_success "ipfs repo rm-root does not run on daemon" '
test_must_fail ipfs repo rm-root --confirm 2> err &&
cat err &&
fgrep -q "ipfs daemon is running" err
'
test_kill_ipfs_daemon
test_done
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论