From 856b1b42e167bec59e39a59ce88c9d3720a6ee0c Mon Sep 17 00:00:00 2001 From: Kevin Atkinson Date: Wed, 30 May 2018 15:10:22 -0400 Subject: [PATCH 1/4] Implement "repo rm-root" command to unlike the files API root. Closes #3934. License: MIT Signed-off-by: Kevin Atkinson --- cmd/ipfs/ipfs.go | 19 +++--- core/commands/commands_test.go | 1 + core/commands/repo.go | 90 +++++++++++++++++++++++++++++ core/core.go | 5 +- test/sharness/t0089-repo-rm-root.sh | 49 ++++++++++++++++ 5 files changed, 154 insertions(+), 10 deletions(-) create mode 100755 test/sharness/t0089-repo-rm-root.sh diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index 9fc7b1e238d..34010b010ab 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -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 // command or return an error to the user. var cmdDetailsMap = map[string]cmdDetails{ - "init": {doesNotUseConfigAsInput: true, cannotRunOnDaemon: true, doesNotUseRepo: true}, - "daemon": {doesNotUseConfigAsInput: true, cannotRunOnDaemon: true}, - "commands": {doesNotUseRepo: true}, - "version": {doesNotUseConfigAsInput: true, doesNotUseRepo: true}, // must be permitted to run before init - "log": {cannotRunOnClient: true}, - "diag/cmds": {cannotRunOnClient: true}, - "repo/fsck": {cannotRunOnDaemon: true}, - "config/edit": {cannotRunOnDaemon: true, doesNotUseRepo: true}, - "cid": {doesNotUseRepo: true}, + "init": {doesNotUseConfigAsInput: true, cannotRunOnDaemon: true, doesNotUseRepo: true}, + "daemon": {doesNotUseConfigAsInput: true, cannotRunOnDaemon: true}, + "commands": {doesNotUseRepo: true}, + "version": {doesNotUseConfigAsInput: true, doesNotUseRepo: true}, // must be permitted to run before init + "log": {cannotRunOnClient: true}, + "diag/cmds": {cannotRunOnClient: true}, + "repo/fsck": {cannotRunOnDaemon: true}, + "repo/rm-root": {cannotRunOnDaemon: true}, + "config/edit": {cannotRunOnDaemon: true, doesNotUseRepo: true}, + "cid": {doesNotUseRepo: true}, } diff --git a/core/commands/commands_test.go b/core/commands/commands_test.go index a34de3dfc4f..23de2ba5225 100644 --- a/core/commands/commands_test.go +++ b/core/commands/commands_test.go @@ -188,6 +188,7 @@ func TestCommands(t *testing.T) { "/repo/stat", "/repo/verify", "/repo/version", + "/repo/rm-root", "/resolve", "/shutdown", "/stats", diff --git a/core/commands/repo.go b/core/commands/repo.go index 5f12ec516c6..6e2edca8c60 100644 --- a/core/commands/repo.go +++ b/core/commands/repo.go @@ -12,6 +12,7 @@ import ( "sync" "text/tabwriter" + core "github.com/ipfs/go-ipfs/core" cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv" corerepo "github.com/ipfs/go-ipfs/core/corerepo" fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo" @@ -19,6 +20,8 @@ import ( cmds "gx/ipfs/QmQkW9fnCsg9SLHdViiAh6qfBppodsPZVpU92dZLqYtEfs/go-ipfs-cmds" cid "gx/ipfs/QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN/go-cid" 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" cmdkit "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit" ) @@ -41,6 +44,7 @@ var RepoCmd = &cmds.Command{ "fsck": repoFsckCmd, "version": repoVersionCmd, "verify": repoVerifyCmd, + "rm-root": repoRmRootCmd, }, } @@ -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 { Msg string Progress int diff --git a/core/core.go b/core/core.go index c5fbfe4c1c2..6db1c703b1f 100644 --- a/core/core.go +++ b/core/core.go @@ -855,8 +855,11 @@ func (n *IpfsNode) loadBootstrapPeers() ([]pstore.PeerInfo, error) { 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 { - dsk := ds.NewKey("/local/filesroot") + dsk := FilesRootKey() pf := func(ctx context.Context, c cid.Cid) error { return n.Repo.Datastore().Put(dsk, c.Bytes()) } diff --git a/test/sharness/t0089-repo-rm-root.sh b/test/sharness/t0089-repo-rm-root.sh new file mode 100755 index 00000000000..eee17e44153 --- /dev/null +++ b/test/sharness/t0089-repo-rm-root.sh @@ -0,0 +1,49 @@ +#!/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 From 03e79f472b479fb88162185c3b5f6a0fc6d1f302 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 1 Mar 2019 16:43:31 -0800 Subject: [PATCH 2/4] cmds(repo rm-root): use a variable for FilesRootKey License: MIT Signed-off-by: Steven Allen --- core/commands/repo.go | 5 ++--- core/core.go | 11 +++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/core/commands/repo.go b/core/commands/repo.go index 6e2edca8c60..de5bd0a9ff2 100644 --- a/core/commands/repo.go +++ b/core/commands/repo.go @@ -313,8 +313,7 @@ This command can only run when the ipfs daemon is not running. // 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) + val, err := repo.Datastore().Get(core.FilesRootKey) if err == ds.ErrNotFound || val == nil { return cmds.EmitOnce(res, &MessageOutput{"Files API root not found.\n"}) } @@ -335,7 +334,7 @@ This command can only run when the ipfs daemon is not running. return fmt.Errorf("root does not %s exists locally. Please remove --remove-local-root to continue", cidStr) } - err = repo.Datastore().Delete(dsk) + err = repo.Datastore().Delete(core.FilesRootKey) if err != nil { return fmt.Errorf("unable to remove API root: %s. Root hash was %s", err.Error(), cidStr) } diff --git a/core/core.go b/core/core.go index 6db1c703b1f..a045ecbbed3 100644 --- a/core/core.go +++ b/core/core.go @@ -80,6 +80,9 @@ import ( const IpnsValidatorTag = "ipns" +// FilesRootKey is the datastore key for the files root. +var FilesRootKey ds.Key = ds.NewKey("/local/filesroot") + const kReprovideFrequency = time.Hour * 12 const discoveryConnTimeout = time.Second * 30 const DefaultIpnsCacheSize = 128 @@ -855,17 +858,13 @@ func (n *IpfsNode) loadBootstrapPeers() ([]pstore.PeerInfo, error) { 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 { - dsk := FilesRootKey() pf := func(ctx context.Context, c cid.Cid) error { - return n.Repo.Datastore().Put(dsk, c.Bytes()) + return n.Repo.Datastore().Put(FilesRootKey, c.Bytes()) } var nd *merkledag.ProtoNode - val, err := n.Repo.Datastore().Get(dsk) + val, err := n.Repo.Datastore().Get(FilesRootKey) switch { case err == ds.ErrNotFound || val == nil: From dc303d06d66c188348a819244bd32f4746cfcb19 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Fri, 1 Mar 2019 17:23:34 -0800 Subject: [PATCH 3/4] cmds(repo rm-root): bring --remove-existing-root in-line with it's documentation This command is already annoying enough. If a user decides to automate this, that's their issue (and we should allow it). (also rename back to remove-existing-root from remove-local-root) License: MIT Signed-off-by: Steven Allen --- core/commands/repo.go | 10 ++++------ test/sharness/t0089-repo-rm-root.sh | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/core/commands/repo.go b/core/commands/repo.go index de5bd0a9ff2..8ef3b2719ef 100644 --- a/core/commands/repo.go +++ b/core/commands/repo.go @@ -286,11 +286,11 @@ 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."), + cmdkit.BoolOption("remove-existing-root", "Remove even if it 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) + removeExistingRoot, _ := req.Options["remove-existing-root"].(bool) if !confirm { return fmt.Errorf("this is a potentially dangerous operation please pass --confirm to proceed") @@ -328,10 +328,8 @@ This command can only run when the ipfs daemon is not running. 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) + if have && !removeExistingRoot { + return fmt.Errorf("root %s exists locally. Are you sure you want to unlink this? Pass --remove-existing-root to continue", cidStr) } err = repo.Datastore().Delete(core.FilesRootKey) diff --git a/test/sharness/t0089-repo-rm-root.sh b/test/sharness/t0089-repo-rm-root.sh index eee17e44153..0dbd4c2a0c5 100755 --- a/test/sharness/t0089-repo-rm-root.sh +++ b/test/sharness/t0089-repo-rm-root.sh @@ -18,14 +18,14 @@ test_expect_success "ipfs repo rm-root fails without --confirm" ' 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_expect_success "ipfs repo rm-root fails to remove existing root without --remove-existing-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 && + ipfs repo rm-root --confirm --remove-existing-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 ' From 784e0d8e6dc886b44f93a409b37078809d9cb328 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Mon, 4 Mar 2019 13:13:23 -0800 Subject: [PATCH 4/4] cmds(ipfs rm-files-root): improve language around rm-root command License: MIT Signed-off-by: Steven Allen --- cmd/ipfs/ipfs.go | 20 ++++----- core/commands/commands_test.go | 2 +- core/commands/repo.go | 24 +++++------ test/sharness/t0089-repo-rm-files-root.sh | 49 +++++++++++++++++++++++ test/sharness/t0089-repo-rm-root.sh | 49 ----------------------- 5 files changed, 72 insertions(+), 72 deletions(-) create mode 100755 test/sharness/t0089-repo-rm-files-root.sh delete mode 100755 test/sharness/t0089-repo-rm-root.sh diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index 34010b010ab..0e94627f961 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -84,14 +84,14 @@ func (d *cmdDetails) usesRepo() bool { return !d.doesNotUseRepo } // properties so that other code can make decisions about whether to invoke a // command or return an error to the user. var cmdDetailsMap = map[string]cmdDetails{ - "init": {doesNotUseConfigAsInput: true, cannotRunOnDaemon: true, doesNotUseRepo: true}, - "daemon": {doesNotUseConfigAsInput: true, cannotRunOnDaemon: true}, - "commands": {doesNotUseRepo: true}, - "version": {doesNotUseConfigAsInput: true, doesNotUseRepo: true}, // must be permitted to run before init - "log": {cannotRunOnClient: true}, - "diag/cmds": {cannotRunOnClient: true}, - "repo/fsck": {cannotRunOnDaemon: true}, - "repo/rm-root": {cannotRunOnDaemon: true}, - "config/edit": {cannotRunOnDaemon: true, doesNotUseRepo: true}, - "cid": {doesNotUseRepo: true}, + "init": {doesNotUseConfigAsInput: true, cannotRunOnDaemon: true, doesNotUseRepo: true}, + "daemon": {doesNotUseConfigAsInput: true, cannotRunOnDaemon: true}, + "commands": {doesNotUseRepo: true}, + "version": {doesNotUseConfigAsInput: true, doesNotUseRepo: true}, // must be permitted to run before init + "log": {cannotRunOnClient: true}, + "diag/cmds": {cannotRunOnClient: true}, + "repo/fsck": {cannotRunOnDaemon: true}, + "repo/rm-files-root": {cannotRunOnDaemon: true}, + "config/edit": {cannotRunOnDaemon: true, doesNotUseRepo: true}, + "cid": {doesNotUseRepo: true}, } diff --git a/core/commands/commands_test.go b/core/commands/commands_test.go index 23de2ba5225..b15dc7677ef 100644 --- a/core/commands/commands_test.go +++ b/core/commands/commands_test.go @@ -188,7 +188,7 @@ func TestCommands(t *testing.T) { "/repo/stat", "/repo/verify", "/repo/version", - "/repo/rm-root", + "/repo/rm-files-root", "/resolve", "/shutdown", "/stats", diff --git a/core/commands/repo.go b/core/commands/repo.go index 8ef3b2719ef..da8f8a7124e 100644 --- a/core/commands/repo.go +++ b/core/commands/repo.go @@ -39,12 +39,12 @@ var RepoCmd = &cmds.Command{ }, Subcommands: map[string]*cmds.Command{ - "stat": repoStatCmd, - "gc": repoGcCmd, - "fsck": repoFsckCmd, - "version": repoVersionCmd, - "verify": repoVerifyCmd, - "rm-root": repoRmRootCmd, + "stat": repoStatCmd, + "gc": repoGcCmd, + "fsck": repoFsckCmd, + "version": repoVersionCmd, + "verify": repoVerifyCmd, + "rm-files-root": repoRmFilesRootCmd, }, } @@ -266,11 +266,11 @@ daemons are running. }, } -var repoRmRootCmd = &cmds.Command{ +var repoRmFilesRootCmd = &cmds.Command{ Helptext: cmdkit.HelpText{ - Tagline: "Unlink the root used by the files API.", + Tagline: "Unlink the root used by the `ipfs files` comands.", ShortDescription: ` -'ipfs repo rm-root' will unlink the root used by the files API ('ipfs +'ipfs repo rm-files-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. @@ -315,7 +315,7 @@ This command can only run when the ipfs daemon is not running. // such as pin it val, err := repo.Datastore().Get(core.FilesRootKey) if err == ds.ErrNotFound || val == nil { - return cmds.EmitOnce(res, &MessageOutput{"Files API root not found.\n"}) + return cmds.EmitOnce(res, &MessageOutput{"`ipfs files` root not found.\n"}) } var cidStr string @@ -329,12 +329,12 @@ This command can only run when the ipfs daemon is not running. } if have && !removeExistingRoot { - return fmt.Errorf("root %s exists locally. Are you sure you want to unlink this? Pass --remove-existing-root to continue", cidStr) + return fmt.Errorf("`ipfs files` root %s exists locally. Are you sure you want to unlink this? Pass --remove-existing-root to continue", cidStr) } err = repo.Datastore().Delete(core.FilesRootKey) if err != nil { - return fmt.Errorf("unable to remove API root: %s. Root hash was %s", err.Error(), cidStr) + return fmt.Errorf("unable to remove `ipfs files` 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), diff --git a/test/sharness/t0089-repo-rm-files-root.sh b/test/sharness/t0089-repo-rm-files-root.sh new file mode 100755 index 00000000000..ba7b8f19116 --- /dev/null +++ b/test/sharness/t0089-repo-rm-files-root.sh @@ -0,0 +1,49 @@ +#!/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-files-root fails without --confirm" ' + test_must_fail ipfs repo rm-files-root 2> err && + cat err && + fgrep -q "please pass --confirm to proceed" err +' + +test_expect_success "ipfs repo rm-files-root fails to remove existing root without --remove-existing-root" ' + test_must_fail ipfs repo rm-files-root --confirm 2> err && + cat err && + fgrep -q "Are you sure you want to unlink this?" err +' + +test_expect_success "ipfs repo rm-files-root" ' + ipfs repo rm-files-root --confirm --remove-existing-root | tee rm-files-root.actual && + echo "Unlinked files API root. Root hash was $ROOT_HASH." > rm-files-root.expected && + test_cmp rm-files-root.expected rm-files-root.actual +' + +test_expect_success "files api root really removed" ' + ipfs repo rm-files-root --confirm | tee rm-files-root-post.actual && + echo "Files API root not found." > rm-files-root-post.expected && + test_cmp rm-files-root-post.expected rm-files-root-post.actual +' + +test_launch_ipfs_daemon + +test_expect_success "ipfs repo rm-files-root does not run on daemon" ' + test_must_fail ipfs repo rm-files-root --confirm 2> err && + cat err && + fgrep -q "ipfs daemon is running" err +' + +test_kill_ipfs_daemon + +test_done diff --git a/test/sharness/t0089-repo-rm-root.sh b/test/sharness/t0089-repo-rm-root.sh deleted file mode 100755 index 0dbd4c2a0c5..00000000000 --- a/test/sharness/t0089-repo-rm-root.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/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-existing-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-existing-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