repo.go 6.34 KB
Newer Older
1 2 3 4 5
package commands

import (
	"bytes"
	"fmt"
6 7
	cmds "github.com/ipfs/go-ipfs/commands"
	corerepo "github.com/ipfs/go-ipfs/core/corerepo"
michael's avatar
michael committed
8 9
	config "github.com/ipfs/go-ipfs/repo/config"
	lockfile "github.com/ipfs/go-ipfs/repo/fsrepo/lock"
Michael Pfister's avatar
Michael Pfister committed
10
	fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
11
	u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
David Dias's avatar
David Dias committed
12
	"io"
michael's avatar
michael committed
13 14
	"os"
	"path/filepath"
15 16
)

Michael Pfister's avatar
Michael Pfister committed
17 18 19 20
type RepoVersion struct {
	Version string
}

21 22
var RepoCmd = &cmds.Command{
	Helptext: cmds.HelpText{
rht's avatar
rht committed
23
		Tagline: "Manipulate the IPFS repo.",
24 25 26 27 28 29
		ShortDescription: `
'ipfs repo' is a plumbing command used to manipulate the repo.
`,
	},

	Subcommands: map[string]*cmds.Command{
30 31
		"gc":   repoGcCmd,
		"stat": repoStatCmd,
michael's avatar
michael committed
32
		"fsck": RepoFsckCmd,
Michael Pfister's avatar
Michael Pfister committed
33
		"version": repoVersionCmd,
34 35 36 37 38
	},
}

var repoGcCmd = &cmds.Command{
	Helptext: cmds.HelpText{
rht's avatar
rht committed
39
		Tagline: "Perform a garbage collection sweep on the repo.",
40 41 42 43 44 45 46
		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{
47
		cmds.BoolOption("quiet", "q", "Write minimal output.").Default(false),
48
	},
49
	Run: func(req cmds.Request, res cmds.Response) {
Jeromy's avatar
Jeromy committed
50
		n, err := req.InvocContext().GetNode()
51
		if err != nil {
52 53
			res.SetError(err, cmds.ErrNormal)
			return
54 55
		}

Jeromy's avatar
Jeromy committed
56
		gcOutChan, err := corerepo.GarbageCollectAsync(n, req.Context())
57
		if err != nil {
58 59
			res.SetError(err, cmds.ErrNormal)
			return
60 61
		}

Jeromy's avatar
Jeromy committed
62
		outChan := make(chan interface{})
63 64
		res.SetOutput((<-chan interface{})(outChan))

Jeromy's avatar
Jeromy committed
65 66 67 68 69 70
		go func() {
			defer close(outChan)
			for k := range gcOutChan {
				outChan <- k
			}
		}()
71
	},
72
	Type: corerepo.KeyRemoved{},
73 74
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
75
			outChan, ok := res.Output().(<-chan interface{})
76 77 78 79 80 81 82 83 84 85
			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) {
86
				obj, ok := v.(*corerepo.KeyRemoved)
87 88 89 90
				if !ok {
					return nil, u.ErrCast()
				}

91
				buf := new(bytes.Buffer)
92 93 94 95 96 97 98 99 100 101 102
				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,
103
				Res:       res,
104 105 106 107
			}, nil
		},
	},
}
108 109 110

var repoStatCmd = &cmds.Command{
	Helptext: cmds.HelpText{
David Dias's avatar
David Dias committed
111 112 113 114
		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:
115 116 117
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.
118
Version         string The repo version
David Dias's avatar
David Dias committed
119
`,
120 121 122 123 124 125 126 127
	},
	Run: func(req cmds.Request, res cmds.Response) {
		n, err := req.InvocContext().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

David Dias's avatar
David Dias committed
128
		stat, err := corerepo.RepoStat(n, req.Context())
129 130 131 132 133
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

David Dias's avatar
David Dias committed
134
		res.SetOutput(stat)
135
	},
David Dias's avatar
David Dias committed
136
	Options: []cmds.Option{
137
		cmds.BoolOption("human", "Output RepoSize in MiB.").Default(false),
David Dias's avatar
David Dias committed
138 139
	},
	Type: corerepo.Stat{},
140 141
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
David Dias's avatar
David Dias committed
142
			stat, ok := res.Output().(*corerepo.Stat)
143 144 145 146
			if !ok {
				return nil, u.ErrCast()
			}

David Dias's avatar
David Dias committed
147 148 149 150 151 152 153 154 155 156 157 158 159 160
			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)
161
			fmt.Fprintf(buf, "Version \t %s\n", stat.Version)
Thomas Gardner's avatar
Thomas Gardner committed
162

David Dias's avatar
David Dias committed
163
			return buf, nil
164 165 166
		},
	},
}
michael's avatar
michael committed
167 168 169

var RepoFsckCmd = &cmds.Command{
	Helptext: cmds.HelpText{
Richard Littauer's avatar
Richard Littauer committed
170
		Tagline: "Removes repo lockfiles.",
michael's avatar
michael committed
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
		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,
Michael Pfister's avatar
Michael Pfister committed
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
var repoVersionCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Show the repo version.",
		ShortDescription: `
'ipfs repo version' returns the current repo version.
`,
	},

	Options: []cmds.Option{
		cmds.BoolOption("quiet", "q", "Write minimal output."),
	},
	Run: func(req cmds.Request, res cmds.Response) {
		res.SetOutput(&RepoVersion{
			Version: fsrepo.RepoVersion,
		})
	},
	Type: RepoVersion{},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			response := res.Output().(*RepoVersion)

			quiet, _, err := res.Request().Option("quiet").Bool()
			if err != nil {
				return nil, err
			}

			buf := new(bytes.Buffer)
			if quiet {
				buf = bytes.NewBufferString(fmt.Sprintf("fs-repo@%s\n", response.Version))
			} else {
				buf = bytes.NewBufferString(fmt.Sprintf("ipfs repo version fs-repo@%s\n", response.Version))
			}
			return buf, nil

		},
michael's avatar
michael committed
252 253
	},
}