Skip to content

Commit

Permalink
Merge pull request #2208 from ipfs/add-arguments-to-pin-ls
Browse files Browse the repository at this point in the history
Add arguments to 'ipfs pin ls'
  • Loading branch information
jbenet committed Jan 21, 2016
2 parents 89a6f01 + 29830da commit 9c1375f
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 102 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# go-ipfs changelog

### 0.4.0 - 2016-01-31

* Features
* add optional path arguments to 'ipfs pin ls' (@chriscool)

* Incompatible Changes
* the default for '--type' in 'ipfs pin ls' is now "all" (@chriscool)


### 0.3.10 - 2015-12-07

This patch update introduces the 'ipfs update' command which will be used for
Expand Down
159 changes: 111 additions & 48 deletions core/commands/pin.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import (

key "github.com/ipfs/go-ipfs/blocks/key"
cmds "github.com/ipfs/go-ipfs/commands"
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
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"
u "github.com/ipfs/go-ipfs/util"
)

Expand Down Expand Up @@ -158,31 +161,41 @@ var listPinCmd = &cmds.Command{
Tagline: "List objects pinned to local storage",
ShortDescription: `
Returns a list of objects that are pinned locally.
By default, only recursively pinned returned, but others may be shown via the '--type' flag.
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, only recursively pinned returned, but others may be shown via the '--type' flag.
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 decendants
* "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
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
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\". Defaults to \"recursive\""),
cmds.BoolOption("count", "n", "Show refcount when listing indirect pins"),
Expand All @@ -195,60 +208,37 @@ Example:
return
}

typeStr, found, err := req.Option("type").String()
typeStr, typeStrFound, err := req.Option("type").String()
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
if !found {
typeStr = "recursive"
}

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)
}

keys := make(map[string]RefKeyObject)
if typeStr == "direct" || typeStr == "all" {
for _, k := range n.Pinning.DirectKeys() {
keys[k.B58String()] = RefKeyObject{
Type: "direct",
}
if typeStrFound {
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
}
} else {
typeStr = "all"
}
if typeStr == "indirect" || typeStr == "all" {
ks := key.NewKeySet()
for _, k := range n.Pinning.RecursiveKeys() {
nd, err := n.DAG.Get(n.Context(), k)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}
err = dag.EnumerateChildren(n.Context(), n.DAG, nd, ks)
if err != nil {
res.SetError(err, cmds.ErrNormal)
return
}

}
for _, k := range ks.Keys() {
keys[k.B58String()] = RefKeyObject{
Type: "indirect",
}
}
}
if typeStr == "recursive" || typeStr == "all" {
for _, k := range n.Pinning.RecursiveKeys() {
keys[k.B58String()] = RefKeyObject{
Type: "recursive",
}
}
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)
}

res.SetOutput(&RefKeyList{Keys: keys})
if err != nil {
res.SetError(err, cmds.ErrNormal)
} else {
res.SetOutput(&RefKeyList{Keys: keys})
}
},
Type: RefKeyList{},
Marshalers: cmds.MarshalerMap{
Expand Down Expand Up @@ -282,3 +272,76 @@ type RefKeyObject struct {
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
}

pinType, pinned, err := n.Pinning.IsPinnedWithType(k, typeStr)
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)
if err != nil {
return nil, err
}
}
AddToResultKeys(ks.Keys(), "indirect")
}
if typeStr == "recursive" || typeStr == "all" {
AddToResultKeys(n.Pinning.RecursiveKeys(), "recursive")
}

return keys, nil
}
39 changes: 32 additions & 7 deletions pin/pin.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const (

type Pinner interface {
IsPinned(key.Key) (string, bool, error)
IsPinnedWithType(key.Key, string) (string, bool, error)
Pin(context.Context, *mdag.Node, bool) error
Unpin(context.Context, key.Key, bool) error

Expand Down Expand Up @@ -126,7 +127,7 @@ func (p *pinner) Pin(ctx context.Context, node *mdag.Node, recurse bool) error {
func (p *pinner) Unpin(ctx context.Context, k key.Key, recursive bool) error {
p.lock.Lock()
defer p.lock.Unlock()
reason, pinned, err := p.isPinned(k)
reason, pinned, err := p.isPinnedWithType(k, "all")
if err != nil {
return err
}
Expand Down Expand Up @@ -159,22 +160,46 @@ func (p *pinner) isInternalPin(key key.Key) bool {
func (p *pinner) IsPinned(k key.Key) (string, bool, error) {
p.lock.RLock()
defer p.lock.RUnlock()
return p.isPinned(k)
return p.isPinnedWithType(k, "all")
}

// isPinned is the implementation of IsPinned that does not lock.
func (p *pinner) IsPinnedWithType(k key.Key, typeStr string) (string, bool, error) {
p.lock.RLock()
defer p.lock.RUnlock()
return p.isPinnedWithType(k, typeStr)
}

// isPinnedWithType is the implementation of IsPinnedWithType that does not lock.
// intended for use by other pinned methods that already take locks
func (p *pinner) isPinned(k key.Key) (string, bool, error) {
if p.recursePin.HasKey(k) {
func (p *pinner) isPinnedWithType(k key.Key, typeStr string) (string, bool, error) {
switch typeStr {
case "all", "direct", "indirect", "recursive", "internal":
default:
err := fmt.Errorf("Invalid type '%s', must be one of {direct, indirect, recursive, internal, all}", typeStr)
return "", false, err
}
if (typeStr == "recursive" || typeStr == "all") && p.recursePin.HasKey(k) {
return "recursive", true, nil
}
if p.directPin.HasKey(k) {
if typeStr == "recursive" {
return "", false, nil
}

if (typeStr == "direct" || typeStr == "all") && p.directPin.HasKey(k) {
return "direct", true, nil
}
if p.isInternalPin(k) {
if typeStr == "direct" {
return "", false, nil
}

if (typeStr == "internal" || typeStr == "all") && p.isInternalPin(k) {
return "internal", true, nil
}
if typeStr == "internal" {
return "", false, nil
}

// Default is "indirect"
for _, rk := range p.recursePin.GetKeys() {
rnd, err := p.dserv.Get(context.Background(), rk)
if err != nil {
Expand Down
1 change: 0 additions & 1 deletion test/bin/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,4 @@
# Do not ignore the following special scripts
!checkflags
!continueyn
!ipfs-pin-stat
!verify-go-fmt.sh
30 changes: 0 additions & 30 deletions test/bin/ipfs-pin-stat

This file was deleted.

25 changes: 9 additions & 16 deletions test/sharness/t0081-repo-pinning.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,23 @@ test_description="Test ipfs repo pinning"

. lib/test-lib.sh



test_pin_flag() {
object=$1
ptype=$2
expect=$3

echo "test_pin_flag" $@
echo "test_pin_flag" "$@"

ipfs-pin-stat "$object" | egrep "\b$ptype\b"
actual=$?

if [ "$expect" = "true" ]; then
if [ "$actual" != "0" ]; then
echo "$object should be pinned $ptype ($actual)"
return 1
fi
if ipfs pin ls --type="$ptype" "$object" >actual
then
test "$expect" = "true" && return
test_fsh cat actual
return
else
if [ "$actual" != "1" ]; then
echo "$object should NOT be pinned $ptype ($actual)"
return 1
fi
test "$expect" = "false" && return
test_fsh cat actual
return
fi
return 0
}

test_pin() {
Expand Down

0 comments on commit 9c1375f

Please sign in to comment.