1
0
mirror of https://github.com/ipfs/kubo.git synced 2025-05-22 17:37:49 +08:00
Files
kubo/core/commands/pin.go
Thomas Gardner ddc8d0c60c commands: remove EnableStdin support for StringArg
With verbose flag:
* remove EnableStdin() flags on all StringArg,

* remove all unneeded parsing code for StringArg, and print an
* informative message if `ipfs` begins reading from a CharDevice,

* remove broken go tests for EnableStdin cli parsing, and add some
* trivial test cases for reading FileArg from stdin,

* add a panic to prevent EnableStdin from being set on
* StringArg in the future.

Resolves: #2877, #2870
License: MIT
Signed-off-by: Thomas Gardner <tmg@fastmail.com>
2016-06-26 12:28:55 +10:00

346 lines
8.8 KiB
Go

package commands
import (
"bytes"
"fmt"
"io"
key "github.com/ipfs/go-ipfs/blocks/key"
cmds "github.com/ipfs/go-ipfs/commands"
core "github.com/ipfs/go-ipfs/core"
corerepo "github.com/ipfs/go-ipfs/core/corerepo"
dag "github.com/ipfs/go-ipfs/merkledag"
path "github.com/ipfs/go-ipfs/path"
pin "github.com/ipfs/go-ipfs/pin"
u "gx/ipfs/QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1/go-ipfs-util"
context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
)
var PinCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Pin (and unpin) objects to local storage.",
},
Subcommands: map[string]*cmds.Command{
"add": addPinCmd,
"rm": rmPinCmd,
"ls": listPinCmd,
},
}
type PinOutput struct {
Pins []key.Key
}
var addPinCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Pins objects to local storage.",
ShortDescription: "Stores an IPFS object(s) from a given path locally to disk.",
},
Arguments: []cmds.Argument{
cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be pinned."),
},
Options: []cmds.Option{
cmds.BoolOption("recursive", "r", "Recursively pin the object linked to by the specified object(s).").Default(true),
},
Type: PinOutput{},
Run: func(req cmds.Request, res cmds.Response) {
n, err := req.InvocContext().GetNode()
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
defer n.Blockstore.PinLock().Unlock()
// set recursive flag
recursive, _, err := req.Option("recursive").Bool()
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
added, err := corerepo.Pin(n, req.Context(), req.Arguments(), recursive)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
res.SetOutput(&PinOutput{added})
},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) (io.Reader, error) {
added, ok := res.Output().(*PinOutput)
if !ok {
return nil, u.ErrCast()
}
var pintype string
rec, found, _ := res.Request().Option("recursive").Bool()
if rec || !found {
pintype = "recursively"
} else {
pintype = "directly"
}
buf := new(bytes.Buffer)
for _, k := range added.Pins {
fmt.Fprintf(buf, "pinned %s %s\n", k, pintype)
}
return buf, nil
},
},
}
var rmPinCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Removes the pinned object from local storage.",
ShortDescription: `
Removes the pin from the given object allowing it to be garbage
collected if needed. (By default, recursively. Use -r=false for direct pins)
`,
},
Arguments: []cmds.Argument{
cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be unpinned."),
},
Options: []cmds.Option{
cmds.BoolOption("recursive", "r", "Recursively unpin the object linked to by the specified object(s).").Default(true),
},
Type: PinOutput{},
Run: func(req cmds.Request, res cmds.Response) {
n, err := req.InvocContext().GetNode()
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
// set recursive flag
recursive, _, err := req.Option("recursive").Bool()
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
removed, err := corerepo.Unpin(n, req.Context(), req.Arguments(), recursive)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
res.SetOutput(&PinOutput{removed})
},
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.Pins {
fmt.Fprintf(buf, "unpinned %s\n", k)
}
return buf, nil
},
},
}
var listPinCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "List objects pinned to local storage.",
ShortDescription: `
Returns a list of objects that are pinned locally.
By default, all pinned objects are returned, but the '--type' flag or
arguments can restrict that to a specific pin type or to some specific objects
respectively.
`,
LongDescription: `
Returns a list of objects that are pinned locally.
By default, all pinned objects are returned, but the '--type' flag or
arguments can restrict that to a specific pin type or to some specific objects
respectively.
Use --type=<type> to specify the type of pinned keys to list.
Valid values are:
* "direct": pin that specific object.
* "recursive": pin that specific object, and indirectly pin all its
descendants
* "indirect": pinned indirectly by an ancestor (like a refcount)
* "all"
With arguments, the command fails if any of the arguments is not a pinned
object. And if --type=<type> is additionally used, the command will also fail
if any of the arguments is not of the specified type.
Example:
$ echo "hello" | ipfs add -q
QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN
$ ipfs pin ls
QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN recursive
# now remove the pin, and repin it directly
$ ipfs pin rm QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN
unpinned QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN
$ ipfs pin add -r=false QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN
pinned QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN directly
$ ipfs pin ls --type=direct
QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN direct
$ ipfs pin ls QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN
QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN direct
`,
},
Arguments: []cmds.Argument{
cmds.StringArg("ipfs-path", false, true, "Path to object(s) to be listed."),
},
Options: []cmds.Option{
cmds.StringOption("type", "t", "The type of pinned keys to list. Can be \"direct\", \"indirect\", \"recursive\", or \"all\".").Default("all"),
cmds.BoolOption("quiet", "q", "Write just hashes of objects.").Default(false),
},
Run: func(req cmds.Request, res cmds.Response) {
n, err := req.InvocContext().GetNode()
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
typeStr, _, err := req.Option("type").String()
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
switch typeStr {
case "all", "direct", "indirect", "recursive":
default:
err = fmt.Errorf("Invalid type '%s', must be one of {direct, indirect, recursive, all}", typeStr)
res.SetError(err, cmds.ErrClient)
return
}
var keys map[string]RefKeyObject
if len(req.Arguments()) > 0 {
keys, err = pinLsKeys(req.Arguments(), typeStr, req.Context(), n)
} else {
keys, err = pinLsAll(typeStr, req.Context(), n)
}
if err != nil {
res.SetError(err, cmds.ErrNormal)
} else {
res.SetOutput(&RefKeyList{Keys: keys})
}
},
Type: RefKeyList{},
Marshalers: cmds.MarshalerMap{
cmds.Text: func(res cmds.Response) (io.Reader, error) {
quiet, _, err := res.Request().Option("quiet").Bool()
if err != nil {
return nil, err
}
keys, ok := res.Output().(*RefKeyList)
if !ok {
return nil, u.ErrCast()
}
out := new(bytes.Buffer)
for k, v := range keys.Keys {
if quiet {
fmt.Fprintf(out, "%s\n", k)
} else {
fmt.Fprintf(out, "%s %s\n", k, v.Type)
}
}
return out, nil
},
},
}
type RefKeyObject struct {
Type string
}
type RefKeyList struct {
Keys map[string]RefKeyObject
}
func pinLsKeys(args []string, typeStr string, ctx context.Context, n *core.IpfsNode) (map[string]RefKeyObject, error) {
keys := make(map[string]RefKeyObject)
for _, p := range args {
dagNode, err := core.Resolve(ctx, n, path.Path(p))
if err != nil {
return nil, err
}
k, err := dagNode.Key()
if err != nil {
return nil, err
}
mode, ok := pin.StringToPinMode(typeStr)
if !ok {
return nil, fmt.Errorf("Invalid pin mode '%s'", typeStr)
}
pinType, pinned, err := n.Pinning.IsPinnedWithType(k, mode)
if err != nil {
return nil, err
}
if !pinned {
return nil, fmt.Errorf("Path '%s' is not pinned", p)
}
switch pinType {
case "direct", "indirect", "recursive", "internal":
default:
pinType = "indirect through " + pinType
}
keys[k.B58String()] = RefKeyObject{
Type: pinType,
}
}
return keys, nil
}
func pinLsAll(typeStr string, ctx context.Context, n *core.IpfsNode) (map[string]RefKeyObject, error) {
keys := make(map[string]RefKeyObject)
AddToResultKeys := func(keyList []key.Key, typeStr string) {
for _, k := range keyList {
keys[k.B58String()] = RefKeyObject{
Type: typeStr,
}
}
}
if typeStr == "direct" || typeStr == "all" {
AddToResultKeys(n.Pinning.DirectKeys(), "direct")
}
if typeStr == "indirect" || typeStr == "all" {
ks := key.NewKeySet()
for _, k := range n.Pinning.RecursiveKeys() {
nd, err := n.DAG.Get(ctx, k)
if err != nil {
return nil, err
}
err = dag.EnumerateChildren(n.Context(), n.DAG, nd, ks, false)
if err != nil {
return nil, err
}
}
AddToResultKeys(ks.Keys(), "indirect")
}
if typeStr == "recursive" || typeStr == "all" {
AddToResultKeys(n.Pinning.RecursiveKeys(), "recursive")
}
return keys, nil
}