@cryptotaxi247 / kubo / commits / 46bcdce15

commands: repo fsck (#2597)

* 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>

michael committed Apr 27, 2016 at 13:28 UTC 46bcdce15d73d20b2f3a2def2136ae6b62c120c0
7 files changed +268 -5
cmd/ipfs/init.go
+1
@@ -51,6 +51,7 @@ at ~/.ipfs. To change the repo location, set the $IPFS_PATH environment variable
51
52 log.Info("checking if daemon is running...")
53 if daemonLocked {
54 + log.Debug("Ipfs daemon is running.")
55 e := "ipfs daemon is running. please stop it to run this command"
56 return cmds.ClientError(e)
57 }
cmd/ipfs/ipfs.go
+1
@@ -105,4 +105,5 @@ var cmdDetailsMap = map[*cmds.Command]cmdDetails{
105 commands.CommandsDaemonCmd: {doesNotUseRepo: true},
106 commands.VersionCmd: {doesNotUseConfigAsInput: true, doesNotUseRepo: true}, // must be permitted to run before init
107 commands.LogCmd: {cannotRunOnClient: true},
108 + commands.RepoFsckCmd: {cannotRunOnDaemon: true},
109 }
cmd/ipfs/main.go
+2 -1
@@ -417,9 +417,10 @@ func commandShouldRunOnDaemon(details cmdDetails, req cmds.Request, root *cmds.C
417 return nil, err
418 }
419
420 - if client != nil { // api file exists
420 + if client != nil {
421 if details.cannotRunOnDaemon {
422 // check if daemon locked. legacy error text, for now.
423 + log.Debugf("Command cannot run on daemon. Checking if daemon is locked")
424 if daemonLocked, _ := fsrepo.LockedByOtherProcess(req.InvocContext().ConfigRoot); daemonLocked {
425 return nil, cmds.ClientError("ipfs daemon is running. please stop it to run this command")
426 }
core/commands/repo.go
+57 -1
@@ -5,8 +5,12 @@ import (
5 "fmt"
6 cmds "github.com/ipfs/go-ipfs/commands"
7 corerepo "github.com/ipfs/go-ipfs/core/corerepo"
8 + config "github.com/ipfs/go-ipfs/repo/config"
9 + lockfile "github.com/ipfs/go-ipfs/repo/fsrepo/lock"
10 u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
11 "io"
12 + "os"
13 + "path/filepath"
14 )
15
16 var RepoCmd = &cmds.Command{
@@ -20,6 +24,7 @@ var RepoCmd = &cmds.Command{
24 Subcommands: map[string]*cmds.Command{
25 "gc": repoGcCmd,
26 "stat": repoStatCmd,
27 + "fsck": RepoFsckCmd,
28 },
29 }
30
@@ -32,7 +37,6 @@ set of stored objects and remove ones that are not pinned in
37 order to reclaim hard disk space.
38 `,
39 },
35 -
40 Options: []cmds.Option{
41 cmds.BoolOption("quiet", "q", "Write minimal output."),
42 },
@@ -152,3 +156,55 @@ RepoSize int Size in bytes that the repo is currently taking.
156 },
157 },
158 }
159 +
160 +var RepoFsckCmd = &cmds.Command{
161 + Helptext: cmds.HelpText{
162 + Tagline: "Removes repo lockfiles",
163 + ShortDescription: `
164 +'ipfs repo fsck' is a plumbing command that will remove repo and level db
165 +lockfiles, as well as the api file. This command can only run when no ipfs
166 +daemons are running.
167 +`,
168 + },
169 + Run: func(req cmds.Request, res cmds.Response) {
170 + configRoot := req.InvocContext().ConfigRoot
171 +
172 + dsPath, err := config.DataStorePath(configRoot)
173 + if err != nil {
174 + res.SetError(err, cmds.ErrNormal)
175 + return
176 + }
177 +
178 + dsLockFile := filepath.Join(dsPath, "LOCK") // TODO: get this lockfile programmatically
179 + repoLockFile := filepath.Join(configRoot, lockfile.LockFile)
180 + apiFile := filepath.Join(configRoot, "api") // TODO: get this programmatically
181 +
182 + log.Infof("Removing repo lockfile: %s", repoLockFile)
183 + log.Infof("Removing datastore lockfile: %s", dsLockFile)
184 + log.Infof("Removing api file: %s", apiFile)
185 +
186 + err = os.Remove(repoLockFile)
187 + if err != nil && !os.IsNotExist(err) {
188 + res.SetError(err, cmds.ErrNormal)
189 + return
190 + }
191 + err = os.Remove(dsLockFile)
192 + if err != nil && !os.IsNotExist(err) {
193 + res.SetError(err, cmds.ErrNormal)
194 + return
195 + }
196 + err = os.Remove(apiFile)
197 + if err != nil && !os.IsNotExist(err) {
198 + res.SetError(err, cmds.ErrNormal)
199 + return
200 + }
201 +
202 + s := "Lockfiles have been removed."
203 + log.Info(s)
204 + res.SetOutput(&MessageOutput{s + "\n"})
205 + },
206 + Type: MessageOutput{},
207 + Marshalers: cmds.MarshalerMap{
208 + cmds.Text: MessageTextMarshaler,
209 + },
210 +}
repo/fsrepo/fsrepo.go
+6 -3
@@ -259,8 +259,11 @@ func Remove(repoPath string) error {
259 // process. If true, then the repo cannot be opened by this process.
260 func LockedByOtherProcess(repoPath string) (bool, error) {
261 repoPath = filepath.Clean(repoPath)
262 - // NB: the lock is only held when repos are Open
263 - return lockfile.Locked(repoPath)
262 + locked, err := lockfile.Locked(repoPath)
263 + if locked {
264 + log.Debugf("(%t)<->Lock is held at %s", locked, repoPath)
265 + }
266 + return locked, err
267 }
268
269 // APIAddr returns the registered API addr, according to the api file
@@ -360,7 +363,7 @@ func (r *FSRepo) Close() error {
363 }
364
365 err := os.Remove(filepath.Join(r.path, apiFile))
363 - if err != nil {
366 + if err != nil && !os.IsNotExist(err) {
367 log.Warning("error removing api file: ", err)
368 }
369
repo/fsrepo/lock/lock.go
+11
@@ -10,12 +10,16 @@ import (
10
11 lock "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/camlistore/lock"
12 "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
13 + logging "gx/ipfs/Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH/go-log"
14 )
15
16 // LockFile is the filename of the repo lock, relative to config dir
17 // TODO rename repo lock and hide name
18 const LockFile = "repo.lock"
19
20 +// log is the fsrepo logger
21 +var log = logging.Logger("lock")
22 +
23 func errPerm(path string) error {
24 return fmt.Errorf("failed to take lock at %s: permission denied", path)
25 }
@@ -26,29 +30,36 @@ func Lock(confdir string) (io.Closer, error) {
30 }
31
32 func Locked(confdir string) (bool, error) {
33 + log.Debugf("Checking lock")
34 if !util.FileExists(path.Join(confdir, LockFile)) {
35 + log.Debugf("File doesn't exist: %s", path.Join(confdir, LockFile))
36 return false, nil
37 }
38 if lk, err := Lock(confdir); err != nil {
39 // EAGAIN == someone else has the lock
40 if err == syscall.EAGAIN {
41 + log.Debugf("Someone else has the lock: %s", path.Join(confdir, LockFile))
42 return true, nil
43 }
44 if strings.Contains(err.Error(), "can't Lock file") {
45 + log.Debugf("Can't lock file: %s.\n reason: %s", path.Join(confdir, LockFile), err.Error())
46 return true, nil
47 }
48
49 // lock fails on permissions error
50 if os.IsPermission(err) {
51 + log.Debugf("Lock fails on permissions error")
52 return false, errPerm(confdir)
53 }
54 if isLockCreatePermFail(err) {
55 + log.Debugf("Lock fails on permissions error")
56 return false, errPerm(confdir)
57 }
58
59 // otherwise, we cant guarantee anything, error out
60 return false, err
61 } else {
62 + log.Debugf("No one has a lock")
63 lk.Close()
64 return false, nil
65 }
test/sharness/t0083-repo-fsck.sh new
+190
@@ -0,0 +1,190 @@
1 +#!/bin/sh
2 +#
3 +# Copyright (c) 2016 Mike Pfister
4 +# MIT Licensed; see the LICENSE file in this repository.
5 +#
6 +
7 +test_description="Test ipfs repo fsck operations"
8 +
9 +. lib/test-lib.sh
10 +
11 +test_init_ipfs
12 +
13 +#############################
14 +# Test without daemon running
15 +#############################
16 +# Note: if api file isn't present we can assume the daemon isn't running
17 +
18 +# Try with all lock files present: repo.lock, api, and datastore/LOCK with
19 +# repo.lock and datastore/LOCK being empty
20 +test_expect_success "'ipfs repo fsck' succeeds with no daemon running empty
21 +repo.lock" '
22 + mkdir -p $IPFS_PATH &&
23 + mkdir -p $IPFS_PATH/datastore &&
24 + touch $IPFS_PATH/datastore/LOCK &&
25 + touch $IPFS_PATH/repo.lock &&
26 + printf "/ip4/127.0.0.1/tcp/5001" > $IPFS_PATH/api &&
27 + ipfs repo fsck > fsck_out_actual1
28 +'
29 +test_expect_success "'ipfs repo fsck' output looks good with no daemon" '
30 + grep "Lockfiles have been removed." fsck_out_actual1
31 +'
32 +
33 +# Make sure the files are actually removed
34 +test_expect_success "'ipfs repo fsck' confirm file deletion" '
35 + test ! -e "$IPFS_PATH/repo.lock" &&
36 + test ! -e "$IPFS_PATH/datastore/LOCK" &&
37 + test ! -e "$IPFS_PATH/api"
38 +'
39 +
40 +# Try with all lock files present: repo.lock, api, and datastore/LOCK with
41 +# repo.lock is non-zero TODO: this test is broken until we find consensus on the
42 +# non-zero repo.lock issue
43 +test_expect_success "'ipfs repo fsck' succeeds with no daemon running non-zero
44 +repo.lock" '
45 + mkdir -p $IPFS_PATH &&
46 + printf ":D" > $IPFS_PATH/repo.lock &&
47 + touch $IPFS_PATH/datastore/LOCK &&
48 + ipfs repo fsck > fsck_out_actual1b
49 +'
50 +test_expect_success "'ipfs repo fsck' output looks good with no daemon" '
51 + grep "Lockfiles have been removed." fsck_out_actual1b
52 +'
53 +
54 +# Make sure the files are actually removed
55 +test_expect_success "'ipfs repo fsck' confirm file deletion" '
56 + test ! -e "$IPFS_PATH/repo.lock" &&
57 + test ! -e "$IPFS_PATH/datastore/LOCK" &&
58 + test ! -e "$IPFS_PATH/api"
59 +'
60 +
61 +########################
62 +# Test for partial locks
63 +########################
64 +
65 +# Try with locks api and datastore/LOCK
66 +test_expect_success "'ipfs repo fsck' succeeds partial lock" '
67 + printf "/ip4/127.0.0.1/tcp/5001" > $IPFS_PATH/api &&
68 + touch $IPFS_PATH/datastore/LOCK &&
69 + ipfs repo fsck > fsck_out_actual2
70 +'
71 +
72 +test_expect_success "'ipfs repo fsck' output looks good with no daemon" '
73 + grep "Lockfiles have been removed." fsck_out_actual2
74 +'
75 +
76 +# Make sure the files are actually removed
77 +test_expect_success "'ipfs repo fsck' confirm file deletion" '
78 + test ! -e "$IPFS_PATH/repo.lock" &&
79 + test ! -e "$IPFS_PATH/datastore/LOCK" &&
80 + test ! -e "$IPFS_PATH/api"
81 +'
82 +
83 +# Try with locks api and repo.lock
84 +test_expect_success "'ipfs repo fsck' succeeds partial lock" '
85 + printf "/ip4/127.0.0.1/tcp/5001" > $IPFS_PATH/api &&
86 + touch $IPFS_PATH/repo.lock &&
87 + ipfs repo fsck > fsck_out_actual3
88 +'
89 +
90 +test_expect_success "'ipfs repo fsck' output looks good with no daemon" '
91 + grep "Lockfiles have been removed." fsck_out_actual3
92 +'
93 +
94 +# Make sure the files are actually removed
95 +test_expect_success "'ipfs repo fsck' confirm file deletion" '
96 + test ! -e "$IPFS_PATH/repo.lock" &&
97 + test ! -e "$IPFS_PATH/datastore/LOCK" &&
98 + test ! -e "$IPFS_PATH/api"
99 +'
100 +
101 +# Try with locks repo.lock and datastore
102 +test_expect_success "'ipfs repo fsck' succeeds partial lock" '
103 + touch $IPFS_PATH/repo.lock &&
104 + touch $IPFS_PATH/datastore/LOCK &&
105 + ipfs repo fsck > fsck_out_actual4
106 +'
107 +
108 +test_expect_success "'ipfs repo fsck' output looks good with no daemon" '
109 + grep "Lockfiles have been removed." fsck_out_actual4
110 +'
111 +
112 +# Make sure the files are actually removed
113 +test_expect_success "'ipfs repo fsck' confirm file deletion" '
114 + test ! -e "$IPFS_PATH/repo.lock" &&
115 + test ! -e "$IPFS_PATH/datastore/LOCK" &&
116 + test ! -e "$IPFS_PATH/api"
117 +'
118 +
119 +#######################
120 +# Test for single locks
121 +#######################
122 +
123 +# Try with single locks repo.lock
124 +test_expect_success "'ipfs repo fsck' succeeds partial lock" '
125 + touch $IPFS_PATH/repo.lock &&
126 + ipfs repo fsck > fsck_out_actual5
127 +'
128 +test_expect_success "'ipfs repo fsck' output looks good with no daemon" '
129 + grep "Lockfiles have been removed." fsck_out_actual5
130 +'
131 +
132 +# Make sure the files are actually removed
133 +test_expect_success "'ipfs repo fsck' confirm file deletion" '
134 + test ! -e "$IPFS_PATH/repo.lock" &&
135 + test ! -e "$IPFS_PATH/datastore/LOCK" &&
136 + test ! -e "$IPFS_PATH/api"
137 +'
138 +
139 +# Try with single locks datastore/LOCK
140 +test_expect_success "'ipfs repo fsck' succeeds partial lock" '
141 + touch $IPFS_PATH/datastore/LOCK &&
142 + ipfs repo fsck > fsck_out_actual6
143 +'
144 +test_expect_success "'ipfs repo fsck' output looks good with no daemon" '
145 + grep "Lockfiles have been removed." fsck_out_actual6
146 +'
147 +
148 +# Make sure the files are actually removed
149 +test_expect_success "'ipfs repo fsck' confirm file deletion" '
150 + test ! -e "$IPFS_PATH/repo.lock" &&
151 + test ! -e "$IPFS_PATH/datastore/LOCK" &&
152 + test ! -e "$IPFS_PATH/api"
153 +'
154 +
155 +# Try with single lock api
156 +test_expect_success "'ipfs repo fsck' succeeds partial lock" '
157 + printf "/ip4/127.0.0.1/tcp/5001" > $IPFS_PATH/api &&
158 + ipfs repo fsck > fsck_out_actual7
159 +'
160 +
161 +test_expect_success "'ipfs repo fsck' output looks good with no daemon" '
162 + grep "Lockfiles have been removed." fsck_out_actual7
163 +'
164 +
165 +# Make sure the files are actually removed
166 +test_expect_success "'ipfs repo fsck' confirm file deletion" '
167 + test ! -e "$IPFS_PATH/repo.lock" &&
168 + test ! -e "$IPFS_PATH/datastore/LOCK" &&
169 + test ! -e "$IPFS_PATH/api"
170 +'
171 +
172 +##########################
173 +# Test with daemon running
174 +##########################
175 +
176 +test_launch_ipfs_daemon
177 +
178 +# Daemon is running -> command doesn't run
179 +test_expect_success "'ipfs repo fsck' fails with daemon running" '
180 + ! (ipfs repo fsck 2>fsck_out_actual8 )
181 +
182 +'
183 +
184 +test_expect_success "'ipfs repo fsck' output looks good with daemon" '
185 + grep "Error: ipfs daemon is running" fsck_out_actual8
186 +'
187 +
188 +test_kill_ipfs_daemon
189 +
190 +test_done