Commit 9e3bfa28 authored by Juan Batiz-Benet's avatar Juan Batiz-Benet

Merge pull request #185 from cryptix/cmdObjects

object plumbing commands
parents c8ea9136 468494e5
......@@ -37,6 +37,10 @@ Tool commands:
version Show ipfs version information.
commands List all available commands.
Plumbing commands:
block Interact with raw blocks in the datastore
object Interact with raw dag nodes
Advanced Commands:
mount Mount an ipfs read-only mountpoint.
......@@ -62,6 +66,7 @@ Use "ipfs help <command>" for more information about a command.
cmdIpfsBootstrap,
cmdIpfsDiag,
cmdIpfsBlock,
cmdIpfsObject,
cmdIpfsLog,
},
Flag: *flag.NewFlagSet("ipfs", flag.ExitOnError),
......
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
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, ... ]`,
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>",
Long: `ipfs data <key> - data outputs the raw bytes named by <key>
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..")
}
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,
}),
}
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))
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))
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
}
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
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
}
// ErrObjectTooLarge is returned when too much data was read from stdin. current limit 512k
var ErrObjectTooLarge = errors.New("input object was too large. limit is 512kbytes")
const inputLimit = 512 * 1024
// 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(io.LimitReader(os.Stdin, inputLimit+10))
if err != nil {
return fmt.Errorf("ObjectPut error: %v", err)
}
if len(data) >= inputLimit {
return ErrObjectTooLarge
}
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
}
if err != nil {
return fmt.Errorf("ObjectPut error: %v", err)
}
return addNode(n, dagnode, "stdin", out)
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment