pin.go 5.09 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 168

Defaults to "direct".
169 170 171
`,
	},

172
	Options: []cmds.Option{
173
		cmds.StringOption("type", "t", "The type of pinned keys to list. Can be \"direct\", \"indirect\", \"recursive\", or \"all\". Defaults to \"direct\""),
174
	},
175
	Run: func(req cmds.Request, res cmds.Response) {
176 177
		n, err := req.Context().GetNode()
		if err != nil {
178 179
			res.SetError(err, cmds.ErrNormal)
			return
180 181
		}

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

191 192 193
		switch typeStr {
		case "all", "direct", "indirect", "recursive":
		default:
194 195
			err = fmt.Errorf("Invalid type '%s', must be one of {direct, indirect, recursive, all}", typeStr)
			res.SetError(err, cmds.ErrClient)
196 197 198 199 200 201 202 203 204 205 206 207 208
		}

		keys := make([]u.Key, 0)
		if typeStr == "direct" || typeStr == "all" {
			keys = append(keys, n.Pinning.DirectKeys()...)
		}
		if typeStr == "indirect" || typeStr == "all" {
			keys = append(keys, n.Pinning.IndirectKeys()...)
		}
		if typeStr == "recursive" || typeStr == "all" {
			keys = append(keys, n.Pinning.RecursiveKeys()...)
		}

209
		res.SetOutput(&KeyList{Keys: keys})
210
	},
211
	Type: KeyList{},
212 213
	Marshalers: cmds.MarshalerMap{
		cmds.Text: KeyListTextMarshaler,
214 215
	},
}