pin.go 5.94 KB
Newer Older
1 2 3
package commands

import (
Jeromy's avatar
Jeromy committed
4
	"bytes"
5
	"fmt"
Jeromy's avatar
Jeromy committed
6
	"io"
7

8 9 10
	cmds "github.com/ipfs/go-ipfs/commands"
	corerepo "github.com/ipfs/go-ipfs/core/corerepo"
	u "github.com/ipfs/go-ipfs/util"
11 12
)

13
var PinCmd = &cmds.Command{
14
	Helptext: cmds.HelpText{
Brian Tiger Chow's avatar
Brian Tiger Chow committed
15
		Tagline: "Pin (and unpin) objects to local storage",
16
	},
17

Brian Tiger Chow's avatar
Brian Tiger Chow committed
18 19 20
	Subcommands: map[string]*cmds.Command{
		"add": addPinCmd,
		"rm":  rmPinCmd,
21
		"ls":  listPinCmd,
Brian Tiger Chow's avatar
Brian Tiger Chow committed
22 23 24
	},
}

Jeromy's avatar
Jeromy committed
25 26 27 28
type PinOutput struct {
	Pinned []u.Key
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
29
var addPinCmd = &cmds.Command{
30 31 32 33
	Helptext: cmds.HelpText{
		Tagline: "Pins objects to local storage",
		ShortDescription: `
Retrieves the object named by <ipfs-path> and stores it locally
34
on disk.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
35
`,
36
	},
37

38
	Arguments: []cmds.Argument{
39
		cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be pinned").EnableStdin(),
40 41
	},
	Options: []cmds.Option{
42
		cmds.BoolOption("recursive", "r", "Recursively pin the object linked to by the specified object(s)"),
43
	},
Jeromy's avatar
Jeromy committed
44
	Type: PinOutput{},
45
	Run: func(req cmds.Request, res cmds.Response) {
46 47
		n, err := req.Context().GetNode()
		if err != nil {
48 49
			res.SetError(err, cmds.ErrNormal)
			return
50
		}
51 52

		// set recursive flag
53 54
		recursive, found, err := req.Option("recursive").Bool()
		if err != nil {
55 56
			res.SetError(err, cmds.ErrNormal)
			return
57 58 59 60
		}
		if !found {
			recursive = false
		}
61

62
		added, err := corerepo.Pin(n, req.Arguments(), recursive)
63
		if err != nil {
64 65
			res.SetError(err, cmds.ErrNormal)
			return
66 67
		}

68
		res.SetOutput(&PinOutput{added})
Jeromy's avatar
Jeromy committed
69 70 71 72 73 74 75 76
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			added, ok := res.Output().(*PinOutput)
			if !ok {
				return nil, u.ErrCast()
			}

Jeromy's avatar
Jeromy committed
77 78 79 80 81 82 83 84
			var pintype string
			rec, _, _ := res.Request().Option("recursive").Bool()
			if rec {
				pintype = "recursively"
			} else {
				pintype = "directly"
			}

Jeromy's avatar
Jeromy committed
85 86
			buf := new(bytes.Buffer)
			for _, k := range added.Pinned {
Jeromy's avatar
Jeromy committed
87
				fmt.Fprintf(buf, "pinned %s %s\n", k, pintype)
Jeromy's avatar
Jeromy committed
88 89 90
			}
			return buf, nil
		},
91 92 93
	},
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
94
var rmPinCmd = &cmds.Command{
95 96 97 98
	Helptext: cmds.HelpText{
		Tagline: "Unpin an object from local storage",
		ShortDescription: `
Removes the pin from the given object allowing it to be garbage
99
collected if needed.
Brian Tiger Chow's avatar
Brian Tiger Chow committed
100
`,
101
	},
102

103
	Arguments: []cmds.Argument{
104
		cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be unpinned").EnableStdin(),
105 106
	},
	Options: []cmds.Option{
107
		cmds.BoolOption("recursive", "r", "Recursively unpin the object linked to by the specified object(s)"),
108
	},
Jeromy's avatar
Jeromy committed
109
	Type: PinOutput{},
110
	Run: func(req cmds.Request, res cmds.Response) {
111 112
		n, err := req.Context().GetNode()
		if err != nil {
113 114
			res.SetError(err, cmds.ErrNormal)
			return
115
		}
116 117

		// set recursive flag
118 119
		recursive, found, err := req.Option("recursive").Bool()
		if err != nil {
120 121
			res.SetError(err, cmds.ErrNormal)
			return
122 123 124 125
		}
		if !found {
			recursive = false // default
		}
126

127
		removed, err := corerepo.Unpin(n, req.Arguments(), recursive)
128
		if err != nil {
129 130
			res.SetError(err, cmds.ErrNormal)
			return
131 132
		}

133
		res.SetOutput(&PinOutput{removed})
Jeromy's avatar
Jeromy committed
134 135 136 137 138 139 140 141 142 143
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			added, ok := res.Output().(*PinOutput)
			if !ok {
				return nil, u.ErrCast()
			}

			buf := new(bytes.Buffer)
			for _, k := range added.Pinned {
Jeromy's avatar
Jeromy committed
144
				fmt.Fprintf(buf, "unpinned %s\n", k)
Jeromy's avatar
Jeromy committed
145 146 147
			}
			return buf, nil
		},
148 149
	},
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
150

151 152 153 154 155 156
var listPinCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List objects pinned to local storage",
		ShortDescription: `
Returns a list of hashes of objects being pinned. Objects that are indirectly
or recursively pinned are not included in the list.
157 158 159 160 161 162
`,
		LongDescription: `
Returns a list of hashes of objects being pinned. Objects that are indirectly
or recursively pinned are not included in the list.

Use --type=<type> to specify the type of pinned keys to list. Valid values are:
163 164 165
    * "direct": pin that specific object.
    * "recursive": pin that specific object, and indirectly pin all its decendants
    * "indirect": pinned indirectly by an ancestor (like a refcount)
166
    * "all"
167

Jeromy's avatar
Jeromy committed
168
To see the ref count on indirect pins, pass the -count option flag.
169
Defaults to "direct".
170 171 172
`,
	},

