@cryptotaxi247 / kubo / commits / d4ffc9454

refactor object patch command to work more betterer

License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>

Jeromy committed Jan 3, 2016 at 11:34 UTC d4ffc9454fcf9820d69e880c4c3976c2bd962d1a
4 files changed +330 -254
core/commands/object/object.go renamed
+14 -247
@@ -1,4 +1,4 @@
1 -package commands
1 +package objectcmd
2
3 import (
4 "bytes"
@@ -13,14 +13,11 @@ import (
13
14 mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
15
16 - key "github.com/ipfs/go-ipfs/blocks/key"
16 cmds "github.com/ipfs/go-ipfs/commands"
17 core "github.com/ipfs/go-ipfs/core"
18 dag "github.com/ipfs/go-ipfs/merkledag"
20 - dagutils "github.com/ipfs/go-ipfs/merkledag/utils"
19 path "github.com/ipfs/go-ipfs/path"
20 ft "github.com/ipfs/go-ipfs/unixfs"
23 - u "github.com/ipfs/go-ipfs/util"
21 )
22
23 // ErrObjectTooLarge is returned when too much data was read from stdin. current limit 512k
@@ -61,17 +58,17 @@ ipfs object patch <args> - Create new object from old ones
58 },
59
60 Subcommands: map[string]*cmds.Command{
64 - "data": objectDataCmd,
65 - "links": objectLinksCmd,
66 - "get": objectGetCmd,
67 - "put": objectPutCmd,
68 - "stat": objectStatCmd,
69 - "new": objectNewCmd,
70 - "patch": objectPatchCmd,
61 + "data": ObjectDataCmd,
62 + "links": ObjectLinksCmd,
63 + "get": ObjectGetCmd,
64 + "put": ObjectPutCmd,
65 + "stat": ObjectStatCmd,
66 + "new": ObjectNewCmd,
67 + "patch": ObjectPatchCmd,
68 },
69 }
70
74 -var objectDataCmd = &cmds.Command{
71 +var ObjectDataCmd = &cmds.Command{
72 Helptext: cmds.HelpText{
73 Tagline: "Outputs the raw bytes in an IPFS object",
74 ShortDescription: `
@@ -109,7 +106,7 @@ output is the raw data of the object.
106 },
107 }
108
112 -var objectLinksCmd = &cmds.Command{
109 +var ObjectLinksCmd = &cmds.Command{
110 Helptext: cmds.HelpText{
111 Tagline: "Outputs the links pointed to by the specified object",
112 ShortDescription: `
@@ -158,7 +155,7 @@ multihash.
155 Type: Object{},
156 }
157
161 -var objectGetCmd = &cmds.Command{
158 +var ObjectGetCmd = &cmds.Command{
159 Helptext: cmds.HelpText{
160 Tagline: "Get and serialize the DAG node named by <key>",
161 ShortDescription: `
@@ -229,7 +226,7 @@ This command outputs data in the following encodings:
226 },
227 }
228
232 -var objectStatCmd = &cmds.Command{
229 +var ObjectStatCmd = &cmds.Command{
230 Helptext: cmds.HelpText{
231 Tagline: "Get stats for the DAG node named by <key>",
232 ShortDescription: `
@@ -290,7 +287,7 @@ var objectStatCmd = &cmds.Command{
287 },
288 }
289
293 -var objectPutCmd = &cmds.Command{
290 +var ObjectPutCmd = &cmds.Command{
291 Helptext: cmds.HelpText{
292 Tagline: "Stores input as a DAG object, outputs its key",
293 ShortDescription: `
@@ -377,7 +374,7 @@ and then run
374 Type: Object{},
375 }
376
380 -var objectNewCmd = &cmds.Command{
377 +var ObjectNewCmd = &cmds.Command{
378 Helptext: cmds.HelpText{
379 Tagline: "creates a new object from an ipfs template",
380 ShortDescription: `
@@ -430,235 +427,6 @@ Available templates:
427 Type: Object{},
428 }
429
433 -var objectPatchCmd = &cmds.Command{
434 - Helptext: cmds.HelpText{
435 - Tagline: "Create a new merkledag object based on an existing one",
436 - ShortDescription: `
437 -'ipfs object patch <root> <cmd> <args>' is a plumbing command used to
438 -build custom DAG objects. It adds and removes links from objects, creating a new
439 -object as a result. This is the merkle-dag version of modifying an object. It
440 -can also set the data inside a node with 'set-data' and append to that data as
441 -well with 'append-data'.
442 -
443 -Patch commands:
444 - add-link <name> <ref> - adds a link to a node
445 - rm-link <name> - removes a link from a node
446 - set-data - sets a nodes data from stdin
447 - append-data - appends to a nodes data from stdin
448 -
449 -Examples:
450 -
451 - EMPTY_DIR=$(ipfs object new unixfs-dir)
452 - BAR=$(echo "bar" | ipfs add -q)
453 - ipfs object patch $EMPTY_DIR add-link foo $BAR
454 -
455 -This takes an empty directory, and adds a link named foo under it, pointing to
456 -a file containing 'bar', and returns the hash of the new object.
457 -
458 - ipfs object patch $FOO_BAR rm-link foo
459 -
460 -This removes the link named foo from the hash in $FOO_BAR and returns the
461 -resulting object hash.
462 -
463 -The data inside the node can be modified as well:
464 -
465 - ipfs object patch $FOO_BAR set-data < file.dat
466 - ipfs object patch $FOO_BAR append-data < file.dat
467 -
468 -`,
469 - },
470 - Options: []cmds.Option{
471 - cmds.BoolOption("create", "p", "create intermediate directories on add-link"),
472 - },
473 - Arguments: []cmds.Argument{
474 - cmds.StringArg("root", true, false, "the hash of the node to modify"),
475 - cmds.StringArg("command", true, false, "the operation to perform"),
476 - cmds.StringArg("args", true, true, "extra arguments").EnableStdin(),
477 - },
478 - Type: Object{},
479 - Run: func(req cmds.Request, res cmds.Response) {
480 - nd, err := req.InvocContext().GetNode()
481 - if err != nil {
482 - res.SetError(err, cmds.ErrNormal)
483 - return
484 - }
485 -
486 - rootarg := req.Arguments()[0]
487 - if strings.HasPrefix(rootarg, "/ipfs/") {
488 - rootarg = rootarg[6:]
489 - }
490 - rhash := key.B58KeyDecode(rootarg)
491 - if rhash == "" {
492 - res.SetError(fmt.Errorf("incorrectly formatted root hash: %s", req.Arguments()[0]), cmds.ErrNormal)
493 - return
494 - }
495 -
496 - rnode, err := nd.DAG.Get(req.Context(), rhash)
497 - if err != nil {
498 - res.SetError(err, cmds.ErrNormal)
499 - return
500 - }
501 -
502 - action := req.Arguments()[1]
503 -
504 - switch action {
505 - case "add-link":
506 - k, err := addLinkCaller(req, rnode)
507 - if err != nil {
508 - res.SetError(err, cmds.ErrNormal)
509 - return
510 - }
511 - res.SetOutput(&Object{Hash: k.B58String()})
512 - case "rm-link":
513 - k, err := rmLinkCaller(req, rnode)
514 - if err != nil {
515 - res.SetError(err, cmds.ErrNormal)
516 - return
517 - }
518 - res.SetOutput(&Object{Hash: k.B58String()})
519 - case "set-data":
520 - k, err := setDataCaller(req, rnode)
521 - if err != nil {
522 - res.SetError(err, cmds.ErrNormal)
523 - return
524 - }
525 - res.SetOutput(&Object{Hash: k.B58String()})
526 - case "append-data":
527 - k, err := appendDataCaller(req, rnode)
528 - if err != nil {
529 - res.SetError(err, cmds.ErrNormal)
530 - return
531 - }
532 - res.SetOutput(&Object{Hash: k.B58String()})
533 - default:
534 - res.SetError(fmt.Errorf("unrecognized subcommand"), cmds.ErrNormal)
535 - return
536 - }
537 - },
538 - Marshalers: cmds.MarshalerMap{
539 - cmds.Text: func(res cmds.Response) (io.Reader, error) {
540 - o, ok := res.Output().(*Object)
541 - if !ok {
542 - return nil, u.ErrCast()
543 - }
544 -
545 - return strings.NewReader(o.Hash + "\n"), nil
546 - },
547 - },
548 -}
549 -
550 -func appendDataCaller(req cmds.Request, root *dag.Node) (key.Key, error) {
551 - if len(req.Arguments()) < 3 {
552 - return "", fmt.Errorf("not enough arguments for set-data")
553 - }
554 -
555 - nd, err := req.InvocContext().GetNode()
556 - if err != nil {
557 - return "", err
558 - }
559 -
560 - root.Data = append(root.Data, []byte(req.Arguments()[2])...)
561 -
562 - newkey, err := nd.DAG.Add(root)
563 - if err != nil {
564 - return "", err
565 - }
566 -
567 - return newkey, nil
568 -}
569 -
570 -func setDataCaller(req cmds.Request, root *dag.Node) (key.Key, error) {
571 - if len(req.Arguments()) < 3 {
572 - return "", fmt.Errorf("not enough arguments for set-data")
573 - }
574 -
575 - nd, err := req.InvocContext().GetNode()
576 - if err != nil {
577 - return "", err
578 - }
579 -
580 - root.Data = []byte(req.Arguments()[2])
581 -
582 - newkey, err := nd.DAG.Add(root)
583 - if err != nil {
584 - return "", err
585 - }
586 -
587 - return newkey, nil
588 -}
589 -
590 -func rmLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) {
591 - if len(req.Arguments()) < 3 {
592 - return "", fmt.Errorf("not enough arguments for rm-link")
593 - }
594 -
595 - nd, err := req.InvocContext().GetNode()
596 - if err != nil {
597 - return "", err
598 - }
599 -
600 - path := req.Arguments()[2]
601 -
602 - e := dagutils.NewDagEditor(root, nd.DAG)
603 -
604 - err = e.RmLink(req.Context(), path)
605 - if err != nil {
606 - return "", err
607 - }
608 -
609 - nnode, err := e.Finalize(nd.DAG)
610 - if err != nil {
611 - return "", err
612 - }
613 -
614 - return nnode.Key()
615 -}
616 -
617 -func addLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) {
618 - if len(req.Arguments()) < 4 {
619 - return "", fmt.Errorf("not enough arguments for add-link")
620 - }
621 -
622 - nd, err := req.InvocContext().GetNode()
623 - if err != nil {
624 - return "", err
625 - }
626 -
627 - path := req.Arguments()[2]
628 - childk := key.B58KeyDecode(req.Arguments()[3])
629 -
630 - create, _, err := req.Option("create").Bool()
631 - if err != nil {
632 - return "", err
633 - }
634 -
635 - var createfunc func() *dag.Node
636 - if create {
637 - createfunc = func() *dag.Node {
638 - return &dag.Node{Data: ft.FolderPBData()}
639 - }
640 - }
641 -
642 - e := dagutils.NewDagEditor(root, nd.DAG)
643 -
644 - childnd, err := nd.DAG.Get(req.Context(), childk)
645 - if err != nil {
646 - return "", err
647 - }
648 -
649 - err = e.InsertNodeAtPath(req.Context(), path, childnd, createfunc)
650 - if err != nil {
651 - return "", err
652 - }
653 -
654 - nnode, err := e.Finalize(nd.DAG)
655 - if err != nil {
656 - return "", err
657 - }
658 -
659 - return nnode.Key()
660 -}
661 -
430 func nodeFromTemplate(template string) (*dag.Node, error) {
431 switch template {
432 case "unixfs-dir":
@@ -757,7 +525,6 @@ func getObjectEnc(o interface{}) objectEncoding {
525 v, ok := o.(string)
526 if !ok {
527 // chosen as default because it's human readable
760 - log.Warning("option is not a string - falling back to json")
528 return objectEncodingJSON
529 }
530
core/commands/object/patch.go new
+308
@@ -0,0 +1,308 @@
1 +package objectcmd
2 +
3 +import (
4 + "io"
5 + "io/ioutil"
6 + "strings"
7 +
8 + key "github.com/ipfs/go-ipfs/blocks/key"
9 + cmds "github.com/ipfs/go-ipfs/commands"
10 + core "github.com/ipfs/go-ipfs/core"
11 + dag "github.com/ipfs/go-ipfs/merkledag"
12 + dagutils "github.com/ipfs/go-ipfs/merkledag/utils"
13 + path "github.com/ipfs/go-ipfs/path"
14 + ft "github.com/ipfs/go-ipfs/unixfs"
15 + u "github.com/ipfs/go-ipfs/util"
16 +)
17 +
18 +var ObjectPatchCmd = &cmds.Command{
19 + Helptext: cmds.HelpText{
20 + Tagline: "Create a new merkledag object based on an existing one",
21 + ShortDescription: `
22 +'ipfs object patch <root> <cmd> <args>' is a plumbing command used to
23 +build custom DAG objects. It adds and removes links from objects, creating a new
24 +object as a result. This is the merkle-dag version of modifying an object. It
25 +can also set the data inside a node with 'set-data' and append to that data as
26 +well with 'append-data'.
27 +
28 +Patch commands:
29 + add-link <name> <ref> - adds a link to a node
30 + rm-link <name> - removes a link from a node
31 + set-data - sets a nodes data from stdin
32 + append-data - appends to a nodes data from stdin
33 +
34 +
35 +
36 + ipfs object patch $FOO_BAR rm-link foo
37 +
38 +This removes the link named foo from the hash in $FOO_BAR and returns the
39 +resulting object hash.
40 +
41 +The data inside the node can be modified as well:
42 +
43 + ipfs object patch $FOO_BAR set-data < file.dat
44 + ipfs object patch $FOO_BAR append-data < file.dat
45 +
46 +`,
47 + },
48 + Arguments: []cmds.Argument{},
49 + Subcommands: map[string]*cmds.Command{
50 + "append-data": patchAppendDataCmd,
51 + "add-link": patchAddLinkCmd,
52 + "rm-link": patchRmLinkCmd,
53 + "set-data": patchSetDataCmd,
54 + },
55 +}
56 +
57 +func objectMarshaler(res cmds.Response) (io.Reader, error) {
58 + o, ok := res.Output().(*Object)
59 + if !ok {
60 + return nil, u.ErrCast()
61 + }
62 +
63 + return strings.NewReader(o.Hash + "\n"), nil
64 +}
65 +
66 +var patchAppendDataCmd = &cmds.Command{
67 + Helptext: cmds.HelpText{
68 + Tagline: "Append data to the data segment of a dag node",
69 + ShortDescription: `
70 + `,
71 + },
72 + Arguments: []cmds.Argument{
73 + cmds.StringArg("root", true, false, "the hash of the node to modify"),
74 + cmds.FileArg("data", true, false, "data to append").EnableStdin(),
75 + },
76 + Run: func(req cmds.Request, res cmds.Response) {
77 + nd, err := req.InvocContext().GetNode()
78 + if err != nil {
79 + res.SetError(err, cmds.ErrNormal)
80 + return
81 + }
82 +
83 + root, err := path.ParsePath(req.Arguments()[0])
84 + if err != nil {
85 + res.SetError(err, cmds.ErrNormal)
86 + return
87 + }
88 +
89 + rootnd, err := core.Resolve(req.Context(), nd, root)
90 + if err != nil {
91 + res.SetError(err, cmds.ErrNormal)
92 + return
93 + }
94 +
95 + data, err := ioutil.ReadAll(req.Files())
96 + if err != nil {
97 + res.SetError(err, cmds.ErrNormal)
98 + return
99 + }
100 +
101 + rootnd.Data = append(rootnd.Data, data...)
102 +
103 + newkey, err := nd.DAG.Add(rootnd)
104 + if err != nil {
105 + res.SetError(err, cmds.ErrNormal)
106 + return
107 + }
108 +
109 + res.SetOutput(&Object{Hash: newkey.B58String()})
110 + },
111 + Type: Object{},
112 + Marshalers: cmds.MarshalerMap{
113 + cmds.Text: objectMarshaler,
114 + },
115 +}
116 +
117 +var patchSetDataCmd = &cmds.Command{
118 + Helptext: cmds.HelpText{},
119 + Arguments: []cmds.Argument{
120 + cmds.StringArg("root", true, false, "the hash of the node to modify"),
121 + cmds.FileArg("data", true, false, "data fill with").EnableStdin(),
122 + },
123 + Run: func(req cmds.Request, res cmds.Response) {
124 + nd, err := req.InvocContext().GetNode()
125 + if err != nil {
126 + res.SetError(err, cmds.ErrNormal)
127 + return
128 + }
129 +
130 + rp, err := path.ParsePath(req.Arguments()[0])
131 + if err != nil {
132 + res.SetError(err, cmds.ErrNormal)
133 + return
134 + }
135 +
136 + root, err := core.Resolve(req.Context(), nd, rp)
137 + if err != nil {
138 + res.SetError(err, cmds.ErrNormal)
139 + return
140 + }
141 +
142 + data, err := ioutil.ReadAll(req.Files())
143 + if err != nil {
144 + res.SetError(err, cmds.ErrNormal)
145 + return
146 + }
147 +
148 + root.Data = data
149 +
150 + newkey, err := nd.DAG.Add(root)
151 + if err != nil {
152 + res.SetError(err, cmds.ErrNormal)
153 + return
154 + }
155 +
156 + res.SetOutput(&Object{Hash: newkey.B58String()})
157 + },
158 + Type: Object{},
159 + Marshalers: cmds.MarshalerMap{
160 + cmds.Text: objectMarshaler,
161 + },
162 +}
163 +
164 +var patchRmLinkCmd = &cmds.Command{
165 + Helptext: cmds.HelpText{},
166 + Arguments: []cmds.Argument{
167 + cmds.StringArg("root", true, false, "the hash of the node to modify"),
168 + cmds.StringArg("link", true, false, "name of the link to remove"),
169 + },
170 + Run: func(req cmds.Request, res cmds.Response) {
171 + nd, err := req.InvocContext().GetNode()
172 + if err != nil {
173 + res.SetError(err, cmds.ErrNormal)
174 + return
175 + }
176 +
177 + rootp, err := path.ParsePath(req.Arguments()[0])
178 + if err != nil {
179 + res.SetError(err, cmds.ErrNormal)
180 + return
181 + }
182 +
183 + root, err := core.Resolve(req.Context(), nd, rootp)
184 + if err != nil {
185 + res.SetError(err, cmds.ErrNormal)
186 + return
187 + }
188 +
189 + path := req.Arguments()[1]
190 +
191 + e := dagutils.NewDagEditor(root, nd.DAG)
192 +
193 + err = e.RmLink(req.Context(), path)
194 + if err != nil {
195 + res.SetError(err, cmds.ErrNormal)
196 + return
197 + }
198 +
199 + nnode, err := e.Finalize(nd.DAG)
200 + if err != nil {
201 + res.SetError(err, cmds.ErrNormal)
202 + return
203 + }
204 +
205 + nk, err := nnode.Key()
206 + if err != nil {
207 + res.SetError(err, cmds.ErrNormal)
208 + return
209 + }
210 +
211 + res.SetOutput(&Object{Hash: nk.B58String()})
212 + },
213 + Type: Object{},
214 + Marshalers: cmds.MarshalerMap{
215 + cmds.Text: objectMarshaler,
216 + },
217 +}
218 +
219 +var patchAddLinkCmd = &cmds.Command{
220 + Helptext: cmds.HelpText{
221 + Tagline: "add a link to a given object",
222 + ShortDescription: `
223 +Examples:
224 +
225 + EMPTY_DIR=$(ipfs object new unixfs-dir)
226 + BAR=$(echo "bar" | ipfs add -q)
227 + ipfs object patch $EMPTY_DIR add-link foo $BAR
228 +
229 +This takes an empty directory, and adds a link named foo under it, pointing to
230 +a file containing 'bar', and returns the hash of the new object.
231 +`,
232 + },
233 + Options: []cmds.Option{
234 + cmds.BoolOption("p", "create", "create intermediary nodes"),
235 + },
236 + Arguments: []cmds.Argument{
237 + cmds.StringArg("root", true, false, "the hash of the node to modify"),
238 + cmds.StringArg("name", true, false, "name of link to create"),
239 + cmds.StringArg("ref", true, false, "ipfs object to add link to"),
240 + },
241 + Run: func(req cmds.Request, res cmds.Response) {
242 + nd, err := req.InvocContext().GetNode()
243 + if err != nil {
244 + res.SetError(err, cmds.ErrNormal)
245 + return
246 + }
247 +
248 + rootp, err := path.ParsePath(req.Arguments()[0])
249 + if err != nil {
250 + res.SetError(err, cmds.ErrNormal)
251 + return
252 + }
253 +
254 + root, err := core.Resolve(req.Context(), nd, rootp)
255 + if err != nil {
256 + res.SetError(err, cmds.ErrNormal)
257 + return
258 + }
259 +
260 + path := req.Arguments()[1]
261 + childk := key.B58KeyDecode(req.Arguments()[2])
262 +
263 + create, _, err := req.Option("create").Bool()
264 + if err != nil {
265 + res.SetError(err, cmds.ErrNormal)
266 + return
267 + }
268 +
269 + var createfunc func() *dag.Node
270 + if create {
271 + createfunc = func() *dag.Node {
272 + return &dag.Node{Data: ft.FolderPBData()}
273 + }
274 + }
275 +
276 + e := dagutils.NewDagEditor(root, nd.DAG)
277 +
278 + childnd, err := nd.DAG.Get(req.Context(), childk)
279 + if err != nil {
280 + res.SetError(err, cmds.ErrNormal)
281 + return
282 + }
283 +
284 + err = e.InsertNodeAtPath(req.Context(), path, childnd, createfunc)
285 + if err != nil {
286 + res.SetError(err, cmds.ErrNormal)
287 + return
288 + }
289 +
290 + nnode, err := e.Finalize(nd.DAG)
291 + if err != nil {
292 + res.SetError(err, cmds.ErrNormal)
293 + return
294 + }
295 +
296 + nk, err := nnode.Key()
297 + if err != nil {
298 + res.SetError(err, cmds.ErrNormal)
299 + return
300 + }
301 +
302 + res.SetOutput(&Object{Hash: nk.B58String()})
303 + },
304 + Type: Object{},
305 + Marshalers: cmds.MarshalerMap{
306 + cmds.Text: objectMarshaler,
307 + },
308 +}
core/commands/root.go
+7 -6
@@ -6,6 +6,7 @@ import (
6
7 cmds "github.com/ipfs/go-ipfs/commands"
8 files "github.com/ipfs/go-ipfs/core/commands/files"
9 + ocmd "github.com/ipfs/go-ipfs/core/commands/object"
10 unixfs "github.com/ipfs/go-ipfs/core/commands/unixfs"
11 logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log"
12 )
@@ -107,7 +108,7 @@ var rootSubcommands = map[string]*cmds.Command{
108 "ls": LsCmd,
109 "mount": MountCmd,
110 "name": NameCmd,
110 - "object": ObjectCmd,
111 + "object": ocmd.ObjectCmd,
112 "pin": PinCmd,
113 "ping": PingCmd,
114 "refs": RefsCmd,
@@ -148,11 +149,11 @@ var rootROSubcommands = map[string]*cmds.Command{
149 },
150 "object": &cmds.Command{
151 Subcommands: map[string]*cmds.Command{
151 - "data": objectDataCmd,
152 - "links": objectLinksCmd,
153 - "get": objectGetCmd,
154 - "stat": objectStatCmd,
155 - "patch": objectPatchCmd,
152 + "data": ocmd.ObjectDataCmd,
153 + "links": ocmd.ObjectLinksCmd,
154 + "get": ocmd.ObjectGetCmd,
155 + "stat": ocmd.ObjectStatCmd,
156 + "patch": ocmd.ObjectPatchCmd,
157 },
158 },
159 "refs": RefsROCmd,
test/sharness/t0051-object.sh
+1 -1
@@ -16,7 +16,7 @@ test_patch_create_path() {
16 target=$3
17
18 test_expect_success "object patch --create works" '
19 - PCOUT=$(ipfs object patch --create $root add-link $name $target)
19 + PCOUT=$(ipfs object patch $root add-link --create $name $target)
20 '
21
22 test_expect_success "output looks good" '