-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
object plumbing commands #185
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package main | ||
|
||
import ( | ||
flag "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" | ||
commander "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" | ||
"github.com/jbenet/go-ipfs/core/commands" | ||
) | ||
|
||
var cmdIpfsObject = &commander.Command{ | ||
UsageLine: "object", | ||
Short: "interact with ipfs objects", | ||
Long: `ipfs object - interact with ipfs objects | ||
|
||
ipfs object data <key> - return the data for this key as raw bytes | ||
ipfs object links <key> - lists (the keys of ?) the links this key points to | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe: |
||
ipfs object get <key> - output dag object to stdout | ||
ipfs object put - add dag object from stdin | ||
|
||
ipfs object is a plumbing command used to manipulate dag objects directly. | ||
- <key> is a base58 encoded multihash. | ||
- It reads from stdin or writes to stdout. | ||
- It accepts multiple encodings: --encoding=[ protobuf, json, ... ]`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if there are any other encodings we would want to support... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Depends on how nice we want to be. msgpack, xml, rdf, and so on do see some use. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, xml encoding might be nice to integrate with older systems |
||
Subcommands: []*commander.Command{ | ||
cmdIpfsObjectData, | ||
cmdIpfsObjectLinks, | ||
cmdIpfsObjectGet, | ||
cmdIpfsObjectPut, | ||
}, | ||
Flag: *flag.NewFlagSet("ipfs-object", flag.ExitOnError), | ||
} | ||
|
||
var cmdIpfsObjectData = &commander.Command{ | ||
UsageLine: "data <key>", | ||
Short: "data outputs the raw bytes named by <key>", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe make it more clear that its stripping the actual "Data" out of the protobuf? not sure how to word that |
||
Long: `ipfs data <key> - data outputs the raw bytes named by <key> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. command hierarchy: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. actually, if you want, we can merge this and i can clean up all the wording -- sorry i'm very picky :s There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's fine by me :) |
||
|
||
ipfs data is a plumbing command for retreiving the raw bytes stored in a dag node. | ||
It outputs to stdout, and <key> is a base58 encoded multihash.`, | ||
Run: makeCommand(command{ | ||
name: "objectData", | ||
args: 1, | ||
flags: nil, | ||
online: true, | ||
cmdFn: commands.ObjectData, | ||
}), | ||
} | ||
|
||
var cmdIpfsObjectLinks = &commander.Command{ | ||
UsageLine: "links <key>", | ||
Short: "outputs the links pointed to by <key>", | ||
Long: `ipfs links <key> - outputs the links pointed to by <key> | ||
|
||
ipfs block get is a plumbing command for retreiving raw ipfs blocks. | ||
It outputs to stdout, and <key> is a base58 encoded multihash.`, | ||
Run: makeCommand(command{ | ||
name: "objectLinks", | ||
args: 1, | ||
flags: nil, | ||
online: true, | ||
cmdFn: commands.ObjectLinks, | ||
}), | ||
} | ||
|
||
func init() { | ||
cmdIpfsObjectGet.Flag.String("encoding", "json", "the encoding to use..") | ||
cmdIpfsObjectPut.Flag.String("encoding", "json", "the encoding to use..") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. im curious what some example usages of this command with the encodings are, especially for put There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought it was supposed for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, you would be able to put an object described as json, as below, and have it be turned into the native encoding (protobuf) and so on for you. {
"links": [
["QmbnJp7DAnncbvQEpvPWcCErsoCBHaSn3fM8wS9V7Ty9wR", "149759", "accum-transform"],
["QmaksLKRUazZNJzqJkcZoz4dk6uJqVEJbDCUGXgUS88DT3", "1988708", "request"],
["QmVA4mvPhsq5C48XBwHB5VE3MLcUSKXyYpmpxxovKDMWGR", "125726", "through2"]
],
"data": "some data"
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. being able to manipulate json and put it back will be a big deal. we need to make sure that all the tooling we have exposes human readable versions easily. (analogous to the power of "view source" on the web). When it comes to adoption, human readability has often trumped performance. Why not both. |
||
} | ||
|
||
var cmdIpfsObjectGet = &commander.Command{ | ||
UsageLine: "get <key>", | ||
Short: "get and serialize the dag node named by <key>", | ||
Long: `ipfs get <key> - get and output the dag node named by <key> | ||
|
||
ipfs object get is a plumbing command for retreiving dag nodes. | ||
It serialize the dag node to the format specified by the format flag. | ||
It outputs to stdout, and <key> is a base58 encoded multihash. | ||
|
||
Formats: | ||
|
||
This command outputs and accepts data in a variety of encodings: protobuf, json, etc. | ||
Use the --encoding flag | ||
`, | ||
Run: makeCommand(command{ | ||
name: "blockGet", | ||
args: 1, | ||
flags: []string{"encoding"}, | ||
online: true, | ||
cmdFn: commands.ObjectGet, | ||
}), | ||
} | ||
|
||
var cmdIpfsObjectPut = &commander.Command{ | ||
UsageLine: "put", | ||
Short: "store stdin as a dag object, outputs <key>", | ||
Long: `ipfs put - store stdin as a dag object, outputs <key> | ||
|
||
ipfs object put is a plumbing command for storing dag nodes. | ||
It serialize the dag node to the format specified by the format flag. | ||
It reads from stding, and <key> is a base58 encoded multihash. | ||
|
||
Formats: | ||
|
||
This command outputs and accepts data in a variety of encodings: protobuf, json, etc. | ||
Use the --encoding flag`, | ||
Run: makeCommand(command{ | ||
name: "blockPut", | ||
args: 0, | ||
flags: []string{"encoding"}, | ||
online: true, | ||
cmdFn: commands.ObjectPut, | ||
}), | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
package commands | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"os" | ||
|
||
"github.com/jbenet/go-ipfs/core" | ||
dag "github.com/jbenet/go-ipfs/merkledag" | ||
) | ||
|
||
// ObjectData takes a key string from args and writes out the raw bytes of that node (if there is one) | ||
func ObjectData(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { | ||
dagnode, err := n.Resolver.ResolvePath(args[0]) | ||
if err != nil { | ||
return fmt.Errorf("objectData error: %v", err) | ||
} | ||
log.Debug("objectData: found dagnode %q (# of bytes: %d - # links: %d)", args[0], len(dagnode.Data), len(dagnode.Links)) | ||
|
||
_, err = io.Copy(out, bytes.NewReader(dagnode.Data)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. out.Write(dagnode.Data) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wouldn't you have to check the number of bytes written for larger files? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also: io.Copy() checks for implementations of WriteTo or ReadFrom which saves copying in ram |
||
return err | ||
} | ||
|
||
// ObjectLinks takes a key string from args and lists the links it points to | ||
func ObjectLinks(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { | ||
dagnode, err := n.Resolver.ResolvePath(args[0]) | ||
if err != nil { | ||
return fmt.Errorf("objectLinks error: %v", err) | ||
} | ||
log.Debug("ObjectLinks: found dagnode %q (# of bytes: %d - # links: %d)", args[0], len(dagnode.Data), len(dagnode.Links)) | ||
|
||
if len(dagnode.Links) == 0 { | ||
// TODO(cryptix): named error? | ||
return errors.New("objectLinks: no links in this node") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 named errors. But-- in this case, empty links is not an error. it's just empty. like:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, the root file object can(should) also contain data (ive been trying to push for this to improve latencies) |
||
} | ||
|
||
for _, link := range dagnode.Links { | ||
_, err = fmt.Fprintf(out, "%s %d %q\n", link.Hash.B58String(), link.Size, link.Name) | ||
if err != nil { | ||
break | ||
} | ||
} | ||
|
||
return err | ||
} | ||
|
||
// ErrUnknownObjectEnc is returned if a invalid encoding is supplied | ||
var ErrUnknownObjectEnc = errors.New("unknown object encoding") | ||
|
||
type objectEncoding string | ||
|
||
const ( | ||
objectEncodingJSON objectEncoding = "json" | ||
objectEncodingProtobuf = "protobuf" | ||
) | ||
|
||
func getObjectEnc(o interface{}) objectEncoding { | ||
v, ok := o.(string) | ||
if !ok { | ||
// chosen as default because it's human readable | ||
log.Warning("option is not a string - falling back to json") | ||
return objectEncodingJSON | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
} | ||
|
||
return objectEncoding(v) | ||
} | ||
|
||
// ObjectGet takes a key string from args and a format option and serializes the dagnode to that format | ||
func ObjectGet(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { | ||
dagnode, err := n.Resolver.ResolvePath(args[0]) | ||
if err != nil { | ||
return fmt.Errorf("ObjectGet error: %v", err) | ||
} | ||
log.Debug("objectGet: found dagnode %q (# of bytes: %d - # links: %d)", args[0], len(dagnode.Data), len(dagnode.Links)) | ||
|
||
// sadly all encodings dont implement a common interface | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should do that. let's make a (and, this reminds me, we'll need a multicodec format if we want to enable multiple native encodings) |
||
var data []byte | ||
switch getObjectEnc(opts["encoding"]) { | ||
case objectEncodingJSON: | ||
data, err = json.MarshalIndent(dagnode, "", " ") | ||
|
||
case objectEncodingProtobuf: | ||
data, err = dagnode.Marshal() | ||
|
||
default: | ||
return ErrUnknownObjectEnc | ||
} | ||
|
||
if err != nil { | ||
return fmt.Errorf("ObjectGet error: %v", err) | ||
} | ||
|
||
_, err = io.Copy(out, bytes.NewReader(data)) | ||
return err | ||
} | ||
|
||
// ObjectPut takes a format option, serilizes bytes from stdin and updates the dag with that data | ||
func ObjectPut(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { | ||
var ( | ||
dagnode *dag.Node | ||
data []byte | ||
err error | ||
) | ||
|
||
data, err = ioutil.ReadAll(os.Stdin) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. might be useful to break on a maximum size. not sure good an upper bound is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ive been keeping the number 512k in my head as a max block size. I have no scientific evidence to back up that claim though. |
||
if err != nil { | ||
return fmt.Errorf("ObjectPut error: %v", err) | ||
} | ||
|
||
switch getObjectEnc(opts["encoding"]) { | ||
case objectEncodingJSON: | ||
dagnode = new(dag.Node) | ||
err = json.Unmarshal(data, dagnode) | ||
|
||
case objectEncodingProtobuf: | ||
dagnode, err = dag.Decoded(data) | ||
|
||
default: | ||
return ErrUnknownObjectEnc | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah let's do that |
||
|
||
if err != nil { | ||
return fmt.Errorf("ObjectPut error: %v", err) | ||
} | ||
|
||
return addNode(n, dagnode, "stdin", out) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 on this section. maybe move it below "advanced" as things like
mount
will be used commonly.