173
	Options: []cmds.Option{
174
		cmds.StringOption("type", "t", "The type of pinned keys to list. Can be \"direct\", \"indirect\", \"recursive\", or \"all\". Defaults to \"direct\""),
Jeromy's avatar
Jeromy committed
175
		cmds.BoolOption("count", "n", "Show refcount when listing indirect pins"),
176
	},
177
	Run: func(req cmds.Request, res cmds.Response) {
178 179
		n, err := req.Context().GetNode()
		if err != nil {
180 181
			res.SetError(err, cmds.ErrNormal)
			return
182 183
		}

184 185
		typeStr, found, err := req.Option("type").String()
		if err != nil {
186 187
			res.SetError(err, cmds.ErrNormal)
			return
188 189
		}
		if !found {
190
			typeStr = "direct"
191 192
		}

193 194 195
		switch typeStr {
		case "all", "direct", "indirect", "recursive":
		default:
196 197
			err = fmt.Errorf("Invalid type '%s', must be one of {direct, indirect, recursive, all}", typeStr)
			res.SetError(err, cmds.ErrClient)
198 199
		}

Jeromy's avatar
Jeromy committed
200
		keys := make(map[string]int)
201
		if typeStr == "direct" || typeStr == "all" {
Jeromy's avatar
Jeromy committed
202 203 204
			for _, k := range n.Pinning.DirectKeys() {
				keys[k.B58String()] = 1
			}
205 206
		}
		if typeStr == "indirect" || typeStr == "all" {
Jeromy's avatar
Jeromy committed
207 208 209
			for k, v := range n.Pinning.IndirectKeys() {
				keys[k.B58String()] = v
			}
210 211
		}
		if typeStr == "recursive" || typeStr == "all" {
Jeromy's avatar
Jeromy committed
212 213 214
			for _, k := range n.Pinning.RecursiveKeys() {
				keys[k.B58String()] = 1
			}
215 216
		}

Jeromy's avatar
Jeromy committed
217
		res.SetOutput(&RefKeyList{Keys: keys})
218
	},
Jeromy's avatar
Jeromy committed
219
	Type: RefKeyList{},
220
	Marshalers: cmds.MarshalerMap{
Jeromy's avatar
Jeromy committed
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
		cmds.Text: func(res cmds.Response) (io.Reader, error) {
			typeStr, _, err := res.Request().Option("type").String()
			if err != nil {
				return nil, err
			}

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

			keys, ok := res.Output().(*RefKeyList)
			if !ok {
				return nil, u.ErrCast()
			}
			out := new(bytes.Buffer)
			if typeStr == "indirect" && count {
				for k, v := range keys.Keys {
					fmt.Fprintf(out, "%s %d\n", k, v)
				}
			} else {
				for k, _ := range keys.Keys {
					fmt.Fprintf(out, "%s\n", k)
				}
			}
			return out, nil
		},
248 249
	},
}
Jeromy's avatar
Jeromy committed
250 251 252 253

type RefKeyList struct {
	Keys map[string]int
}