github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/core/commands/pin.go (about)

     1  package commands
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  
     8  	key "github.com/ipfs/go-ipfs/blocks/key"
     9  	cmds "github.com/ipfs/go-ipfs/commands"
    10  	corerepo "github.com/ipfs/go-ipfs/core/corerepo"
    11  	u "github.com/ipfs/go-ipfs/util"
    12  )
    13  
    14  var PinCmd = &cmds.Command{
    15  	Helptext: cmds.HelpText{
    16  		Tagline: "Pin (and unpin) objects to local storage",
    17  	},
    18  
    19  	Subcommands: map[string]*cmds.Command{
    20  		"add": addPinCmd,
    21  		"rm":  rmPinCmd,
    22  		"ls":  listPinCmd,
    23  	},
    24  }
    25  
    26  type PinOutput struct {
    27  	Pinned []key.Key
    28  }
    29  
    30  var addPinCmd = &cmds.Command{
    31  	Helptext: cmds.HelpText{
    32  		Tagline: "Pins objects to local storage",
    33  		ShortDescription: `
    34  Retrieves the object named by <ipfs-path> and stores it locally
    35  on disk.
    36  `,
    37  	},
    38  
    39  	Arguments: []cmds.Argument{
    40  		cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be pinned").EnableStdin(),
    41  	},
    42  	Options: []cmds.Option{
    43  		cmds.BoolOption("recursive", "r", "Recursively pin the object linked to by the specified object(s)"),
    44  	},
    45  	Type: PinOutput{},
    46  	Run: func(req cmds.Request, res cmds.Response) {
    47  		n, err := req.InvocContext().GetNode()
    48  		if err != nil {
    49  			res.SetError(err, cmds.ErrNormal)
    50  			return
    51  		}
    52  
    53  		// set recursive flag
    54  		recursive, found, err := req.Option("recursive").Bool()
    55  		if err != nil {
    56  			res.SetError(err, cmds.ErrNormal)
    57  			return
    58  		}
    59  		if !found {
    60  			recursive = false
    61  		}
    62  
    63  		added, err := corerepo.Pin(n, req.Context(), req.Arguments(), recursive)
    64  		if err != nil {
    65  			res.SetError(err, cmds.ErrNormal)
    66  			return
    67  		}
    68  
    69  		res.SetOutput(&PinOutput{added})
    70  	},
    71  	Marshalers: cmds.MarshalerMap{
    72  		cmds.Text: func(res cmds.Response) (io.Reader, error) {
    73  			added, ok := res.Output().(*PinOutput)
    74  			if !ok {
    75  				return nil, u.ErrCast()
    76  			}
    77  
    78  			var pintype string
    79  			rec, _, _ := res.Request().Option("recursive").Bool()
    80  			if rec {
    81  				pintype = "recursively"
    82  			} else {
    83  				pintype = "directly"
    84  			}
    85  
    86  			buf := new(bytes.Buffer)
    87  			for _, k := range added.Pinned {
    88  				fmt.Fprintf(buf, "pinned %s %s\n", k, pintype)
    89  			}
    90  			return buf, nil
    91  		},
    92  	},
    93  }
    94  
    95  var rmPinCmd = &cmds.Command{
    96  	Helptext: cmds.HelpText{
    97  		Tagline: "Unpin an object from local storage",
    98  		ShortDescription: `
    99  Removes the pin from the given object allowing it to be garbage
   100  collected if needed.
   101  `,
   102  	},
   103  
   104  	Arguments: []cmds.Argument{
   105  		cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be unpinned").EnableStdin(),
   106  	},
   107  	Options: []cmds.Option{
   108  		cmds.BoolOption("recursive", "r", "Recursively unpin the object linked to by the specified object(s)"),
   109  	},
   110  	Type: PinOutput{},
   111  	Run: func(req cmds.Request, res cmds.Response) {
   112  		n, err := req.InvocContext().GetNode()
   113  		if err != nil {
   114  			res.SetError(err, cmds.ErrNormal)
   115  			return
   116  		}
   117  
   118  		// set recursive flag
   119  		recursive, found, err := req.Option("recursive").Bool()
   120  		if err != nil {
   121  			res.SetError(err, cmds.ErrNormal)
   122  			return
   123  		}
   124  		if !found {
   125  			recursive = false // default
   126  		}
   127  
   128  		removed, err := corerepo.Unpin(n, req.Context(), req.Arguments(), recursive)
   129  		if err != nil {
   130  			res.SetError(err, cmds.ErrNormal)
   131  			return
   132  		}
   133  
   134  		res.SetOutput(&PinOutput{removed})
   135  	},
   136  	Marshalers: cmds.MarshalerMap{
   137  		cmds.Text: func(res cmds.Response) (io.Reader, error) {
   138  			added, ok := res.Output().(*PinOutput)
   139  			if !ok {
   140  				return nil, u.ErrCast()
   141  			}
   142  
   143  			buf := new(bytes.Buffer)
   144  			for _, k := range added.Pinned {
   145  				fmt.Fprintf(buf, "unpinned %s\n", k)
   146  			}
   147  			return buf, nil
   148  		},
   149  	},
   150  }
   151  
   152  var listPinCmd = &cmds.Command{
   153  	Helptext: cmds.HelpText{
   154  		Tagline: "List objects pinned to local storage",
   155  		ShortDescription: `
   156  Returns a list of hashes of objects being pinned. Objects that are indirectly
   157  or recursively pinned are not included in the list.
   158  `,
   159  		LongDescription: `
   160  Returns a list of hashes of objects being pinned. Objects that are indirectly
   161  or recursively pinned are not included in the list.
   162  
   163  Use --type=<type> to specify the type of pinned keys to list. Valid values are:
   164      * "direct": pin that specific object.
   165      * "recursive": pin that specific object, and indirectly pin all its decendants
   166      * "indirect": pinned indirectly by an ancestor (like a refcount)
   167      * "all"
   168  
   169  To see the ref count on indirect pins, pass the -count option flag.
   170  Defaults to "direct".
   171  `,
   172  	},
   173  
   174  	Options: []cmds.Option{
   175  		cmds.StringOption("type", "t", "The type of pinned keys to list. Can be \"direct\", \"indirect\", \"recursive\", or \"all\". Defaults to \"direct\""),
   176  		cmds.BoolOption("count", "n", "Show refcount when listing indirect pins"),
   177  		cmds.BoolOption("quiet", "q", "Write just hashes of objects"),
   178  	},
   179  	Run: func(req cmds.Request, res cmds.Response) {
   180  		n, err := req.InvocContext().GetNode()
   181  		if err != nil {
   182  			res.SetError(err, cmds.ErrNormal)
   183  			return
   184  		}
   185  
   186  		typeStr, found, err := req.Option("type").String()
   187  		if err != nil {
   188  			res.SetError(err, cmds.ErrNormal)
   189  			return
   190  		}
   191  		if !found {
   192  			typeStr = "direct"
   193  		}
   194  
   195  		switch typeStr {
   196  		case "all", "direct", "indirect", "recursive":
   197  		default:
   198  			err = fmt.Errorf("Invalid type '%s', must be one of {direct, indirect, recursive, all}", typeStr)
   199  			res.SetError(err, cmds.ErrClient)
   200  		}
   201  
   202  		keys := make(map[string]RefKeyObject)
   203  		if typeStr == "direct" || typeStr == "all" {
   204  			for _, k := range n.Pinning.DirectKeys() {
   205  				keys[k.B58String()] = RefKeyObject{
   206  					Type:  "direct",
   207  					Count: 1,
   208  				}
   209  			}
   210  		}
   211  		if typeStr == "indirect" || typeStr == "all" {
   212  			for k, v := range n.Pinning.IndirectKeys() {
   213  				keys[k.B58String()] = RefKeyObject{
   214  					Type:  "indirect",
   215  					Count: v,
   216  				}
   217  			}
   218  		}
   219  		if typeStr == "recursive" || typeStr == "all" {
   220  			for _, k := range n.Pinning.RecursiveKeys() {
   221  				keys[k.B58String()] = RefKeyObject{
   222  					Type:  "recursive",
   223  					Count: 1,
   224  				}
   225  			}
   226  		}
   227  
   228  		res.SetOutput(&RefKeyList{Keys: keys})
   229  	},
   230  	Type: RefKeyList{},
   231  	Marshalers: cmds.MarshalerMap{
   232  		cmds.Text: func(res cmds.Response) (io.Reader, error) {
   233  			typeStr, _, err := res.Request().Option("type").String()
   234  			if err != nil {
   235  				return nil, err
   236  			}
   237  
   238  			count, _, err := res.Request().Option("count").Bool()
   239  			if err != nil {
   240  				return nil, err
   241  			}
   242  
   243  			quiet, _, err := res.Request().Option("quiet").Bool()
   244  			if err != nil {
   245  				return nil, err
   246  			}
   247  
   248  			keys, ok := res.Output().(*RefKeyList)
   249  			if !ok {
   250  				return nil, u.ErrCast()
   251  			}
   252  			out := new(bytes.Buffer)
   253  			if typeStr == "indirect" && count {
   254  				for k, v := range keys.Keys {
   255  					if quiet {
   256  						fmt.Fprintf(out, "%s %d\n", k, v.Count)
   257  					} else {
   258  						fmt.Fprintf(out, "%s %s %d\n", k, v.Type, v.Count)
   259  					}
   260  				}
   261  			} else {
   262  				for k, v := range keys.Keys {
   263  					if quiet {
   264  						fmt.Fprintf(out, "%s\n", k)
   265  					} else {
   266  						fmt.Fprintf(out, "%s %s\n", k, v.Type)
   267  					}
   268  				}
   269  			}
   270  			return out, nil
   271  		},
   272  	},
   273  }
   274  
   275  type RefKeyObject struct {
   276  	Type  string
   277  	Count int
   278  }
   279  
   280  type RefKeyList struct {
   281  	Keys map[string]RefKeyObject
   282  }