From bc6800035b77737e47049e7ac58ab7287a03f456 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 28 Oct 2014 23:47:53 -0700 Subject: [PATCH 001/383] move ipfs commands --- cmd/{ipfs => ipfs2}/.gitignore | 0 cmd/{ipfs => ipfs2}/Makefile | 0 cmd/{ipfs => ipfs2}/README.md | 0 cmd/{ipfs => ipfs2}/add.go | 0 cmd/{ipfs => ipfs2}/block.go | 0 cmd/{ipfs => ipfs2}/bootstrap.go | 0 cmd/{ipfs => ipfs2}/cat.go | 0 cmd/{ipfs => ipfs2}/commands.go | 0 cmd/{ipfs => ipfs2}/config.go | 0 cmd/{ipfs => ipfs2}/diag.go | 0 cmd/{ipfs => ipfs2}/equinox.yaml | 0 cmd/{ipfs => ipfs2}/gen.go | 0 cmd/{ipfs => ipfs2}/init.go | 0 cmd/{ipfs => ipfs2}/ipfs.go | 0 cmd/{ipfs => ipfs2}/log.go | 0 cmd/{ipfs => ipfs2}/ls.go | 0 cmd/{ipfs => ipfs2}/mount_unix.go | 0 cmd/{ipfs => ipfs2}/mount_windows.go | 0 cmd/{ipfs => ipfs2}/name.go | 0 cmd/{ipfs => ipfs2}/objects.go | 0 cmd/{ipfs => ipfs2}/pin.go | 0 cmd/{ipfs => ipfs2}/publish.go | 0 cmd/{ipfs => ipfs2}/refs.go | 0 cmd/{ipfs => ipfs2}/resolve.go | 0 cmd/{ipfs => ipfs2}/run.go | 0 cmd/{ipfs => ipfs2}/serve.go | 0 cmd/{ipfs => ipfs2}/tour.go | 0 cmd/{ipfs => ipfs2}/update.go | 0 cmd/{ipfs => ipfs2}/version.go | 0 29 files changed, 0 insertions(+), 0 deletions(-) rename cmd/{ipfs => ipfs2}/.gitignore (100%) rename cmd/{ipfs => ipfs2}/Makefile (100%) rename cmd/{ipfs => ipfs2}/README.md (100%) rename cmd/{ipfs => ipfs2}/add.go (100%) rename cmd/{ipfs => ipfs2}/block.go (100%) rename cmd/{ipfs => ipfs2}/bootstrap.go (100%) rename cmd/{ipfs => ipfs2}/cat.go (100%) rename cmd/{ipfs => ipfs2}/commands.go (100%) rename cmd/{ipfs => ipfs2}/config.go (100%) rename cmd/{ipfs => ipfs2}/diag.go (100%) rename cmd/{ipfs => ipfs2}/equinox.yaml (100%) rename cmd/{ipfs => ipfs2}/gen.go (100%) rename cmd/{ipfs => ipfs2}/init.go (100%) rename cmd/{ipfs => ipfs2}/ipfs.go (100%) rename cmd/{ipfs => ipfs2}/log.go (100%) rename cmd/{ipfs => ipfs2}/ls.go (100%) rename cmd/{ipfs => ipfs2}/mount_unix.go (100%) rename cmd/{ipfs => ipfs2}/mount_windows.go (100%) rename cmd/{ipfs => ipfs2}/name.go (100%) rename cmd/{ipfs => ipfs2}/objects.go (100%) rename cmd/{ipfs => ipfs2}/pin.go (100%) rename cmd/{ipfs => ipfs2}/publish.go (100%) rename cmd/{ipfs => ipfs2}/refs.go (100%) rename cmd/{ipfs => ipfs2}/resolve.go (100%) rename cmd/{ipfs => ipfs2}/run.go (100%) rename cmd/{ipfs => ipfs2}/serve.go (100%) rename cmd/{ipfs => ipfs2}/tour.go (100%) rename cmd/{ipfs => ipfs2}/update.go (100%) rename cmd/{ipfs => ipfs2}/version.go (100%) diff --git a/cmd/ipfs/.gitignore b/cmd/ipfs2/.gitignore similarity index 100% rename from cmd/ipfs/.gitignore rename to cmd/ipfs2/.gitignore diff --git a/cmd/ipfs/Makefile b/cmd/ipfs2/Makefile similarity index 100% rename from cmd/ipfs/Makefile rename to cmd/ipfs2/Makefile diff --git a/cmd/ipfs/README.md b/cmd/ipfs2/README.md similarity index 100% rename from cmd/ipfs/README.md rename to cmd/ipfs2/README.md diff --git a/cmd/ipfs/add.go b/cmd/ipfs2/add.go similarity index 100% rename from cmd/ipfs/add.go rename to cmd/ipfs2/add.go diff --git a/cmd/ipfs/block.go b/cmd/ipfs2/block.go similarity index 100% rename from cmd/ipfs/block.go rename to cmd/ipfs2/block.go diff --git a/cmd/ipfs/bootstrap.go b/cmd/ipfs2/bootstrap.go similarity index 100% rename from cmd/ipfs/bootstrap.go rename to cmd/ipfs2/bootstrap.go diff --git a/cmd/ipfs/cat.go b/cmd/ipfs2/cat.go similarity index 100% rename from cmd/ipfs/cat.go rename to cmd/ipfs2/cat.go diff --git a/cmd/ipfs/commands.go b/cmd/ipfs2/commands.go similarity index 100% rename from cmd/ipfs/commands.go rename to cmd/ipfs2/commands.go diff --git a/cmd/ipfs/config.go b/cmd/ipfs2/config.go similarity index 100% rename from cmd/ipfs/config.go rename to cmd/ipfs2/config.go diff --git a/cmd/ipfs/diag.go b/cmd/ipfs2/diag.go similarity index 100% rename from cmd/ipfs/diag.go rename to cmd/ipfs2/diag.go diff --git a/cmd/ipfs/equinox.yaml b/cmd/ipfs2/equinox.yaml similarity index 100% rename from cmd/ipfs/equinox.yaml rename to cmd/ipfs2/equinox.yaml diff --git a/cmd/ipfs/gen.go b/cmd/ipfs2/gen.go similarity index 100% rename from cmd/ipfs/gen.go rename to cmd/ipfs2/gen.go diff --git a/cmd/ipfs/init.go b/cmd/ipfs2/init.go similarity index 100% rename from cmd/ipfs/init.go rename to cmd/ipfs2/init.go diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs2/ipfs.go similarity index 100% rename from cmd/ipfs/ipfs.go rename to cmd/ipfs2/ipfs.go diff --git a/cmd/ipfs/log.go b/cmd/ipfs2/log.go similarity index 100% rename from cmd/ipfs/log.go rename to cmd/ipfs2/log.go diff --git a/cmd/ipfs/ls.go b/cmd/ipfs2/ls.go similarity index 100% rename from cmd/ipfs/ls.go rename to cmd/ipfs2/ls.go diff --git a/cmd/ipfs/mount_unix.go b/cmd/ipfs2/mount_unix.go similarity index 100% rename from cmd/ipfs/mount_unix.go rename to cmd/ipfs2/mount_unix.go diff --git a/cmd/ipfs/mount_windows.go b/cmd/ipfs2/mount_windows.go similarity index 100% rename from cmd/ipfs/mount_windows.go rename to cmd/ipfs2/mount_windows.go diff --git a/cmd/ipfs/name.go b/cmd/ipfs2/name.go similarity index 100% rename from cmd/ipfs/name.go rename to cmd/ipfs2/name.go diff --git a/cmd/ipfs/objects.go b/cmd/ipfs2/objects.go similarity index 100% rename from cmd/ipfs/objects.go rename to cmd/ipfs2/objects.go diff --git a/cmd/ipfs/pin.go b/cmd/ipfs2/pin.go similarity index 100% rename from cmd/ipfs/pin.go rename to cmd/ipfs2/pin.go diff --git a/cmd/ipfs/publish.go b/cmd/ipfs2/publish.go similarity index 100% rename from cmd/ipfs/publish.go rename to cmd/ipfs2/publish.go diff --git a/cmd/ipfs/refs.go b/cmd/ipfs2/refs.go similarity index 100% rename from cmd/ipfs/refs.go rename to cmd/ipfs2/refs.go diff --git a/cmd/ipfs/resolve.go b/cmd/ipfs2/resolve.go similarity index 100% rename from cmd/ipfs/resolve.go rename to cmd/ipfs2/resolve.go diff --git a/cmd/ipfs/run.go b/cmd/ipfs2/run.go similarity index 100% rename from cmd/ipfs/run.go rename to cmd/ipfs2/run.go diff --git a/cmd/ipfs/serve.go b/cmd/ipfs2/serve.go similarity index 100% rename from cmd/ipfs/serve.go rename to cmd/ipfs2/serve.go diff --git a/cmd/ipfs/tour.go b/cmd/ipfs2/tour.go similarity index 100% rename from cmd/ipfs/tour.go rename to cmd/ipfs2/tour.go diff --git a/cmd/ipfs/update.go b/cmd/ipfs2/update.go similarity index 100% rename from cmd/ipfs/update.go rename to cmd/ipfs2/update.go diff --git a/cmd/ipfs/version.go b/cmd/ipfs2/version.go similarity index 100% rename from cmd/ipfs/version.go rename to cmd/ipfs2/version.go From d1fa4bd9b5a3a9ec8620fb67fe052bec3bd314cc Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 28 Oct 2014 23:48:11 -0700 Subject: [PATCH 002/383] copy to old location --- cmd/ipfs/.gitignore | 1 + cmd/ipfs/Makefile | 7 ++ cmd/ipfs/README.md | 29 +++++ cmd/ipfs/add.go | 39 +++++++ cmd/ipfs/block.go | 58 ++++++++++ cmd/ipfs/bootstrap.go | 236 ++++++++++++++++++++++++++++++++++++++ cmd/ipfs/cat.go | 26 +++++ cmd/ipfs/commands.go | 74 ++++++++++++ cmd/ipfs/config.go | 143 +++++++++++++++++++++++ cmd/ipfs/diag.go | 32 ++++++ cmd/ipfs/equinox.yaml | 6 + cmd/ipfs/gen.go | 72 ++++++++++++ cmd/ipfs/init.go | 150 ++++++++++++++++++++++++ cmd/ipfs/ipfs.go | 220 +++++++++++++++++++++++++++++++++++ cmd/ipfs/log.go | 29 +++++ cmd/ipfs/ls.go | 29 +++++ cmd/ipfs/mount_unix.go | 94 +++++++++++++++ cmd/ipfs/mount_windows.go | 19 +++ cmd/ipfs/name.go | 57 +++++++++ cmd/ipfs/objects.go | 112 ++++++++++++++++++ cmd/ipfs/pin.go | 62 ++++++++++ cmd/ipfs/publish.go | 41 +++++++ cmd/ipfs/refs.go | 36 ++++++ cmd/ipfs/resolve.go | 42 +++++++ cmd/ipfs/run.go | 36 ++++++ cmd/ipfs/serve.go | 49 ++++++++ cmd/ipfs/tour.go | 134 ++++++++++++++++++++++ cmd/ipfs/update.go | 62 ++++++++++ cmd/ipfs/version.go | 30 +++++ 29 files changed, 1925 insertions(+) create mode 100644 cmd/ipfs/.gitignore create mode 100644 cmd/ipfs/Makefile create mode 100644 cmd/ipfs/README.md create mode 100644 cmd/ipfs/add.go create mode 100644 cmd/ipfs/block.go create mode 100644 cmd/ipfs/bootstrap.go create mode 100644 cmd/ipfs/cat.go create mode 100644 cmd/ipfs/commands.go create mode 100644 cmd/ipfs/config.go create mode 100644 cmd/ipfs/diag.go create mode 100644 cmd/ipfs/equinox.yaml create mode 100644 cmd/ipfs/gen.go create mode 100644 cmd/ipfs/init.go create mode 100644 cmd/ipfs/ipfs.go create mode 100644 cmd/ipfs/log.go create mode 100644 cmd/ipfs/ls.go create mode 100644 cmd/ipfs/mount_unix.go create mode 100644 cmd/ipfs/mount_windows.go create mode 100644 cmd/ipfs/name.go create mode 100644 cmd/ipfs/objects.go create mode 100644 cmd/ipfs/pin.go create mode 100644 cmd/ipfs/publish.go create mode 100644 cmd/ipfs/refs.go create mode 100644 cmd/ipfs/resolve.go create mode 100644 cmd/ipfs/run.go create mode 100644 cmd/ipfs/serve.go create mode 100644 cmd/ipfs/tour.go create mode 100644 cmd/ipfs/update.go create mode 100644 cmd/ipfs/version.go diff --git a/cmd/ipfs/.gitignore b/cmd/ipfs/.gitignore new file mode 100644 index 000000000..8de552d0c --- /dev/null +++ b/cmd/ipfs/.gitignore @@ -0,0 +1 @@ +./ipfs diff --git a/cmd/ipfs/Makefile b/cmd/ipfs/Makefile new file mode 100644 index 000000000..fccb80330 --- /dev/null +++ b/cmd/ipfs/Makefile @@ -0,0 +1,7 @@ +all: install + +build: + go build + +install: build + go install diff --git a/cmd/ipfs/README.md b/cmd/ipfs/README.md new file mode 100644 index 000000000..0051f68e9 --- /dev/null +++ b/cmd/ipfs/README.md @@ -0,0 +1,29 @@ +# go-ipfs/cmd/ipfs + +This is the ipfs commandline tool. For now, it's the main entry point to using IPFS. Use it. + +``` +> go build +> go install +> ipfs +ipfs - global versioned p2p merkledag file system + +Basic commands: + + add Add an object to ipfs. + cat Show ipfs object data. + ls List links from an object. + refs List link hashes from an object. + +Tool commands: + + config Manage configuration. + version Show ipfs version information. + commands List all available commands. + +Advanced Commands: + + mount Mount an ipfs read-only mountpoint. + +Use "ipfs help " for more information about a command. +``` diff --git a/cmd/ipfs/add.go b/cmd/ipfs/add.go new file mode 100644 index 000000000..a755f699e --- /dev/null +++ b/cmd/ipfs/add.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + "path/filepath" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" + "github.com/jbenet/go-ipfs/core/commands" +) + +// Error indicating the max depth has been exceded. +var ErrDepthLimitExceeded = fmt.Errorf("depth limit exceeded") + +var cmdIpfsAdd = &commander.Command{ + UsageLine: "add", + Short: "Add an object to ipfs.", + Long: `ipfs add ... - Add objects to ipfs. + + Adds contents of to ipfs. Use -r to add directories. + Note that directories are added recursively, to form the ipfs + MerkleDAG. A smarter partial add with a staging area (like git) + remains to be implemented. +`, + Run: addCmd, + Flag: *flag.NewFlagSet("ipfs-add", flag.ExitOnError), +} + +func init() { + cmdIpfsAdd.Flag.Bool("r", false, "add objects recursively") +} + +var addCmd = makeCommand(command{ + name: "add", + args: 1, + flags: []string{"r"}, + cmdFn: commands.Add, + argFilter: filepath.Abs, +}) diff --git a/cmd/ipfs/block.go b/cmd/ipfs/block.go new file mode 100644 index 000000000..2f139d0bc --- /dev/null +++ b/cmd/ipfs/block.go @@ -0,0 +1,58 @@ +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 cmdIpfsBlock = &commander.Command{ + UsageLine: "block", + Short: "manipulate raw ipfs blocks", + Long: `ipfs block - manipulate raw ipfs blocks + + ipfs block get - get and output block named by + ipfs block put - store stdin as a block, outputs + +ipfs block is a plumbing command used to manipulate raw ipfs blocks. +Reads from stdin or writes to stdout, and is a base58 encoded +multihash.`, + // Run: blockGetCmd, + Subcommands: []*commander.Command{ + cmdIpfsBlockGet, + cmdIpfsBlockPut, + }, + Flag: *flag.NewFlagSet("ipfs-block", flag.ExitOnError), +} + +var cmdIpfsBlockGet = &commander.Command{ + UsageLine: "get ", + Short: "get and output block named by ", + Long: `ipfs get - get and output block named by + +ipfs block get is a plumbing command for retreiving raw ipfs blocks. +It outputs to stdout, and is a base58 encoded multihash.`, + Run: makeCommand(command{ + name: "blockGet", + args: 1, + flags: nil, + online: true, + cmdFn: commands.BlockGet, + }), +} + +var cmdIpfsBlockPut = &commander.Command{ + UsageLine: "put", + Short: "store stdin as a block, outputs ", + Long: `ipfs put - store stdin as a block, outputs + +ipfs block put is a plumbing command for storing raw ipfs blocks. +It reads from stding, and is a base58 encoded multihash.`, + Run: makeCommand(command{ + name: "blockPut", + args: 0, + flags: nil, + online: true, + cmdFn: commands.BlockPut, + }), +} diff --git a/cmd/ipfs/bootstrap.go b/cmd/ipfs/bootstrap.go new file mode 100644 index 000000000..6f6a78574 --- /dev/null +++ b/cmd/ipfs/bootstrap.go @@ -0,0 +1,236 @@ +package main + +import ( + "errors" + "strings" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + + config "github.com/jbenet/go-ipfs/config" + peer "github.com/jbenet/go-ipfs/peer" + u "github.com/jbenet/go-ipfs/util" +) + +var cmdIpfsBootstrap = &commander.Command{ + UsageLine: "bootstrap", + Short: "Show a list of bootstrapped addresses.", + Long: `ipfs bootstrap - show, or manipulate bootstrap node addresses + +Running 'ipfs bootstrap' with no arguments will run 'ipfs bootstrap list'. + +Commands: + + list Show the boostrap list. + add
Add a node's address to the bootstrap list. + remove
Remove an address from the bootstrap list. + +` + bootstrapSecurityWarning, + Run: bootstrapListCmd, + Subcommands: []*commander.Command{ + cmdIpfsBootstrapRemove, + cmdIpfsBootstrapAdd, + cmdIpfsBootstrapList, + }, + Flag: *flag.NewFlagSet("ipfs-bootstrap", flag.ExitOnError), +} + +var cmdIpfsBootstrapRemove = &commander.Command{ + UsageLine: "remove
", + Short: "Remove addresses from the bootstrap list.", + Long: `ipfs bootstrap remove - remove addresses from the bootstrap list +` + bootstrapSecurityWarning, + Run: bootstrapRemoveCmd, + Flag: *flag.NewFlagSet("ipfs-bootstrap-remove", flag.ExitOnError), +} + +var cmdIpfsBootstrapAdd = &commander.Command{ + UsageLine: "add
", + Short: "Add addresses to the bootstrap list.", + Long: `ipfs bootstrap add - add addresses to the bootstrap list +` + bootstrapSecurityWarning, + Run: bootstrapAddCmd, + Flag: *flag.NewFlagSet("ipfs-bootstrap-add", flag.ExitOnError), +} + +var cmdIpfsBootstrapList = &commander.Command{ + UsageLine: "list", + Short: "Show addresses in the bootstrap list.", + Run: bootstrapListCmd, + Flag: *flag.NewFlagSet("ipfs-bootstrap-list", flag.ExitOnError), +} + +func bootstrapRemoveCmd(c *commander.Command, inp []string) error { + + if len(inp) == 0 { + return errors.New("remove: no address or peerid specified") + } + + toRemove, err := bootstrapInputToPeers(inp) + if err != nil { + return err + } + + cfg, err := getConfig(c) + if err != nil { + return err + } + + keep := []*config.BootstrapPeer{} + remove := []*config.BootstrapPeer{} + + // function to filer what to keep + shouldKeep := func(bp *config.BootstrapPeer) bool { + for _, skipBP := range toRemove { + + // IDs must match to skip. + if bp.PeerID != skipBP.PeerID { + continue + } + + // if Addresses match, or skipBP has no addr (wildcard) + if skipBP.Address == bp.Address || skipBP.Address == "" { + return false + } + } + return true + } + + // filter all the existing peers + for _, currBP := range cfg.Bootstrap { + if shouldKeep(currBP) { + keep = append(keep, currBP) + } else { + remove = append(remove, currBP) + } + } + + // if didn't remove anyone, bail. + if len(keep) == len(cfg.Bootstrap) { + return errors.New("remove: peer given did not match any in list") + } + + // write new config + cfg.Bootstrap = keep + if err := writeConfig(c, cfg); err != nil { + return err + } + + for _, bp := range remove { + u.POut("removed %s\n", bp) + } + return nil +} + +func bootstrapAddCmd(c *commander.Command, inp []string) error { + + if len(inp) == 0 { + return errors.New("add: no address specified") + } + + toAdd, err := bootstrapInputToPeers(inp) + if err != nil { + return err + } + + cfg, err := getConfig(c) + if err != nil { + return err + } + + // function to check whether a peer is already in the list. + combine := func(lists ...[]*config.BootstrapPeer) []*config.BootstrapPeer { + + set := map[string]struct{}{} + final := []*config.BootstrapPeer{} + + for _, list := range lists { + for _, peer := range list { + // if already in the set, continue + _, found := set[peer.String()] + if found { + continue + } + + set[peer.String()] = struct{}{} + final = append(final, peer) + } + } + return final + } + + // combine both lists, removing dups. + cfg.Bootstrap = combine(cfg.Bootstrap, toAdd) + if err := writeConfig(c, cfg); err != nil { + return err + } + + for _, bp := range toAdd { + u.POut("added %s\n", bp) + } + return nil +} + +func bootstrapListCmd(c *commander.Command, inp []string) error { + + cfg, err := getConfig(c) + if err != nil { + return err + } + + for _, bp := range cfg.Bootstrap { + u.POut("%s\n", bp) + } + + return nil +} + +func bootstrapInputToPeers(input []string) ([]*config.BootstrapPeer, error) { + split := func(addr string) (string, string) { + idx := strings.LastIndex(addr, "/") + if idx == -1 { + return "", addr + } + return addr[:idx], addr[idx+1:] + } + + peers := []*config.BootstrapPeer{} + for _, addr := range input { + addrS, peeridS := split(addr) + + // make sure addrS parses as a multiaddr. + if len(addrS) > 0 { + maddr, err := ma.NewMultiaddr(addrS) + if err != nil { + return nil, err + } + + addrS = maddr.String() + } + + // make sure idS parses as a peer.ID + peerid, err := mh.FromB58String(peeridS) + if err != nil { + return nil, err + } + + // construct config entry + peers = append(peers, &config.BootstrapPeer{ + Address: addrS, + PeerID: peer.ID(peerid).Pretty(), + }) + } + return peers, nil +} + +const bootstrapSecurityWarning = ` +SECURITY WARNING: + +The bootstrap command manipulates the "bootstrap list", which contains +the addresses of bootstrap nodes. These are the *trusted peers* from +which to learn about other peers in the network. Only edit this list +if you understand the risks of adding or removing nodes from this list. + +` diff --git a/cmd/ipfs/cat.go b/cmd/ipfs/cat.go new file mode 100644 index 000000000..168a4841e --- /dev/null +++ b/cmd/ipfs/cat.go @@ -0,0 +1,26 @@ +package main + +import ( + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" + "github.com/jbenet/go-ipfs/core/commands" +) + +var cmdIpfsCat = &commander.Command{ + UsageLine: "cat", + Short: "Show ipfs object data.", + Long: `ipfs cat - Show ipfs object data. + + Retrieves the object named by and displays the Data + it contains. +`, + Run: catCmd, + Flag: *flag.NewFlagSet("ipfs-cat", flag.ExitOnError), +} + +var catCmd = makeCommand(command{ + name: "cat", + args: 1, + flags: nil, + cmdFn: commands.Cat, +}) diff --git a/cmd/ipfs/commands.go b/cmd/ipfs/commands.go new file mode 100644 index 000000000..cc0ed089a --- /dev/null +++ b/cmd/ipfs/commands.go @@ -0,0 +1,74 @@ +package main + +import ( + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" + u "github.com/jbenet/go-ipfs/util" + "strings" + "time" +) + +var cmdIpfsCommands = &commander.Command{ + UsageLine: "commands", + Short: "List all available commands.", + Long: `ipfs commands - List all available commands. + + Lists all available commands (and sub-commands) and exits. + `, + Run: commandsCmd, + Subcommands: []*commander.Command{ + cmdIpfsCommandsHelp, + }, +} + +func commandsCmd(c *commander.Command, args []string) error { + var listCmds func(c *commander.Command) + listCmds = func(c *commander.Command) { + u.POut("%s\n", c.FullSpacedName()) + for _, sc := range c.Subcommands { + listCmds(sc) + } + } + + listCmds(c.Parent) + return nil +} + +var cmdIpfsCommandsHelp = &commander.Command{ + UsageLine: "help", + Short: "List all available commands' help pages.", + Long: `ipfs commands help - List all available commands's help pages. + + Shows the pages of all available commands (and sub-commands) and exits. + Outputs a markdown document. + `, + Run: commandsHelpCmd, +} + +func commandsHelpCmd(c *commander.Command, args []string) error { + u.POut(referenceHeaderMsg) + u.POut("Generated on %s.\n\n", time.Now().UTC().Format("2006-01-02")) + + var printCmds func(*commander.Command, int) + printCmds = func(c *commander.Command, level int) { + u.POut("%s ", strings.Repeat("#", level)) + u.POut("%s\n\n", c.FullSpacedName()) + u.POut("```\n") + u.POut("%s\n", c.Long) + u.POut("```\n\n") + + for _, sc := range c.Subcommands { + printCmds(sc, level+1) + } + } + + printCmds(c.Parent.Parent, 1) + return nil +} + +const referenceHeaderMsg = ` +# ipfs command reference + +This document lists every ipfs command (including subcommands), along with +its help page. It can be viewed by running 'ipfs commands help'. + +` diff --git a/cmd/ipfs/config.go b/cmd/ipfs/config.go new file mode 100644 index 000000000..f1f8be92e --- /dev/null +++ b/cmd/ipfs/config.go @@ -0,0 +1,143 @@ +package main + +import ( + "errors" + "fmt" + "io" + "os" + "os/exec" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" + config "github.com/jbenet/go-ipfs/config" + u "github.com/jbenet/go-ipfs/util" +) + +var cmdIpfsConfig = &commander.Command{ + UsageLine: "config", + Short: "Get/Set ipfs config values", + Long: `ipfs config [] [] - Get/Set ipfs config values. + + ipfs config - Get value of + ipfs config - Set value of to + ipfs config --show - Show config file + ipfs config --edit - Edit config file in $EDITOR + +Examples: + + Get the value of the 'datastore.path' key: + + ipfs config datastore.path + + Set the value of the 'datastore.path' key: + + ipfs config datastore.path ~/.go-ipfs/datastore + +`, + Run: configCmd, + Flag: *flag.NewFlagSet("ipfs-config", flag.ExitOnError), +} + +func init() { + cmdIpfsConfig.Flag.Bool("edit", false, "Edit config file in $EDITOR") + cmdIpfsConfig.Flag.Bool("show", false, "Show config file") +} + +func configCmd(c *commander.Command, inp []string) error { + + confdir, err := getConfigDir(c.Parent) + if err != nil { + return err + } + + filename, err := config.Filename(confdir) + if err != nil { + return err + } + + // if editing, open the editor + if c.Flag.Lookup("edit").Value.Get().(bool) { + return configEditor(filename) + } + + // if showing, cat the file + if c.Flag.Lookup("show").Value.Get().(bool) { + return configCat(filename) + } + + if len(inp) == 0 { + // "ipfs config" run without parameters + u.POut(c.Long) + return nil + } + + // Getter (1 param) + if len(inp) == 1 { + value, err := config.ReadConfigKey(filename, inp[0]) + if err != nil { + return fmt.Errorf("Failed to get config value: %s", err) + } + + strval, ok := value.(string) + if ok { + u.POut("%s\n", strval) + return nil + } + + if err := config.Encode(os.Stdout, value); err != nil { + return fmt.Errorf("Failed to encode config value: %s", err) + } + u.POut("\n") + return nil + } + + // Setter (>1 params) + err = config.WriteConfigKey(filename, inp[0], inp[1]) + if err != nil { + return fmt.Errorf("Failed to set config value: %s", err) + } + + return nil +} + +func configCat(filename string) error { + + file, err := os.Open(filename) + if err != nil { + return err + } + defer file.Close() + + if _, err = io.Copy(os.Stdout, file); err != nil { + return err + } + u.POut("\n") + return nil +} + +func configEditor(filename string) error { + + editor := os.Getenv("EDITOR") + if editor == "" { + return errors.New("ENV variable $EDITOR not set") + } + + cmd := exec.Command("sh", "-c", editor+" "+filename) + cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr + return cmd.Run() +} + +func writeConfig(c *commander.Command, cfg *config.Config) error { + + confdir, err := getConfigDir(c) + if err != nil { + return err + } + + filename, err := config.Filename(confdir) + if err != nil { + return err + } + + return config.WriteConfigFile(filename, cfg) +} diff --git a/cmd/ipfs/diag.go b/cmd/ipfs/diag.go new file mode 100644 index 000000000..657ddae17 --- /dev/null +++ b/cmd/ipfs/diag.go @@ -0,0 +1,32 @@ +package main + +import ( + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" + "github.com/jbenet/go-ipfs/core/commands" +) + +var cmdIpfsDiag = &commander.Command{ + UsageLine: "net-diag", + Short: "Generate a diagnostics report", + Long: `ipfs net-diag - Generate a diagnostics report. + + Sends out a message to each node in the network recursively + requesting a listing of data about them including number of + connected peers and latencies between them. +`, + Run: diagCmd, + Flag: *flag.NewFlagSet("ipfs-net-diag", flag.ExitOnError), +} + +func init() { + cmdIpfsDiag.Flag.Bool("raw", false, "print raw json output") +} + +var diagCmd = makeCommand(command{ + name: "diag", + args: 0, + flags: []string{"raw"}, + cmdFn: commands.Diag, +}) diff --git a/cmd/ipfs/equinox.yaml b/cmd/ipfs/equinox.yaml new file mode 100644 index 000000000..9e764e05c --- /dev/null +++ b/cmd/ipfs/equinox.yaml @@ -0,0 +1,6 @@ +--- +equinox-account: CHANGEME +equinox-secret: CHANGEME +equinox-app: CHANGEME +channel: stable +private-key: equinox-priv diff --git a/cmd/ipfs/gen.go b/cmd/ipfs/gen.go new file mode 100644 index 000000000..e6a957dac --- /dev/null +++ b/cmd/ipfs/gen.go @@ -0,0 +1,72 @@ +package main + +import ( + "fmt" + "os" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" + "github.com/jbenet/go-ipfs/core/commands" + "github.com/jbenet/go-ipfs/daemon" + u "github.com/jbenet/go-ipfs/util" +) + +// command is the descriptor of an ipfs daemon command. +// Used with makeCommand to proxy over commands via the daemon. +type command struct { + name string + args int + flags []string + online bool + cmdFn commands.CmdFunc + argFilter func(string) (string, error) +} + +// commanderFunc is a function that can be passed into the Commander library as +// a command handler. Defined here because commander lacks this definition. +type commanderFunc func(*commander.Command, []string) error + +// makeCommand Wraps a commands.CmdFunc so that it may be safely run by the +// commander library +func makeCommand(cmdDesc command) commanderFunc { + return func(c *commander.Command, inp []string) error { + if len(inp) < cmdDesc.args { + u.POut(c.Long) + return nil + } + confdir, err := getConfigDir(c) + if err != nil { + return err + } + + cmd := daemon.NewCommand() + cmd.Command = cmdDesc.name + if cmdDesc.argFilter != nil { + for _, a := range inp { + s, err := cmdDesc.argFilter(a) + if err != nil { + return err + } + cmd.Args = append(cmd.Args, s) + } + } else { + cmd.Args = inp + } + + for _, a := range cmdDesc.flags { + cmd.Opts[a] = c.Flag.Lookup(a).Value.Get() + } + + err = daemon.SendCommand(cmd, confdir) + if err != nil { + log.Info("Executing command locally: %s", err) + // Do locally + n, err := localNode(confdir, cmdDesc.online) + if err != nil { + return fmt.Errorf("Local node creation failed: %v", err) + } + + return cmdDesc.cmdFn(n, cmd.Args, cmd.Opts, os.Stdout) + } + return nil + } +} diff --git a/cmd/ipfs/init.go b/cmd/ipfs/init.go new file mode 100644 index 000000000..2c4446c58 --- /dev/null +++ b/cmd/ipfs/init.go @@ -0,0 +1,150 @@ +package main + +import ( + "encoding/base64" + "errors" + "os" + "path/filepath" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" + config "github.com/jbenet/go-ipfs/config" + ci "github.com/jbenet/go-ipfs/crypto" + peer "github.com/jbenet/go-ipfs/peer" + updates "github.com/jbenet/go-ipfs/updates" + u "github.com/jbenet/go-ipfs/util" +) + +var cmdIpfsInit = &commander.Command{ + UsageLine: "init", + Short: "Initialize ipfs local configuration", + Long: `ipfs init + + Initializes ipfs configuration files and generates a + new keypair. +`, + Run: initCmd, + Flag: *flag.NewFlagSet("ipfs-init", flag.ExitOnError), +} + +func init() { + cmdIpfsInit.Flag.Int("b", 4096, "number of bits for keypair") + cmdIpfsInit.Flag.String("p", "", "passphrase for encrypting keys") + cmdIpfsInit.Flag.Bool("f", false, "force overwrite of existing config") + cmdIpfsInit.Flag.String("d", "", "Change default datastore location") +} + +func initCmd(c *commander.Command, inp []string) error { + configpath, err := getConfigDir(c.Parent) + if err != nil { + return err + } + + u.POut("initializing ipfs node at %s\n", configpath) + filename, err := config.Filename(configpath) + if err != nil { + return errors.New("Couldn't get home directory path") + } + + dspath, ok := c.Flag.Lookup("d").Value.Get().(string) + if !ok { + return errors.New("failed to parse datastore flag") + } + + fi, err := os.Lstat(filename) + force, ok := c.Flag.Lookup("f").Value.Get().(bool) + if !ok { + return errors.New("failed to parse force flag") + } + if fi != nil || (err != nil && !os.IsNotExist(err)) { + if !force { + return errors.New("ipfs configuration file already exists!\nReinitializing would overwrite your keys.\n(use -f to force overwrite)") + } + } + cfg := new(config.Config) + + cfg.Datastore = config.Datastore{} + if len(dspath) == 0 { + dspath, err = config.DataStorePath("") + if err != nil { + return err + } + } + cfg.Datastore.Path = dspath + cfg.Datastore.Type = "leveldb" + + // Construct the data store if missing + if err := os.MkdirAll(dspath, os.ModePerm); err != nil { + return err + } + + // Check the directory is writeable + if f, err := os.Create(filepath.Join(dspath, "._check_writeable")); err == nil { + os.Remove(f.Name()) + } else { + return errors.New("Datastore '" + dspath + "' is not writeable") + } + + cfg.Identity = config.Identity{} + + // setup the node addresses. + cfg.Addresses = config.Addresses{ + Swarm: "/ip4/0.0.0.0/tcp/4001", + API: "/ip4/127.0.0.1/tcp/5001", + } + + // setup the node mount points. + cfg.Mounts = config.Mounts{ + IPFS: "/ipfs", + IPNS: "/ipns", + } + + nbits, ok := c.Flag.Lookup("b").Value.Get().(int) + if !ok { + return errors.New("failed to get bits flag") + } + if nbits < 1024 { + return errors.New("Bitsize less than 1024 is considered unsafe.") + } + + u.POut("generating key pair\n") + sk, pk, err := ci.GenerateKeyPair(ci.RSA, nbits) + if err != nil { + return err + } + + // currently storing key unencrypted. in the future we need to encrypt it. + // TODO(security) + skbytes, err := sk.Bytes() + if err != nil { + return err + } + cfg.Identity.PrivKey = base64.StdEncoding.EncodeToString(skbytes) + + id, err := peer.IDFromPubKey(pk) + if err != nil { + return err + } + cfg.Identity.PeerID = id.Pretty() + + // Use these hardcoded bootstrap peers for now. + cfg.Bootstrap = []*config.BootstrapPeer{ + &config.BootstrapPeer{ + // mars.i.ipfs.io + PeerID: "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + Address: "/ip4/104.131.131.82/tcp/4001", + }, + } + + // tracking ipfs version used to generate the init folder and adding update checker default setting. + cfg.Version = config.Version{ + Check: "error", + Current: updates.Version, + } + + err = config.WriteConfigFile(filename, cfg) + if err != nil { + return err + } + return nil +} diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go new file mode 100644 index 000000000..9ce5d7faf --- /dev/null +++ b/cmd/ipfs/ipfs.go @@ -0,0 +1,220 @@ +package main + +import ( + "errors" + "fmt" + "os" + "runtime/pprof" + + 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" + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + + config "github.com/jbenet/go-ipfs/config" + core "github.com/jbenet/go-ipfs/core" + daemon "github.com/jbenet/go-ipfs/daemon" + updates "github.com/jbenet/go-ipfs/updates" + u "github.com/jbenet/go-ipfs/util" +) + +// The IPFS command tree. It is an instance of `commander.Command`. +var CmdIpfs = &commander.Command{ + UsageLine: "ipfs [] []", + Short: "global versioned p2p merkledag file system", + Long: `ipfs - global versioned p2p merkledag file system + +Basic commands: + + init Initialize ipfs local configuration. + add Add an object to ipfs. + cat Show ipfs object data. + ls List links from an object. + refs List link hashes from an object. + +Tool commands: + + config Manage configuration. + update Download and apply go-ipfs updates. + version Show ipfs version information. + commands List all available commands. + +Advanced Commands: + + mount Mount an ipfs read-only mountpoint. + serve Serve an interface to ipfs. + net-diag Print network diagnostic + +Plumbing commands: + + block Interact with raw blocks in the datastore + object Interact with raw dag nodes + + +Use "ipfs help " for more information about a command. +`, + Run: ipfsCmd, + Subcommands: []*commander.Command{ + cmdIpfsAdd, + cmdIpfsCat, + cmdIpfsLs, + cmdIpfsRefs, + cmdIpfsConfig, + cmdIpfsVersion, + cmdIpfsCommands, + cmdIpfsMount, + cmdIpfsInit, + cmdIpfsServe, + cmdIpfsRun, + cmdIpfsName, + cmdIpfsBootstrap, + cmdIpfsDiag, + cmdIpfsBlock, + cmdIpfsObject, + cmdIpfsUpdate, + cmdIpfsLog, + cmdIpfsPin, + cmdIpfsTour, + }, + Flag: *flag.NewFlagSet("ipfs", flag.ExitOnError), +} + +// log is the command logger +var log = u.Logger("cmd/ipfs") + +func init() { + config, err := config.PathRoot() + if err != nil { + fmt.Fprintln(os.Stderr, "Failure initializing the default Config Directory: ", err) + os.Exit(1) + } + CmdIpfs.Flag.String("c", config, "specify config directory") +} + +func ipfsCmd(c *commander.Command, args []string) error { + u.POut(c.Long) + return nil +} + +func main() { + // if debugging, setup profiling. + if u.Debug { + ofi, err := os.Create("cpu.prof") + if err != nil { + fmt.Println(err) + return + } + pprof.StartCPUProfile(ofi) + defer ofi.Close() + defer pprof.StopCPUProfile() + } + + err := CmdIpfs.Dispatch(os.Args[1:]) + if err != nil { + if len(err.Error()) > 0 { + fmt.Fprintf(os.Stderr, "ipfs %s: %v\n", os.Args[1], err) + } + os.Exit(1) + } + return +} + +// localNode constructs a node +func localNode(confdir string, online bool) (*core.IpfsNode, error) { + filename, err := config.Filename(confdir) + if err != nil { + return nil, err + } + + cfg, err := config.Load(filename) + if err != nil { + return nil, err + } + + if err := updates.CliCheckForUpdates(cfg, filename); err != nil { + return nil, err + } + + return core.NewIpfsNode(cfg, online) +} + +// Gets the config "-c" flag from the command, or returns +// the default configuration root directory +func getConfigDir(c *commander.Command) (string, error) { + + // use the root cmd (that's where config is specified) + for ; c.Parent != nil; c = c.Parent { + } + + // flag should be defined on root. + param := c.Flag.Lookup("c").Value.Get().(string) + if param != "" { + return u.TildeExpansion(param) + } + + return config.PathRoot() +} + +func getConfig(c *commander.Command) (*config.Config, error) { + confdir, err := getConfigDir(c) + if err != nil { + return nil, err + } + + filename, err := config.Filename(confdir) + if err != nil { + return nil, err + } + + return config.Load(filename) +} + +// cmdContext is a wrapper structure that keeps a node, a daemonlistener, and +// a config directory together. These three are needed for most commands. +type cmdContext struct { + node *core.IpfsNode + daemon *daemon.DaemonListener + configDir string +} + +// setupCmdContext initializes a cmdContext structure from a given command. +func setupCmdContext(c *commander.Command, online bool) (cc cmdContext, err error) { + rootCmd := c + for ; rootCmd.Parent != nil; rootCmd = rootCmd.Parent { + } + + cc.configDir, err = getConfigDir(rootCmd) + if err != nil { + return + } + + cc.node, err = localNode(cc.configDir, online) + if err != nil { + return + } + + cc.daemon, err = setupDaemon(cc.configDir, cc.node) + if err != nil { + return + } + + return +} + +// setupDaemon sets up the daemon corresponding to given node. +func setupDaemon(confdir string, node *core.IpfsNode) (*daemon.DaemonListener, error) { + if node.Config.Addresses.API == "" { + return nil, errors.New("no config.Addresses.API endpoint supplied") + } + + maddr, err := ma.NewMultiaddr(node.Config.Addresses.API) + if err != nil { + return nil, err + } + + dl, err := daemon.NewDaemonListener(node, maddr, confdir) + if err != nil { + return nil, err + } + go dl.Listen() + return dl, nil +} diff --git a/cmd/ipfs/log.go b/cmd/ipfs/log.go new file mode 100644 index 000000000..255b7c263 --- /dev/null +++ b/cmd/ipfs/log.go @@ -0,0 +1,29 @@ +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 cmdIpfsLog = &commander.Command{ + UsageLine: "log ", + Short: "switch logging levels of a running daemon", + Long: `ipfs log - switch logging levels of a running daemon + + is a the subsystem logging identifier. Use * for all subsystems. + is one of: debug, info, notice, warning, error, critical + +ipfs log is a utility command used to change the logging output of a running daemon. +`, + Run: logCmd, + Flag: *flag.NewFlagSet("ipfs-log", flag.ExitOnError), +} + +var logCmd = makeCommand(command{ + name: "log", + args: 2, + flags: nil, + online: true, + cmdFn: commands.Log, +}) diff --git a/cmd/ipfs/ls.go b/cmd/ipfs/ls.go new file mode 100644 index 000000000..0a2e8aff9 --- /dev/null +++ b/cmd/ipfs/ls.go @@ -0,0 +1,29 @@ +package main + +import ( + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" + "github.com/jbenet/go-ipfs/core/commands" +) + +var cmdIpfsLs = &commander.Command{ + UsageLine: "ls", + Short: "List links from an object.", + Long: `ipfs ls - List links from an object. + + Retrieves the object named by and displays the links + it contains, with the following format: + + + +`, + Run: lsCmd, + Flag: *flag.NewFlagSet("ipfs-ls", flag.ExitOnError), +} + +var lsCmd = makeCommand(command{ + name: "ls", + args: 1, + flags: nil, + cmdFn: commands.Ls, +}) diff --git a/cmd/ipfs/mount_unix.go b/cmd/ipfs/mount_unix.go new file mode 100644 index 000000000..84fbc9cf6 --- /dev/null +++ b/cmd/ipfs/mount_unix.go @@ -0,0 +1,94 @@ +// +build linux darwin freebsd + +package main + +import ( + "fmt" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" + + core "github.com/jbenet/go-ipfs/core" + ipns "github.com/jbenet/go-ipfs/fuse/ipns" + rofs "github.com/jbenet/go-ipfs/fuse/readonly" +) + +var cmdIpfsMount = &commander.Command{ + UsageLine: "mount", + Short: "Mount an ipfs read-only mountpoint.", + Long: `ipfs mount - Mount an ipfs read-only mountpoint. + + Mount ipfs at a read-only mountpoint on the OS. All ipfs objects + will be accessible under that directory. Note that the root will + not be listable, as it is virtual. Accessing known paths directly. + +`, + Run: mountCmd, + Flag: *flag.NewFlagSet("ipfs-mount", flag.ExitOnError), +} + +func init() { + cmdIpfsMount.Flag.String("f", "", "specify a mountpoint for ipfs") + cmdIpfsMount.Flag.String("n", "", "specify a mountpoint for ipns") +} + +func mountCmd(c *commander.Command, inp []string) error { + + cc, err := setupCmdContext(c, true) + if err != nil { + return err + } + defer cc.daemon.Close() + + // update fsdir with flag. + fsdir := cc.node.Config.Mounts.IPFS + if val, ok := c.Flag.Lookup("f").Value.Get().(string); ok && val != "" { + fsdir = val + } + fsdone := mountIpfs(cc.node, fsdir) + + // get default mount points + nsdir := cc.node.Config.Mounts.IPNS + if val, ok := c.Flag.Lookup("n").Value.Get().(string); ok && val != "" { + nsdir = val + } + nsdone := mountIpns(cc.node, nsdir, fsdir) + + // wait till mounts are done. + err1 := <-fsdone + err2 := <-nsdone + + if err1 != nil { + return err1 + } + return err2 +} + +func mountIpfs(node *core.IpfsNode, fsdir string) <-chan error { + done := make(chan error) + fmt.Printf("mounting ipfs at %s\n", fsdir) + + go func() { + err := rofs.Mount(node, fsdir) + done <- err + close(done) + }() + + return done +} + +func mountIpns(node *core.IpfsNode, nsdir, fsdir string) <-chan error { + if nsdir == "" { + return nil + } + done := make(chan error) + fmt.Printf("mounting ipns at %s\n", nsdir) + + go func() { + err := ipns.Mount(node, nsdir, fsdir) + done <- err + close(done) + }() + + return done +} diff --git a/cmd/ipfs/mount_windows.go b/cmd/ipfs/mount_windows.go new file mode 100644 index 000000000..aabf4b4ff --- /dev/null +++ b/cmd/ipfs/mount_windows.go @@ -0,0 +1,19 @@ +package main + +import ( + "errors" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" +) + +var cmdIpfsMount = &commander.Command{ + UsageLine: "mount", + Short: "Mount an ipfs read-only mountpoint.", + Long: `Not yet implemented on windows.`, + Run: mountCmd, + Flag: *flag.NewFlagSet("ipfs-mount", flag.ExitOnError), +} + +func mountCmd(c *commander.Command, inp []string) error { + return errors.New("mount not yet implemented on windows") +} diff --git a/cmd/ipfs/name.go b/cmd/ipfs/name.go new file mode 100644 index 000000000..7021fe612 --- /dev/null +++ b/cmd/ipfs/name.go @@ -0,0 +1,57 @@ +package main + +import ( + "fmt" + + 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" +) + +var cmdIpfsName = &commander.Command{ + UsageLine: "name [publish | resolve]", + Short: "ipfs namespace (ipns) tool", + Long: `ipfs name - Get/Set ipfs config values. + + ipfs name publish [] - Assign the to + ipfs name resolve [] - Resolve the value of + +IPNS is a PKI namespace, where names are the hashes of public keys, and +the private key enables publishing new (signed) values. In both publish +and resolve, the default value of is your own identity public key. + + +Examples: + +Publish a to your identity name: + + > ipfs name publish QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +Publish a to another public key: + + > ipfs name publish QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +Resolve the value of your identity: + + > ipfs name resolve + QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +Resolve te value of another name: + + > ipfs name resolve QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n + QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +`, + Run: nameCmd, + Flag: *flag.NewFlagSet("ipfs-name", flag.ExitOnError), + Subcommands: []*commander.Command{ + cmdIpfsPub, + cmdIpfsResolve, + }, +} + +func nameCmd(c *commander.Command, args []string) error { + fmt.Println(c.Long) + return nil +} diff --git a/cmd/ipfs/objects.go b/cmd/ipfs/objects.go new file mode 100644 index 000000000..c2092ebf0 --- /dev/null +++ b/cmd/ipfs/objects.go @@ -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 - return the data for this key as raw bytes + ipfs object links - lists (the keys of ?) the links this key points to + ipfs object get - 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. +- 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 ", + Short: "data outputs the raw bytes named by ", + Long: `ipfs data - data outputs the raw bytes named by + +ipfs data is a plumbing command for retreiving the raw bytes stored in a dag node. +It outputs to stdout, and 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 ", + Short: "outputs the links pointed to by ", + Long: `ipfs links - outputs the links pointed to by + +ipfs block get is a plumbing command for retreiving raw ipfs blocks. +It outputs to stdout, and 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 ", + Short: "get and serialize the dag node named by ", + Long: `ipfs get - get and output the dag node named by + +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 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 ", + Long: `ipfs put - store stdin as a dag object, outputs + +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 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, + }), +} diff --git a/cmd/ipfs/pin.go b/cmd/ipfs/pin.go new file mode 100644 index 000000000..5b331f190 --- /dev/null +++ b/cmd/ipfs/pin.go @@ -0,0 +1,62 @@ +package main + +import ( + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" + "github.com/jbenet/go-ipfs/core/commands" +) + +var cmdIpfsPin = &commander.Command{ + UsageLine: "pin", + Short: "", + Long: `ipfs pin [add|rm] - object pinning commands +`, + Subcommands: []*commander.Command{ + cmdIpfsSubPin, + cmdIpfsSubUnpin, + }, +} + +var cmdIpfsSubPin = &commander.Command{ + UsageLine: "add", + Short: "pin an ipfs object to local storage.", + Long: `ipfs pin add - pin ipfs object to local storage. + + Retrieves the object named by and stores it locally + on disk. +`, + Run: pinSubCmd, + Flag: *flag.NewFlagSet("ipfs-pin", flag.ExitOnError), +} + +var pinSubCmd = makeCommand(command{ + name: "pin", + args: 1, + flags: []string{"r", "d"}, + cmdFn: commands.Pin, +}) + +var cmdIpfsSubUnpin = &commander.Command{ + UsageLine: "rm", + Short: "unpin an ipfs object from local storage.", + Long: `ipfs pin rm - unpin ipfs object from local storage. + + Removes the pin from the given object allowing it to be garbage + collected if needed. +`, + Run: unpinSubCmd, + Flag: *flag.NewFlagSet("ipfs-unpin", flag.ExitOnError), +} + +var unpinSubCmd = makeCommand(command{ + name: "unpin", + args: 1, + flags: []string{"r"}, + cmdFn: commands.Unpin, +}) + +func init() { + cmdIpfsSubPin.Flag.Bool("r", false, "pin objects recursively") + cmdIpfsSubPin.Flag.Int("d", 1, "recursive depth") + cmdIpfsSubUnpin.Flag.Bool("r", false, "unpin objects recursively") +} diff --git a/cmd/ipfs/publish.go b/cmd/ipfs/publish.go new file mode 100644 index 000000000..041da0028 --- /dev/null +++ b/cmd/ipfs/publish.go @@ -0,0 +1,41 @@ +package main + +import ( + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" + "github.com/jbenet/go-ipfs/core/commands" +) + +var cmdIpfsPub = &commander.Command{ + UsageLine: "publish", + Short: "publish a to ipns.", + Long: `ipfs publish [] - publish a to ipns. + +IPNS is a PKI namespace, where names are the hashes of public keys, and +the private key enables publishing new (signed) values. In publish, the +default value of is your own identity public key. + +Examples: + +Publish a to your identity name: + + > ipfs name publish QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +Publish a to another public key: + + > ipfs name publish QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +`, + Run: pubCmd, + Flag: *flag.NewFlagSet("ipfs-publish", flag.ExitOnError), +} + +var pubCmd = makeCommand(command{ + name: "publish", + args: 1, + flags: nil, + online: true, + cmdFn: commands.Publish, +}) diff --git a/cmd/ipfs/refs.go b/cmd/ipfs/refs.go new file mode 100644 index 000000000..b3aaf85fc --- /dev/null +++ b/cmd/ipfs/refs.go @@ -0,0 +1,36 @@ +package main + +import ( + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" + commands "github.com/jbenet/go-ipfs/core/commands" +) + +var cmdIpfsRefs = &commander.Command{ + UsageLine: "refs", + Short: "List link hashes from an object.", + Long: `ipfs refs - List link hashes from an object.. + + Retrieves the object named by and displays the link + hashes it contains, with the following format: + + + + Note: list all refs recursively with -r. + +`, + Run: refCmd, + Flag: *flag.NewFlagSet("ipfs-refs", flag.ExitOnError), +} + +func init() { + cmdIpfsRefs.Flag.Bool("r", false, "recursive: list refs recursively") + cmdIpfsRefs.Flag.Bool("u", false, "unique: list each ref only once") +} + +var refCmd = makeCommand(command{ + name: "refs", + args: 1, + flags: []string{"r", "u"}, + cmdFn: commands.Refs, +}) diff --git a/cmd/ipfs/resolve.go b/cmd/ipfs/resolve.go new file mode 100644 index 000000000..9f5107ff8 --- /dev/null +++ b/cmd/ipfs/resolve.go @@ -0,0 +1,42 @@ +package main + +import ( + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" + "github.com/jbenet/go-ipfs/core/commands" +) + +var cmdIpfsResolve = &commander.Command{ + UsageLine: "resolve", + Short: "resolve an ipns name to a ", + Long: `ipfs resolve [] - Resolve an ipns name to a . + +IPNS is a PKI namespace, where names are the hashes of public keys, and +the private key enables publishing new (signed) values. In resolve, the +default value of is your own identity public key. + + +Examples: + +Resolve the value of your identity: + + > ipfs name resolve + QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +Resolve te value of another name: + + > ipfs name resolve QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n + QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +`, + Run: resolveCmd, + Flag: *flag.NewFlagSet("ipfs-resolve", flag.ExitOnError), +} + +var resolveCmd = makeCommand(command{ + name: "resolve", + args: 0, + flags: nil, + online: true, + cmdFn: commands.Resolve, +}) diff --git a/cmd/ipfs/run.go b/cmd/ipfs/run.go new file mode 100644 index 000000000..b651ca9d9 --- /dev/null +++ b/cmd/ipfs/run.go @@ -0,0 +1,36 @@ +package main + +import ( + "os" + "os/signal" + "syscall" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" +) + +var cmdIpfsRun = &commander.Command{ + UsageLine: "run", + Short: "run local ifps node.", + Long: `run a local ipfs node with no other interface. +`, + Run: runCmd, + Flag: *flag.NewFlagSet("ipfs-run", flag.ExitOnError), +} + +func runCmd(c *commander.Command, inp []string) error { + cc, err := setupCmdContext(c, true) + if err != nil { + return err + } + + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, syscall.SIGHUP, syscall.SIGINT, + syscall.SIGTERM, syscall.SIGQUIT) + + // wait until we get a signal to exit. + <-sigc + + cc.daemon.Close() + return nil +} diff --git a/cmd/ipfs/serve.go b/cmd/ipfs/serve.go new file mode 100644 index 000000000..fda3b3f22 --- /dev/null +++ b/cmd/ipfs/serve.go @@ -0,0 +1,49 @@ +package main + +import ( + "fmt" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + + h "github.com/jbenet/go-ipfs/server/http" +) + +var cmdIpfsServe = &commander.Command{ + UsageLine: "serve", + Short: "Serve an interface to ipfs", + Subcommands: []*commander.Command{ + cmdIpfsServeHTTP, + }, + Flag: *flag.NewFlagSet("ipfs-serve", flag.ExitOnError), +} + +var cmdIpfsServeHTTP = &commander.Command{ + UsageLine: "http", + Short: "Serve an HTTP API", + Long: `ipfs serve http - Serve an http gateway into ipfs.`, + Run: serveHTTPCmd, + Flag: *flag.NewFlagSet("ipfs-serve-http", flag.ExitOnError), +} + +func init() { + cmdIpfsServeHTTP.Flag.String("address", "/ip4/127.0.0.1/tcp/8080", "Listen Address") +} + +func serveHTTPCmd(c *commander.Command, _ []string) error { + cc, err := setupCmdContext(c, true) + if err != nil { + return err + } + defer cc.daemon.Close() + + address := c.Flag.Lookup("address").Value.Get().(string) + maddr, err := ma.NewMultiaddr(address) + if err != nil { + return err + } + + fmt.Printf("Serving on %s\n", address) + return h.Serve(maddr, cc.node) +} diff --git a/cmd/ipfs/tour.go b/cmd/ipfs/tour.go new file mode 100644 index 000000000..0656625e8 --- /dev/null +++ b/cmd/ipfs/tour.go @@ -0,0 +1,134 @@ +// +build linux darwin freebsd + +package main + +import ( + "fmt" + + config "github.com/jbenet/go-ipfs/config" + tour "github.com/jbenet/go-ipfs/tour" + + commander "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" +) + +var cmdIpfsTour = &commander.Command{ + UsageLine: "tour []", + Short: "Take the IPFS Tour.", + Long: `ipfs tour - Take the IPFS Tour. + + ipfs tour [] - Show tour topic. Default to current. + ipfs tour next - Show the next tour topic. + ipfs tour list - Show a list of topics. + ipfs tour restart - Restart the tour. + +This is a tour that takes you through various IPFS concepts, +features, and tools to make sure you get up to speed with +IPFS very quickly. To start, run: + + ipfs tour +`, + Run: tourCmd, + Subcommands: []*commander.Command{ + cmdIpfsTourNext, + cmdIpfsTourList, + cmdIpfsTourRestart, + }, +} + +var cmdIpfsTourNext = &commander.Command{ + UsageLine: "next", + Short: "Show the next IPFS Tour topic.", + Run: tourNextCmd, +} + +var cmdIpfsTourList = &commander.Command{ + UsageLine: "list", + Short: "Show a list of IPFS Tour topics.", + Run: tourListCmd, +} + +var cmdIpfsTourRestart = &commander.Command{ + UsageLine: "restart", + Short: "Restart the IPFS Tour.", + Run: tourRestartCmd, +} + +func tourCmd(c *commander.Command, inp []string) error { + cfg, err := getConfig(c) + if err != nil { + return err + } + + topic := tour.TopicID(cfg.Tour.Last) + if len(inp) > 0 { + topic = tour.TopicID(inp[0]) + } + return tourShow(topic) +} + +func tourNextCmd(c *commander.Command, _ []string) error { + cfg, err := getConfig(c) + if err != nil { + return err + } + + topic := tour.NextTopic(tour.TopicID(cfg.Tour.Last)) + if err := tourShow(topic); err != nil { + return err + } + + // if topic didn't change (last) done + if string(topic) == cfg.Tour.Last { + return nil + } + + // topic changed, not last. write it out. + cfg.Tour.Last = string(topic) + return writeConfig(c, cfg) +} + +func tourListCmd(c *commander.Command, _ []string) error { + cfg, err := getConfig(c) + if err != nil { + return err + } + lastid := tour.TopicID(cfg.Tour.Last) + + for _, id := range tour.IDs { + c := ' ' + switch { + case id == lastid: + c = '*' + case id.LessThan(lastid): + c = '✓' + } + + t := tour.Topics[id] + fmt.Printf("- %c %-5.5s %s\n", c, id, t.Title) + } + return nil +} + +func tourRestartCmd(c *commander.Command, _ []string) error { + cfg, err := getConfig(c) + if err != nil { + return err + } + + cfg.Tour.Last = "" + return writeConfig(c, cfg) +} + +func tourShow(id tour.ID) error { + t, found := tour.Topics[id] + if !found { + return fmt.Errorf("no topic with id: %s", id) + } + + fmt.Printf("Tour %s - %s\n\n%s\n", t.ID, t.Title, t.Text) + return nil +} + +func lastTour(cfg *config.Config) string { + return "" +} diff --git a/cmd/ipfs/update.go b/cmd/ipfs/update.go new file mode 100644 index 000000000..e6d078253 --- /dev/null +++ b/cmd/ipfs/update.go @@ -0,0 +1,62 @@ +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" +) + +func init() { + cmdIpfsUpdate.Flag.Bool("force", false, "force shutdown of daemon when updating") +} + +var cmdIpfsUpdate = &commander.Command{ + UsageLine: "update", + Short: "check for updates and apply them", + Long: `ipfs update - check for updates and apply them + + ipfs update - apply + ipfs update check - just check + ipfs update log - list the changelogs + +ipfs update is a utility command used to check for updates and apply them. +I wont even try, @jbenet. You do this much better :)`, + Run: makeCommand(command{ + name: "updateApply", + args: 0, + flags: []string{"force"}, + online: true, + cmdFn: commands.UpdateApply, + }), + Subcommands: []*commander.Command{ + cmdIpfsUpdateCheck, + cmdIpfsUpdateLog, + }, + Flag: *flag.NewFlagSet("ipfs-update", flag.ExitOnError), +} + +var cmdIpfsUpdateCheck = &commander.Command{ + UsageLine: "check", + Short: "", + Long: `ipfs update check `, + Run: makeCommand(command{ + name: "updateCheck", + args: 0, + flags: nil, + online: false, + cmdFn: commands.UpdateCheck, + }), +} + +var cmdIpfsUpdateLog = &commander.Command{ + UsageLine: "log", + Short: "list the last versions and their changelog", + Long: `ipfs updage log - list the last versions and their changelog`, + Run: makeCommand(command{ + name: "updateLog", + args: 0, + flags: nil, + online: false, + cmdFn: commands.UpdateCheck, + }), +} diff --git a/cmd/ipfs/version.go b/cmd/ipfs/version.go new file mode 100644 index 000000000..1c02bf69f --- /dev/null +++ b/cmd/ipfs/version.go @@ -0,0 +1,30 @@ +package main + +import ( + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" + updates "github.com/jbenet/go-ipfs/updates" + u "github.com/jbenet/go-ipfs/util" +) + +var cmdIpfsVersion = &commander.Command{ + UsageLine: "version", + Short: "Show ipfs version information.", + Long: `ipfs version - Show ipfs version information. + + Returns the current version of ipfs and exits. + `, + Run: versionCmd, +} + +func init() { + cmdIpfsVersion.Flag.Bool("number", false, "show only the number") +} + +func versionCmd(c *commander.Command, _ []string) error { + number := c.Flag.Lookup("number").Value.Get().(bool) + if !number { + u.POut("ipfs version ") + } + u.POut("%s\n", updates.Version) + return nil +} From a5a7490b7731d1dd2f329c59e6ea1b0e753bb787 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 29 Oct 2014 00:24:44 -0700 Subject: [PATCH 003/383] move daemon temporarily --- {daemon => daemon-original}/daemon.go | 0 {daemon => daemon-original}/daemon_client.go | 0 {daemon => daemon-original}/daemon_test.go | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {daemon => daemon-original}/daemon.go (100%) rename {daemon => daemon-original}/daemon_client.go (100%) rename {daemon => daemon-original}/daemon_test.go (100%) diff --git a/daemon/daemon.go b/daemon-original/daemon.go similarity index 100% rename from daemon/daemon.go rename to daemon-original/daemon.go diff --git a/daemon/daemon_client.go b/daemon-original/daemon_client.go similarity index 100% rename from daemon/daemon_client.go rename to daemon-original/daemon_client.go diff --git a/daemon/daemon_test.go b/daemon-original/daemon_test.go similarity index 100% rename from daemon/daemon_test.go rename to daemon-original/daemon_test.go From eff47794aae13f2a9324ebb89aa8c445b9e92403 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 29 Oct 2014 00:25:07 -0700 Subject: [PATCH 004/383] add a copy of daemon to be edited when creating new commands --- daemon/daemon.go | 166 ++++++++++++++++++++++++++++++++++++++++ daemon/daemon_client.go | 107 ++++++++++++++++++++++++++ daemon/daemon_test.go | 86 +++++++++++++++++++++ 3 files changed, 359 insertions(+) create mode 100644 daemon/daemon.go create mode 100644 daemon/daemon_client.go create mode 100644 daemon/daemon_test.go diff --git a/daemon/daemon.go b/daemon/daemon.go new file mode 100644 index 000000000..c8c150a8d --- /dev/null +++ b/daemon/daemon.go @@ -0,0 +1,166 @@ +package daemon + +import ( + "encoding/json" + "fmt" + "io" + "os" + "path" + "sync" + + core "github.com/jbenet/go-ipfs/core" + "github.com/jbenet/go-ipfs/core/commands" + u "github.com/jbenet/go-ipfs/util" + + lock "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/camlistore/lock" + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" +) + +var log = u.Logger("daemon") + +// LockFile is the filename of the daemon lock, relative to config dir +const LockFile = "daemon.lock" + +// DaemonListener listens to an initialized IPFS node and can send it commands instead of +// starting up a new set of connections +type DaemonListener struct { + node *core.IpfsNode + list manet.Listener + closed bool + wg sync.WaitGroup + lk io.Closer +} + +//Command accepts user input and can be sent to the running IPFS node +type Command struct { + Command string + Args []string + Opts map[string]interface{} +} + +func NewDaemonListener(ipfsnode *core.IpfsNode, addr ma.Multiaddr, confdir string) (*DaemonListener, error) { + var err error + confdir, err = u.TildeExpansion(confdir) + if err != nil { + return nil, err + } + + lk, err := daemonLock(confdir) + if err != nil { + return nil, err + } + + ofi, err := os.Create(confdir + "/rpcaddress") + if err != nil { + log.Warningf("Could not create rpcaddress file: %s", err) + return nil, err + } + + _, err = ofi.Write([]byte(addr.String())) + if err != nil { + log.Warningf("Could not write to rpcaddress file: %s", err) + return nil, err + } + ofi.Close() + + list, err := manet.Listen(addr) + if err != nil { + return nil, err + } + log.Info("New daemon listener initialized.") + + return &DaemonListener{ + node: ipfsnode, + list: list, + lk: lk, + }, nil +} + +func NewCommand() *Command { + return &Command{ + Opts: make(map[string]interface{}), + } +} + +func (dl *DaemonListener) Listen() { + if dl.closed { + panic("attempting to listen on a closed daemon Listener") + } + + // add ourselves to workgroup. and remove ourselves when done. + dl.wg.Add(1) + defer dl.wg.Done() + + log.Info("daemon listening") + for { + conn, err := dl.list.Accept() + if err != nil { + if !dl.closed { + log.Warning("DaemonListener Accept: %v", err) + } + return + } + go dl.handleConnection(conn) + } +} + +func (dl *DaemonListener) handleConnection(conn manet.Conn) { + defer conn.Close() + + dec := json.NewDecoder(conn) + + var command Command + err := dec.Decode(&command) + if err != nil { + fmt.Fprintln(conn, err) + return + } + + log.Debug("Got command: %v", command) + switch command.Command { + case "add": + err = commands.Add(dl.node, command.Args, command.Opts, conn) + case "cat": + err = commands.Cat(dl.node, command.Args, command.Opts, conn) + case "ls": + err = commands.Ls(dl.node, command.Args, command.Opts, conn) + case "pin": + err = commands.Pin(dl.node, command.Args, command.Opts, conn) + case "publish": + err = commands.Publish(dl.node, command.Args, command.Opts, conn) + case "resolve": + err = commands.Resolve(dl.node, command.Args, command.Opts, conn) + case "diag": + err = commands.Diag(dl.node, command.Args, command.Opts, conn) + case "blockGet": + err = commands.BlockGet(dl.node, command.Args, command.Opts, conn) + case "blockPut": + err = commands.BlockPut(dl.node, command.Args, command.Opts, conn) + case "log": + err = commands.Log(dl.node, command.Args, command.Opts, conn) + case "unpin": + err = commands.Unpin(dl.node, command.Args, command.Opts, conn) + case "updateApply": + command.Opts["onDaemon"] = true + err = commands.UpdateApply(dl.node, command.Args, command.Opts, conn) + default: + err = fmt.Errorf("Invalid Command: '%s'", command.Command) + } + if err != nil { + log.Errorf("%s: %s", command.Command, err) + fmt.Fprintln(conn, err) + } +} + +func (dl *DaemonListener) Close() error { + dl.closed = true + err := dl.list.Close() + dl.wg.Wait() // wait till done before releasing lock. + dl.lk.Close() + return err +} + +func daemonLock(confdir string) (io.Closer, error) { + return lock.Lock(path.Join(confdir, LockFile)) +} diff --git a/daemon/daemon_client.go b/daemon/daemon_client.go new file mode 100644 index 000000000..031d439c4 --- /dev/null +++ b/daemon/daemon_client.go @@ -0,0 +1,107 @@ +package daemon + +import ( + "bufio" + "encoding/json" + "errors" + "io" + "os" + + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" + + u "github.com/jbenet/go-ipfs/util" +) + +// ErrDaemonNotRunning is returned when attempting to retrieve the daemon's +// address and the daemon is not actually running. +var ErrDaemonNotRunning = errors.New("daemon not running") + +func getDaemonAddr(confdir string) (string, error) { + var err error + confdir, err = u.TildeExpansion(confdir) + if err != nil { + return "", err + } + fi, err := os.Open(confdir + "/rpcaddress") + if err != nil { + log.Debug("getDaemonAddr failed: %s", err) + if err == os.ErrNotExist { + return "", ErrDaemonNotRunning + } + return "", err + } + + read := bufio.NewReader(fi) + + // TODO: operating system agostic line delim + line, err := read.ReadBytes('\n') + if err != nil && err != io.EOF { + return "", err + } + return string(line), nil +} + +// SendCommand attempts to run the command over a currently-running daemon. +// If there is no running daemon, returns ErrDaemonNotRunning. This is done +// over network RPC API. The address of the daemon is retrieved from the config +// directory, where live daemons write their addresses to special files. +func SendCommand(command *Command, confdir string) error { + server := os.Getenv("IPFS_ADDRESS_RPC") + + if server == "" { + //check if daemon is running + log.Info("Checking if daemon is running...") + if !serverIsRunning(confdir) { + return ErrDaemonNotRunning + } + + log.Info("Daemon is running!") + + var err error + server, err = getDaemonAddr(confdir) + if err != nil { + return err + } + } + + return serverComm(server, command) +} + +func serverIsRunning(confdir string) bool { + var err error + confdir, err = u.TildeExpansion(confdir) + if err != nil { + log.Errorf("Tilde Expansion Failed: %s", err) + return false + } + lk, err := daemonLock(confdir) + if err == nil { + lk.Close() + return false + } + return true +} + +func serverComm(server string, command *Command) error { + log.Info("Daemon address: %s", server) + maddr, err := ma.NewMultiaddr(server) + if err != nil { + return err + } + + conn, err := manet.Dial(maddr) + if err != nil { + return err + } + + enc := json.NewEncoder(conn) + err = enc.Encode(command) + if err != nil { + return err + } + + io.Copy(os.Stdout, conn) + + return nil +} diff --git a/daemon/daemon_test.go b/daemon/daemon_test.go new file mode 100644 index 000000000..7fba74269 --- /dev/null +++ b/daemon/daemon_test.go @@ -0,0 +1,86 @@ +package daemon + +import ( + "encoding/base64" + "os" + "testing" + + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + config "github.com/jbenet/go-ipfs/config" + core "github.com/jbenet/go-ipfs/core" + ci "github.com/jbenet/go-ipfs/crypto" + peer "github.com/jbenet/go-ipfs/peer" +) + +func TestInitializeDaemonListener(t *testing.T) { + + priv, pub, err := ci.GenerateKeyPair(ci.RSA, 512) + if err != nil { + t.Fatal(err) + } + prbytes, err := priv.Bytes() + if err != nil { + t.Fatal(err) + } + + ident, _ := peer.IDFromPubKey(pub) + privKey := base64.StdEncoding.EncodeToString(prbytes) + pID := ident.Pretty() + + id := config.Identity{ + PeerID: pID, + PrivKey: privKey, + } + + nodeConfigs := []*config.Config{ + &config.Config{ + Identity: id, + Datastore: config.Datastore{ + Type: "memory", + }, + Addresses: config.Addresses{ + Swarm: "/ip4/0.0.0.0/tcp/4001", + API: "/ip4/127.0.0.1/tcp/8000", + }, + }, + + &config.Config{ + Identity: id, + Datastore: config.Datastore{ + Type: "leveldb", + Path: ".test/datastore", + }, + Addresses: config.Addresses{ + Swarm: "/ip4/0.0.0.0/tcp/4001", + API: "/ip4/127.0.0.1/tcp/8000", + }, + }, + } + + var tempConfigDir = ".test" + err = os.MkdirAll(tempConfigDir, os.ModeDir|0777) + if err != nil { + t.Fatalf("error making temp config dir: %v", err) + } + + for _, c := range nodeConfigs { + + node, _ := core.NewIpfsNode(c, false) + addr, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1327") + if err != nil { + t.Fatal(err) + } + + dl, initErr := NewDaemonListener(node, addr, tempConfigDir) + if initErr != nil { + t.Fatal(initErr) + } + + closeErr := dl.Close() + if closeErr != nil { + t.Fatal(closeErr) + } + + } + +} From e64ffb9ab3fd6562b4c3d165f370869f7713c6e7 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 22 Oct 2014 15:25:41 -0700 Subject: [PATCH 005/383] core/commands: Added root command (with test subcommands) --- core/commands/root.go | 56 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 core/commands/root.go diff --git a/core/commands/root.go b/core/commands/root.go new file mode 100644 index 000000000..608edf403 --- /dev/null +++ b/core/commands/root.go @@ -0,0 +1,56 @@ +package commands + +import ( + cmds "github.com/jbenet/go-ipfs/commands" + "strings" +) + +type TestOutput struct { + Foo string + Bar int +} + +var Root = &cmds.Command{ + Options: []cmds.Option{ + cmds.Option{[]string{"config", "c"}, cmds.String}, + cmds.Option{[]string{"debug", "D"}, cmds.Bool}, + }, + Help: `ipfs - global versioned p2p merkledag file system + +Basic commands: + + init Initialize ipfs local configuration. + add Add an object to ipfs. + cat Show ipfs object data. + ls List links from an object. + refs List link hashes from an object. + +Tool commands: + + config Manage configuration. + version Show ipfs version information. + commands List all available commands. + +Advanced Commands: + + mount Mount an ipfs read-only mountpoint. + serve Serve an interface to ipfs. + net-diag Print network diagnostic. + +Use "ipfs help " for more information about a command. +`, + Subcommands: map[string]*cmds.Command{ + "beep": &cmds.Command{ + Run: func(req cmds.Request, res cmds.Response) { + v := TestOutput{"hello, world", 1337} + res.SetValue(v) + }, + }, + "boop": &cmds.Command{ + Run: func(req cmds.Request, res cmds.Response) { + v := strings.NewReader("hello, world") + res.SetValue(v) + }, + }, + }, +} From 73353824e91c62623b6bc6c3681b6a6dfda9e484 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 22 Oct 2014 15:26:18 -0700 Subject: [PATCH 006/383] server/http: Added HTTP API handler --- server/http/http.go | 94 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 6 deletions(-) diff --git a/server/http/http.go b/server/http/http.go index 3c5992c6e..4b5a6ac3c 100644 --- a/server/http/http.go +++ b/server/http/http.go @@ -5,25 +5,35 @@ import ( "fmt" "io" "net/http" + "strings" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gorilla/mux" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + cmds "github.com/jbenet/go-ipfs/commands" core "github.com/jbenet/go-ipfs/core" + "github.com/jbenet/go-ipfs/core/commands" ) -type handler struct { +type objectHandler struct { ipfs } +type apiHandler struct{} + // Serve starts the http server func Serve(address ma.Multiaddr, node *core.IpfsNode) error { r := mux.NewRouter() - handler := &handler{&ipfsHandler{node}} - r.HandleFunc("/ipfs/", handler.postHandler).Methods("POST") - r.PathPrefix("/ipfs/").Handler(handler).Methods("GET") + objectHandler := &objectHandler{&ipfsHandler{node}} + apiHandler := &apiHandler{} + + r.PathPrefix("/api/v0/").Handler(apiHandler).Methods("GET", "POST") + + r.HandleFunc("/ipfs/", objectHandler.postHandler).Methods("POST") + r.PathPrefix("/ipfs/").Handler(objectHandler).Methods("GET") + http.Handle("/", r) _, host, err := manet.DialArgs(address) @@ -34,7 +44,7 @@ func Serve(address ma.Multiaddr, node *core.IpfsNode) error { return http.ListenAndServe(host, nil) } -func (i *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (i *objectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { path := r.URL.Path[5:] nd, err := i.ResolvePath(path) @@ -55,7 +65,7 @@ func (i *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { io.Copy(w, dr) } -func (i *handler) postHandler(w http.ResponseWriter, r *http.Request) { +func (i *objectHandler) postHandler(w http.ResponseWriter, r *http.Request) { nd, err := i.NewDagFromReader(r.Body) if err != nil { w.WriteHeader(http.StatusInternalServerError) @@ -74,3 +84,75 @@ func (i *handler) postHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusCreated) w.Write([]byte(mh.Multihash(k).B58String())) } + +func (i *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + path := strings.Split(r.URL.Path, "/")[3:] + opts := getOptions(r) + + // TODO: get args + + // ensure the requested command exists, otherwise 404 + _, err := commands.Root.Get(path) + if err != nil { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte("404 page not found")) + return + } + + // build the Request and call the command + req := cmds.NewRequest(path, opts, nil, nil) + res := commands.Root.Call(req) + + // if response contains an error, write an HTTP error status code + if err = res.Error(); err != nil { + e := err.(cmds.Error) + + if e.Code == cmds.ErrClient { + w.WriteHeader(http.StatusBadRequest) + } else { + w.WriteHeader(http.StatusInternalServerError) + } + } + + val := res.Value() + + // if the output value is a io.Reader, stream its output in the request body + if stream, ok := val.(io.Reader); ok { + io.Copy(w, stream) + return + } + + // otherwise, marshall and output the response value or error + if val != nil || res.Error() != nil { + output, err := res.Marshal() + + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + fmt.Println(err) + return + } + + if output != nil { + w.Write(output) + } + } +} + +// getOptions returns the command options in the given HTTP request +// (from the querystring and request body) +func getOptions(r *http.Request) map[string]interface{} { + opts := make(map[string]interface{}) + + query := r.URL.Query() + for k, v := range query { + opts[k] = v[0] + } + + // TODO: get more options from request body (formdata, json, etc) + + if _, exists := opts[cmds.EncShort]; !exists { + opts[cmds.EncShort] = cmds.JSON + } + + return opts +} From 3ae6117c5b35df55c9c375fea1a8d9c37cde9441 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 22 Oct 2014 15:44:32 -0700 Subject: [PATCH 007/383] server/http: Don't cast res.Error() to an Error (fixes panix on response errors) --- server/http/http.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/http/http.go b/server/http/http.go index 4b5a6ac3c..2dcefc872 100644 --- a/server/http/http.go +++ b/server/http/http.go @@ -104,9 +104,7 @@ func (i *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { res := commands.Root.Call(req) // if response contains an error, write an HTTP error status code - if err = res.Error(); err != nil { - e := err.(cmds.Error) - + if e := res.Error(); e != nil { if e.Code == cmds.ErrClient { w.WriteHeader(http.StatusBadRequest) } else { From 3d0ca14b96ff21ac614365ab9ecd39a91eb4b176 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 22 Oct 2014 16:02:12 -0700 Subject: [PATCH 008/383] core/commands: Added more advanced test subcommand --- core/commands/root.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/commands/root.go b/core/commands/root.go index 608edf403..69d093281 100644 --- a/core/commands/root.go +++ b/core/commands/root.go @@ -1,6 +1,7 @@ package commands import ( + "fmt" cmds "github.com/jbenet/go-ipfs/commands" "strings" ) @@ -52,5 +53,24 @@ Use "ipfs help " for more information about a command. res.SetValue(v) }, }, + "warp": &cmds.Command{ + Options: []cmds.Option{ + cmds.Option{[]string{"power", "p"}, cmds.Float}, + }, + Run: func(req cmds.Request, res cmds.Response) { + threshold := 1.21 + + if power, found := req.Option("power"); found && power.(float64) >= threshold { + res.SetValue(struct { + Status string + Power float64 + }{"Flux capacitor activated!", power.(float64)}) + + } else { + err := fmt.Errorf("Insufficient power (%v jiggawatts required)", threshold) + res.SetError(err, cmds.ErrClient) + } + }, + }, }, } From 756474230589cf0fa8dcd7556baf456bd6444465 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 22 Oct 2014 17:37:30 -0700 Subject: [PATCH 009/383] server/http: Fixed error when using long encoding option name --- server/http/http.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/http/http.go b/server/http/http.go index 2dcefc872..9fdfacf8b 100644 --- a/server/http/http.go +++ b/server/http/http.go @@ -148,7 +148,9 @@ func getOptions(r *http.Request) map[string]interface{} { // TODO: get more options from request body (formdata, json, etc) - if _, exists := opts[cmds.EncShort]; !exists { + _, short := opts[cmds.EncShort] + _, long := opts[cmds.EncLong] + if !short && !long { opts[cmds.EncShort] = cmds.JSON } From 12b0ebff7dbd32497916c669fa1f90e40baa28d4 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 22 Oct 2014 17:48:50 -0700 Subject: [PATCH 010/383] server/http: Use Response as Reader instead of manually getting output data --- server/http/http.go | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/server/http/http.go b/server/http/http.go index 9fdfacf8b..dbf8b473b 100644 --- a/server/http/http.go +++ b/server/http/http.go @@ -112,27 +112,10 @@ func (i *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } - val := res.Value() - - // if the output value is a io.Reader, stream its output in the request body - if stream, ok := val.(io.Reader); ok { - io.Copy(w, stream) - return - } - - // otherwise, marshall and output the response value or error - if val != nil || res.Error() != nil { - output, err := res.Marshal() - - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - fmt.Println(err) - return - } - - if output != nil { - w.Write(output) - } + _, err = io.Copy(w, res) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(err.Error())) } } From 0d830e5a2436d55112755080d1084ed1ff13c1eb Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 23 Oct 2014 16:26:40 -0700 Subject: [PATCH 011/383] server/http: Set Content-Type header based on command output --- server/http/http.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/server/http/http.go b/server/http/http.go index dbf8b473b..616b09325 100644 --- a/server/http/http.go +++ b/server/http/http.go @@ -103,6 +103,16 @@ func (i *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { req := cmds.NewRequest(path, opts, nil, nil) res := commands.Root.Call(req) + // set the Content-Type based on res output + if _, ok := res.Value().(io.Reader); ok { + // TODO: set based on actual Content-Type of file + w.Header().Set("Content-Type", "application/octet-stream") + } else { + // TODO: get proper MIME type for encoding from multicodec lib + enc, _ := req.Option(cmds.EncShort) + w.Header().Set("Content-Type", "application/"+enc.(string)) + } + // if response contains an error, write an HTTP error status code if e := res.Error(); e != nil { if e.Code == cmds.ErrClient { @@ -115,6 +125,7 @@ func (i *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { _, err = io.Copy(w, res) if err != nil { w.WriteHeader(http.StatusInternalServerError) + w.Header().Set("Content-Type", "text/plain") w.Write([]byte(err.Error())) } } From 3e062f6e1ed31ae45ca619ac4a2813a9f6a86440 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 23 Oct 2014 16:35:40 -0700 Subject: [PATCH 012/383] cmd/ipfs: Rewrote entry point to invoke commands via new command API --- cmd/ipfs/ipfs.go | 250 ++++++++++------------------------------------- 1 file changed, 49 insertions(+), 201 deletions(-) diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index 9ce5d7faf..a37a44e7b 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -1,220 +1,68 @@ package main import ( - "errors" "fmt" + "io" "os" "runtime/pprof" - 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" - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - - config "github.com/jbenet/go-ipfs/config" - core "github.com/jbenet/go-ipfs/core" - daemon "github.com/jbenet/go-ipfs/daemon" - updates "github.com/jbenet/go-ipfs/updates" + cmds "github.com/jbenet/go-ipfs/commands" + "github.com/jbenet/go-ipfs/commands/cli" + "github.com/jbenet/go-ipfs/core/commands" u "github.com/jbenet/go-ipfs/util" ) -// The IPFS command tree. It is an instance of `commander.Command`. -var CmdIpfs = &commander.Command{ - UsageLine: "ipfs [] []", - Short: "global versioned p2p merkledag file system", - Long: `ipfs - global versioned p2p merkledag file system - -Basic commands: - - init Initialize ipfs local configuration. - add Add an object to ipfs. - cat Show ipfs object data. - ls List links from an object. - refs List link hashes from an object. - -Tool commands: - - config Manage configuration. - update Download and apply go-ipfs updates. - version Show ipfs version information. - commands List all available commands. - -Advanced Commands: - - mount Mount an ipfs read-only mountpoint. - serve Serve an interface to ipfs. - net-diag Print network diagnostic - -Plumbing commands: - - block Interact with raw blocks in the datastore - object Interact with raw dag nodes - - -Use "ipfs help " for more information about a command. -`, - Run: ipfsCmd, - Subcommands: []*commander.Command{ - cmdIpfsAdd, - cmdIpfsCat, - cmdIpfsLs, - cmdIpfsRefs, - cmdIpfsConfig, - cmdIpfsVersion, - cmdIpfsCommands, - cmdIpfsMount, - cmdIpfsInit, - cmdIpfsServe, - cmdIpfsRun, - cmdIpfsName, - cmdIpfsBootstrap, - cmdIpfsDiag, - cmdIpfsBlock, - cmdIpfsObject, - cmdIpfsUpdate, - cmdIpfsLog, - cmdIpfsPin, - cmdIpfsTour, - }, - Flag: *flag.NewFlagSet("ipfs", flag.ExitOnError), -} - // log is the command logger var log = u.Logger("cmd/ipfs") -func init() { - config, err := config.PathRoot() - if err != nil { - fmt.Fprintln(os.Stderr, "Failure initializing the default Config Directory: ", err) - os.Exit(1) - } - CmdIpfs.Flag.String("c", config, "specify config directory") -} - -func ipfsCmd(c *commander.Command, args []string) error { - u.POut(c.Long) - return nil -} +const API_PATH = "/api/v0" func main() { - // if debugging, setup profiling. - if u.Debug { - ofi, err := os.Create("cpu.prof") - if err != nil { - fmt.Println(err) - return - } - pprof.StartCPUProfile(ofi) - defer ofi.Close() - defer pprof.StopCPUProfile() - } - - err := CmdIpfs.Dispatch(os.Args[1:]) + req, err := cli.Parse(os.Args[1:], commands.Root) if err != nil { - if len(err.Error()) > 0 { - fmt.Fprintf(os.Stderr, "ipfs %s: %v\n", os.Args[1], err) - } + fmt.Println(err) os.Exit(1) } - return -} - -// localNode constructs a node -func localNode(confdir string, online bool) (*core.IpfsNode, error) { - filename, err := config.Filename(confdir) - if err != nil { - return nil, err - } - - cfg, err := config.Load(filename) - if err != nil { - return nil, err - } - - if err := updates.CliCheckForUpdates(cfg, filename); err != nil { - return nil, err - } - - return core.NewIpfsNode(cfg, online) -} - -// Gets the config "-c" flag from the command, or returns -// the default configuration root directory -func getConfigDir(c *commander.Command) (string, error) { - - // use the root cmd (that's where config is specified) - for ; c.Parent != nil; c = c.Parent { - } - - // flag should be defined on root. - param := c.Flag.Lookup("c").Value.Get().(string) - if param != "" { - return u.TildeExpansion(param) - } - - return config.PathRoot() -} - -func getConfig(c *commander.Command) (*config.Config, error) { - confdir, err := getConfigDir(c) - if err != nil { - return nil, err - } - - filename, err := config.Filename(confdir) - if err != nil { - return nil, err - } - - return config.Load(filename) -} - -// cmdContext is a wrapper structure that keeps a node, a daemonlistener, and -// a config directory together. These three are needed for most commands. -type cmdContext struct { - node *core.IpfsNode - daemon *daemon.DaemonListener - configDir string -} - -// setupCmdContext initializes a cmdContext structure from a given command. -func setupCmdContext(c *commander.Command, online bool) (cc cmdContext, err error) { - rootCmd := c - for ; rootCmd.Parent != nil; rootCmd = rootCmd.Parent { - } - - cc.configDir, err = getConfigDir(rootCmd) - if err != nil { - return - } - - cc.node, err = localNode(cc.configDir, online) - if err != nil { - return - } - - cc.daemon, err = setupDaemon(cc.configDir, cc.node) - if err != nil { - return - } - - return -} - -// setupDaemon sets up the daemon corresponding to given node. -func setupDaemon(confdir string, node *core.IpfsNode) (*daemon.DaemonListener, error) { - if node.Config.Addresses.API == "" { - return nil, errors.New("no config.Addresses.API endpoint supplied") - } - - maddr, err := ma.NewMultiaddr(node.Config.Addresses.API) - if err != nil { - return nil, err - } - - dl, err := daemon.NewDaemonListener(node, maddr, confdir) - if err != nil { - return nil, err - } - go dl.Listen() - return dl, nil + + cmd, err := commands.Root.Get(req.Path()) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + // TODO: send request to daemon via HTTP API + + if debug, ok := req.Option("debug"); ok && debug.(bool) { + u.Debug = true + + // if debugging, setup profiling. + if u.Debug { + ofi, err := os.Create("cpu.prof") + if err != nil { + fmt.Println(err) + return + } + pprof.StartCPUProfile(ofi) + defer ofi.Close() + defer pprof.StopCPUProfile() + } + } + + res := commands.Root.Call(req) + + if res.Error() != nil { + fmt.Println(res.Error().Error()) + + if cmd.Help != "" && res.Error().Code == cmds.ErrClient { + // TODO: convert from markdown to ANSI terminal format? + fmt.Println(cmd.Help) + } + + os.Exit(1) + } + + _, err = io.Copy(os.Stdout, res) + if err != nil { + fmt.Println(err.Error()) + } } From 26495b799de6979c3ede8ca4ad83ebf7f0cd9c30 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 23 Oct 2014 17:13:52 -0700 Subject: [PATCH 013/383] cmd/ipfs: Deleted existing CLI commands (to be refactored for the commands API) --- cmd/ipfs/add.go | 39 ---------- cmd/ipfs/block.go | 58 --------------- cmd/ipfs/cat.go | 26 ------- cmd/ipfs/commands.go | 74 ------------------- cmd/ipfs/diag.go | 32 -------- cmd/ipfs/gen.go | 72 ------------------ cmd/ipfs/init.go | 150 -------------------------------------- cmd/ipfs/log.go | 29 -------- cmd/ipfs/ls.go | 29 -------- cmd/ipfs/mount_unix.go | 94 ------------------------ cmd/ipfs/mount_windows.go | 19 ----- cmd/ipfs/name.go | 57 --------------- cmd/ipfs/objects.go | 112 ---------------------------- cmd/ipfs/pin.go | 62 ---------------- cmd/ipfs/publish.go | 41 ----------- cmd/ipfs/refs.go | 36 --------- cmd/ipfs/resolve.go | 42 ----------- cmd/ipfs/run.go | 36 --------- cmd/ipfs/serve.go | 49 ------------- cmd/ipfs/update.go | 62 ---------------- cmd/ipfs/version.go | 30 -------- 21 files changed, 1149 deletions(-) delete mode 100644 cmd/ipfs/add.go delete mode 100644 cmd/ipfs/block.go delete mode 100644 cmd/ipfs/cat.go delete mode 100644 cmd/ipfs/commands.go delete mode 100644 cmd/ipfs/diag.go delete mode 100644 cmd/ipfs/gen.go delete mode 100644 cmd/ipfs/init.go delete mode 100644 cmd/ipfs/log.go delete mode 100644 cmd/ipfs/ls.go delete mode 100644 cmd/ipfs/mount_unix.go delete mode 100644 cmd/ipfs/mount_windows.go delete mode 100644 cmd/ipfs/name.go delete mode 100644 cmd/ipfs/objects.go delete mode 100644 cmd/ipfs/pin.go delete mode 100644 cmd/ipfs/publish.go delete mode 100644 cmd/ipfs/refs.go delete mode 100644 cmd/ipfs/resolve.go delete mode 100644 cmd/ipfs/run.go delete mode 100644 cmd/ipfs/serve.go delete mode 100644 cmd/ipfs/update.go delete mode 100644 cmd/ipfs/version.go diff --git a/cmd/ipfs/add.go b/cmd/ipfs/add.go deleted file mode 100644 index a755f699e..000000000 --- a/cmd/ipfs/add.go +++ /dev/null @@ -1,39 +0,0 @@ -package main - -import ( - "fmt" - "path/filepath" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" - "github.com/jbenet/go-ipfs/core/commands" -) - -// Error indicating the max depth has been exceded. -var ErrDepthLimitExceeded = fmt.Errorf("depth limit exceeded") - -var cmdIpfsAdd = &commander.Command{ - UsageLine: "add", - Short: "Add an object to ipfs.", - Long: `ipfs add ... - Add objects to ipfs. - - Adds contents of to ipfs. Use -r to add directories. - Note that directories are added recursively, to form the ipfs - MerkleDAG. A smarter partial add with a staging area (like git) - remains to be implemented. -`, - Run: addCmd, - Flag: *flag.NewFlagSet("ipfs-add", flag.ExitOnError), -} - -func init() { - cmdIpfsAdd.Flag.Bool("r", false, "add objects recursively") -} - -var addCmd = makeCommand(command{ - name: "add", - args: 1, - flags: []string{"r"}, - cmdFn: commands.Add, - argFilter: filepath.Abs, -}) diff --git a/cmd/ipfs/block.go b/cmd/ipfs/block.go deleted file mode 100644 index 2f139d0bc..000000000 --- a/cmd/ipfs/block.go +++ /dev/null @@ -1,58 +0,0 @@ -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 cmdIpfsBlock = &commander.Command{ - UsageLine: "block", - Short: "manipulate raw ipfs blocks", - Long: `ipfs block - manipulate raw ipfs blocks - - ipfs block get - get and output block named by - ipfs block put - store stdin as a block, outputs - -ipfs block is a plumbing command used to manipulate raw ipfs blocks. -Reads from stdin or writes to stdout, and is a base58 encoded -multihash.`, - // Run: blockGetCmd, - Subcommands: []*commander.Command{ - cmdIpfsBlockGet, - cmdIpfsBlockPut, - }, - Flag: *flag.NewFlagSet("ipfs-block", flag.ExitOnError), -} - -var cmdIpfsBlockGet = &commander.Command{ - UsageLine: "get ", - Short: "get and output block named by ", - Long: `ipfs get - get and output block named by - -ipfs block get is a plumbing command for retreiving raw ipfs blocks. -It outputs to stdout, and is a base58 encoded multihash.`, - Run: makeCommand(command{ - name: "blockGet", - args: 1, - flags: nil, - online: true, - cmdFn: commands.BlockGet, - }), -} - -var cmdIpfsBlockPut = &commander.Command{ - UsageLine: "put", - Short: "store stdin as a block, outputs ", - Long: `ipfs put - store stdin as a block, outputs - -ipfs block put is a plumbing command for storing raw ipfs blocks. -It reads from stding, and is a base58 encoded multihash.`, - Run: makeCommand(command{ - name: "blockPut", - args: 0, - flags: nil, - online: true, - cmdFn: commands.BlockPut, - }), -} diff --git a/cmd/ipfs/cat.go b/cmd/ipfs/cat.go deleted file mode 100644 index 168a4841e..000000000 --- a/cmd/ipfs/cat.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" - "github.com/jbenet/go-ipfs/core/commands" -) - -var cmdIpfsCat = &commander.Command{ - UsageLine: "cat", - Short: "Show ipfs object data.", - Long: `ipfs cat - Show ipfs object data. - - Retrieves the object named by and displays the Data - it contains. -`, - Run: catCmd, - Flag: *flag.NewFlagSet("ipfs-cat", flag.ExitOnError), -} - -var catCmd = makeCommand(command{ - name: "cat", - args: 1, - flags: nil, - cmdFn: commands.Cat, -}) diff --git a/cmd/ipfs/commands.go b/cmd/ipfs/commands.go deleted file mode 100644 index cc0ed089a..000000000 --- a/cmd/ipfs/commands.go +++ /dev/null @@ -1,74 +0,0 @@ -package main - -import ( - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" - u "github.com/jbenet/go-ipfs/util" - "strings" - "time" -) - -var cmdIpfsCommands = &commander.Command{ - UsageLine: "commands", - Short: "List all available commands.", - Long: `ipfs commands - List all available commands. - - Lists all available commands (and sub-commands) and exits. - `, - Run: commandsCmd, - Subcommands: []*commander.Command{ - cmdIpfsCommandsHelp, - }, -} - -func commandsCmd(c *commander.Command, args []string) error { - var listCmds func(c *commander.Command) - listCmds = func(c *commander.Command) { - u.POut("%s\n", c.FullSpacedName()) - for _, sc := range c.Subcommands { - listCmds(sc) - } - } - - listCmds(c.Parent) - return nil -} - -var cmdIpfsCommandsHelp = &commander.Command{ - UsageLine: "help", - Short: "List all available commands' help pages.", - Long: `ipfs commands help - List all available commands's help pages. - - Shows the pages of all available commands (and sub-commands) and exits. - Outputs a markdown document. - `, - Run: commandsHelpCmd, -} - -func commandsHelpCmd(c *commander.Command, args []string) error { - u.POut(referenceHeaderMsg) - u.POut("Generated on %s.\n\n", time.Now().UTC().Format("2006-01-02")) - - var printCmds func(*commander.Command, int) - printCmds = func(c *commander.Command, level int) { - u.POut("%s ", strings.Repeat("#", level)) - u.POut("%s\n\n", c.FullSpacedName()) - u.POut("```\n") - u.POut("%s\n", c.Long) - u.POut("```\n\n") - - for _, sc := range c.Subcommands { - printCmds(sc, level+1) - } - } - - printCmds(c.Parent.Parent, 1) - return nil -} - -const referenceHeaderMsg = ` -# ipfs command reference - -This document lists every ipfs command (including subcommands), along with -its help page. It can be viewed by running 'ipfs commands help'. - -` diff --git a/cmd/ipfs/diag.go b/cmd/ipfs/diag.go deleted file mode 100644 index 657ddae17..000000000 --- a/cmd/ipfs/diag.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" - "github.com/jbenet/go-ipfs/core/commands" -) - -var cmdIpfsDiag = &commander.Command{ - UsageLine: "net-diag", - Short: "Generate a diagnostics report", - Long: `ipfs net-diag - Generate a diagnostics report. - - Sends out a message to each node in the network recursively - requesting a listing of data about them including number of - connected peers and latencies between them. -`, - Run: diagCmd, - Flag: *flag.NewFlagSet("ipfs-net-diag", flag.ExitOnError), -} - -func init() { - cmdIpfsDiag.Flag.Bool("raw", false, "print raw json output") -} - -var diagCmd = makeCommand(command{ - name: "diag", - args: 0, - flags: []string{"raw"}, - cmdFn: commands.Diag, -}) diff --git a/cmd/ipfs/gen.go b/cmd/ipfs/gen.go deleted file mode 100644 index e6a957dac..000000000 --- a/cmd/ipfs/gen.go +++ /dev/null @@ -1,72 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" - "github.com/jbenet/go-ipfs/core/commands" - "github.com/jbenet/go-ipfs/daemon" - u "github.com/jbenet/go-ipfs/util" -) - -// command is the descriptor of an ipfs daemon command. -// Used with makeCommand to proxy over commands via the daemon. -type command struct { - name string - args int - flags []string - online bool - cmdFn commands.CmdFunc - argFilter func(string) (string, error) -} - -// commanderFunc is a function that can be passed into the Commander library as -// a command handler. Defined here because commander lacks this definition. -type commanderFunc func(*commander.Command, []string) error - -// makeCommand Wraps a commands.CmdFunc so that it may be safely run by the -// commander library -func makeCommand(cmdDesc command) commanderFunc { - return func(c *commander.Command, inp []string) error { - if len(inp) < cmdDesc.args { - u.POut(c.Long) - return nil - } - confdir, err := getConfigDir(c) - if err != nil { - return err - } - - cmd := daemon.NewCommand() - cmd.Command = cmdDesc.name - if cmdDesc.argFilter != nil { - for _, a := range inp { - s, err := cmdDesc.argFilter(a) - if err != nil { - return err - } - cmd.Args = append(cmd.Args, s) - } - } else { - cmd.Args = inp - } - - for _, a := range cmdDesc.flags { - cmd.Opts[a] = c.Flag.Lookup(a).Value.Get() - } - - err = daemon.SendCommand(cmd, confdir) - if err != nil { - log.Info("Executing command locally: %s", err) - // Do locally - n, err := localNode(confdir, cmdDesc.online) - if err != nil { - return fmt.Errorf("Local node creation failed: %v", err) - } - - return cmdDesc.cmdFn(n, cmd.Args, cmd.Opts, os.Stdout) - } - return nil - } -} diff --git a/cmd/ipfs/init.go b/cmd/ipfs/init.go deleted file mode 100644 index 2c4446c58..000000000 --- a/cmd/ipfs/init.go +++ /dev/null @@ -1,150 +0,0 @@ -package main - -import ( - "encoding/base64" - "errors" - "os" - "path/filepath" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" - config "github.com/jbenet/go-ipfs/config" - ci "github.com/jbenet/go-ipfs/crypto" - peer "github.com/jbenet/go-ipfs/peer" - updates "github.com/jbenet/go-ipfs/updates" - u "github.com/jbenet/go-ipfs/util" -) - -var cmdIpfsInit = &commander.Command{ - UsageLine: "init", - Short: "Initialize ipfs local configuration", - Long: `ipfs init - - Initializes ipfs configuration files and generates a - new keypair. -`, - Run: initCmd, - Flag: *flag.NewFlagSet("ipfs-init", flag.ExitOnError), -} - -func init() { - cmdIpfsInit.Flag.Int("b", 4096, "number of bits for keypair") - cmdIpfsInit.Flag.String("p", "", "passphrase for encrypting keys") - cmdIpfsInit.Flag.Bool("f", false, "force overwrite of existing config") - cmdIpfsInit.Flag.String("d", "", "Change default datastore location") -} - -func initCmd(c *commander.Command, inp []string) error { - configpath, err := getConfigDir(c.Parent) - if err != nil { - return err - } - - u.POut("initializing ipfs node at %s\n", configpath) - filename, err := config.Filename(configpath) - if err != nil { - return errors.New("Couldn't get home directory path") - } - - dspath, ok := c.Flag.Lookup("d").Value.Get().(string) - if !ok { - return errors.New("failed to parse datastore flag") - } - - fi, err := os.Lstat(filename) - force, ok := c.Flag.Lookup("f").Value.Get().(bool) - if !ok { - return errors.New("failed to parse force flag") - } - if fi != nil || (err != nil && !os.IsNotExist(err)) { - if !force { - return errors.New("ipfs configuration file already exists!\nReinitializing would overwrite your keys.\n(use -f to force overwrite)") - } - } - cfg := new(config.Config) - - cfg.Datastore = config.Datastore{} - if len(dspath) == 0 { - dspath, err = config.DataStorePath("") - if err != nil { - return err - } - } - cfg.Datastore.Path = dspath - cfg.Datastore.Type = "leveldb" - - // Construct the data store if missing - if err := os.MkdirAll(dspath, os.ModePerm); err != nil { - return err - } - - // Check the directory is writeable - if f, err := os.Create(filepath.Join(dspath, "._check_writeable")); err == nil { - os.Remove(f.Name()) - } else { - return errors.New("Datastore '" + dspath + "' is not writeable") - } - - cfg.Identity = config.Identity{} - - // setup the node addresses. - cfg.Addresses = config.Addresses{ - Swarm: "/ip4/0.0.0.0/tcp/4001", - API: "/ip4/127.0.0.1/tcp/5001", - } - - // setup the node mount points. - cfg.Mounts = config.Mounts{ - IPFS: "/ipfs", - IPNS: "/ipns", - } - - nbits, ok := c.Flag.Lookup("b").Value.Get().(int) - if !ok { - return errors.New("failed to get bits flag") - } - if nbits < 1024 { - return errors.New("Bitsize less than 1024 is considered unsafe.") - } - - u.POut("generating key pair\n") - sk, pk, err := ci.GenerateKeyPair(ci.RSA, nbits) - if err != nil { - return err - } - - // currently storing key unencrypted. in the future we need to encrypt it. - // TODO(security) - skbytes, err := sk.Bytes() - if err != nil { - return err - } - cfg.Identity.PrivKey = base64.StdEncoding.EncodeToString(skbytes) - - id, err := peer.IDFromPubKey(pk) - if err != nil { - return err - } - cfg.Identity.PeerID = id.Pretty() - - // Use these hardcoded bootstrap peers for now. - cfg.Bootstrap = []*config.BootstrapPeer{ - &config.BootstrapPeer{ - // mars.i.ipfs.io - PeerID: "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", - Address: "/ip4/104.131.131.82/tcp/4001", - }, - } - - // tracking ipfs version used to generate the init folder and adding update checker default setting. - cfg.Version = config.Version{ - Check: "error", - Current: updates.Version, - } - - err = config.WriteConfigFile(filename, cfg) - if err != nil { - return err - } - return nil -} diff --git a/cmd/ipfs/log.go b/cmd/ipfs/log.go deleted file mode 100644 index 255b7c263..000000000 --- a/cmd/ipfs/log.go +++ /dev/null @@ -1,29 +0,0 @@ -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 cmdIpfsLog = &commander.Command{ - UsageLine: "log ", - Short: "switch logging levels of a running daemon", - Long: `ipfs log - switch logging levels of a running daemon - - is a the subsystem logging identifier. Use * for all subsystems. - is one of: debug, info, notice, warning, error, critical - -ipfs log is a utility command used to change the logging output of a running daemon. -`, - Run: logCmd, - Flag: *flag.NewFlagSet("ipfs-log", flag.ExitOnError), -} - -var logCmd = makeCommand(command{ - name: "log", - args: 2, - flags: nil, - online: true, - cmdFn: commands.Log, -}) diff --git a/cmd/ipfs/ls.go b/cmd/ipfs/ls.go deleted file mode 100644 index 0a2e8aff9..000000000 --- a/cmd/ipfs/ls.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" - "github.com/jbenet/go-ipfs/core/commands" -) - -var cmdIpfsLs = &commander.Command{ - UsageLine: "ls", - Short: "List links from an object.", - Long: `ipfs ls - List links from an object. - - Retrieves the object named by and displays the links - it contains, with the following format: - - - -`, - Run: lsCmd, - Flag: *flag.NewFlagSet("ipfs-ls", flag.ExitOnError), -} - -var lsCmd = makeCommand(command{ - name: "ls", - args: 1, - flags: nil, - cmdFn: commands.Ls, -}) diff --git a/cmd/ipfs/mount_unix.go b/cmd/ipfs/mount_unix.go deleted file mode 100644 index 84fbc9cf6..000000000 --- a/cmd/ipfs/mount_unix.go +++ /dev/null @@ -1,94 +0,0 @@ -// +build linux darwin freebsd - -package main - -import ( - "fmt" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" - - core "github.com/jbenet/go-ipfs/core" - ipns "github.com/jbenet/go-ipfs/fuse/ipns" - rofs "github.com/jbenet/go-ipfs/fuse/readonly" -) - -var cmdIpfsMount = &commander.Command{ - UsageLine: "mount", - Short: "Mount an ipfs read-only mountpoint.", - Long: `ipfs mount - Mount an ipfs read-only mountpoint. - - Mount ipfs at a read-only mountpoint on the OS. All ipfs objects - will be accessible under that directory. Note that the root will - not be listable, as it is virtual. Accessing known paths directly. - -`, - Run: mountCmd, - Flag: *flag.NewFlagSet("ipfs-mount", flag.ExitOnError), -} - -func init() { - cmdIpfsMount.Flag.String("f", "", "specify a mountpoint for ipfs") - cmdIpfsMount.Flag.String("n", "", "specify a mountpoint for ipns") -} - -func mountCmd(c *commander.Command, inp []string) error { - - cc, err := setupCmdContext(c, true) - if err != nil { - return err - } - defer cc.daemon.Close() - - // update fsdir with flag. - fsdir := cc.node.Config.Mounts.IPFS - if val, ok := c.Flag.Lookup("f").Value.Get().(string); ok && val != "" { - fsdir = val - } - fsdone := mountIpfs(cc.node, fsdir) - - // get default mount points - nsdir := cc.node.Config.Mounts.IPNS - if val, ok := c.Flag.Lookup("n").Value.Get().(string); ok && val != "" { - nsdir = val - } - nsdone := mountIpns(cc.node, nsdir, fsdir) - - // wait till mounts are done. - err1 := <-fsdone - err2 := <-nsdone - - if err1 != nil { - return err1 - } - return err2 -} - -func mountIpfs(node *core.IpfsNode, fsdir string) <-chan error { - done := make(chan error) - fmt.Printf("mounting ipfs at %s\n", fsdir) - - go func() { - err := rofs.Mount(node, fsdir) - done <- err - close(done) - }() - - return done -} - -func mountIpns(node *core.IpfsNode, nsdir, fsdir string) <-chan error { - if nsdir == "" { - return nil - } - done := make(chan error) - fmt.Printf("mounting ipns at %s\n", nsdir) - - go func() { - err := ipns.Mount(node, nsdir, fsdir) - done <- err - close(done) - }() - - return done -} diff --git a/cmd/ipfs/mount_windows.go b/cmd/ipfs/mount_windows.go deleted file mode 100644 index aabf4b4ff..000000000 --- a/cmd/ipfs/mount_windows.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -import ( - "errors" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" -) - -var cmdIpfsMount = &commander.Command{ - UsageLine: "mount", - Short: "Mount an ipfs read-only mountpoint.", - Long: `Not yet implemented on windows.`, - Run: mountCmd, - Flag: *flag.NewFlagSet("ipfs-mount", flag.ExitOnError), -} - -func mountCmd(c *commander.Command, inp []string) error { - return errors.New("mount not yet implemented on windows") -} diff --git a/cmd/ipfs/name.go b/cmd/ipfs/name.go deleted file mode 100644 index 7021fe612..000000000 --- a/cmd/ipfs/name.go +++ /dev/null @@ -1,57 +0,0 @@ -package main - -import ( - "fmt" - - 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" -) - -var cmdIpfsName = &commander.Command{ - UsageLine: "name [publish | resolve]", - Short: "ipfs namespace (ipns) tool", - Long: `ipfs name - Get/Set ipfs config values. - - ipfs name publish [] - Assign the to - ipfs name resolve [] - Resolve the value of - -IPNS is a PKI namespace, where names are the hashes of public keys, and -the private key enables publishing new (signed) values. In both publish -and resolve, the default value of is your own identity public key. - - -Examples: - -Publish a to your identity name: - - > ipfs name publish QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - -Publish a to another public key: - - > ipfs name publish QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - -Resolve the value of your identity: - - > ipfs name resolve - QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - -Resolve te value of another name: - - > ipfs name resolve QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n - QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - -`, - Run: nameCmd, - Flag: *flag.NewFlagSet("ipfs-name", flag.ExitOnError), - Subcommands: []*commander.Command{ - cmdIpfsPub, - cmdIpfsResolve, - }, -} - -func nameCmd(c *commander.Command, args []string) error { - fmt.Println(c.Long) - return nil -} diff --git a/cmd/ipfs/objects.go b/cmd/ipfs/objects.go deleted file mode 100644 index c2092ebf0..000000000 --- a/cmd/ipfs/objects.go +++ /dev/null @@ -1,112 +0,0 @@ -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 - return the data for this key as raw bytes - ipfs object links - lists (the keys of ?) the links this key points to - ipfs object get - 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. -- 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 ", - Short: "data outputs the raw bytes named by ", - Long: `ipfs data - data outputs the raw bytes named by - -ipfs data is a plumbing command for retreiving the raw bytes stored in a dag node. -It outputs to stdout, and 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 ", - Short: "outputs the links pointed to by ", - Long: `ipfs links - outputs the links pointed to by - -ipfs block get is a plumbing command for retreiving raw ipfs blocks. -It outputs to stdout, and 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 ", - Short: "get and serialize the dag node named by ", - Long: `ipfs get - get and output the dag node named by - -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 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 ", - Long: `ipfs put - store stdin as a dag object, outputs - -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 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, - }), -} diff --git a/cmd/ipfs/pin.go b/cmd/ipfs/pin.go deleted file mode 100644 index 5b331f190..000000000 --- a/cmd/ipfs/pin.go +++ /dev/null @@ -1,62 +0,0 @@ -package main - -import ( - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" - "github.com/jbenet/go-ipfs/core/commands" -) - -var cmdIpfsPin = &commander.Command{ - UsageLine: "pin", - Short: "", - Long: `ipfs pin [add|rm] - object pinning commands -`, - Subcommands: []*commander.Command{ - cmdIpfsSubPin, - cmdIpfsSubUnpin, - }, -} - -var cmdIpfsSubPin = &commander.Command{ - UsageLine: "add", - Short: "pin an ipfs object to local storage.", - Long: `ipfs pin add - pin ipfs object to local storage. - - Retrieves the object named by and stores it locally - on disk. -`, - Run: pinSubCmd, - Flag: *flag.NewFlagSet("ipfs-pin", flag.ExitOnError), -} - -var pinSubCmd = makeCommand(command{ - name: "pin", - args: 1, - flags: []string{"r", "d"}, - cmdFn: commands.Pin, -}) - -var cmdIpfsSubUnpin = &commander.Command{ - UsageLine: "rm", - Short: "unpin an ipfs object from local storage.", - Long: `ipfs pin rm - unpin ipfs object from local storage. - - Removes the pin from the given object allowing it to be garbage - collected if needed. -`, - Run: unpinSubCmd, - Flag: *flag.NewFlagSet("ipfs-unpin", flag.ExitOnError), -} - -var unpinSubCmd = makeCommand(command{ - name: "unpin", - args: 1, - flags: []string{"r"}, - cmdFn: commands.Unpin, -}) - -func init() { - cmdIpfsSubPin.Flag.Bool("r", false, "pin objects recursively") - cmdIpfsSubPin.Flag.Int("d", 1, "recursive depth") - cmdIpfsSubUnpin.Flag.Bool("r", false, "unpin objects recursively") -} diff --git a/cmd/ipfs/publish.go b/cmd/ipfs/publish.go deleted file mode 100644 index 041da0028..000000000 --- a/cmd/ipfs/publish.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" - "github.com/jbenet/go-ipfs/core/commands" -) - -var cmdIpfsPub = &commander.Command{ - UsageLine: "publish", - Short: "publish a to ipns.", - Long: `ipfs publish [] - publish a to ipns. - -IPNS is a PKI namespace, where names are the hashes of public keys, and -the private key enables publishing new (signed) values. In publish, the -default value of is your own identity public key. - -Examples: - -Publish a to your identity name: - - > ipfs name publish QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - -Publish a to another public key: - - > ipfs name publish QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - -`, - Run: pubCmd, - Flag: *flag.NewFlagSet("ipfs-publish", flag.ExitOnError), -} - -var pubCmd = makeCommand(command{ - name: "publish", - args: 1, - flags: nil, - online: true, - cmdFn: commands.Publish, -}) diff --git a/cmd/ipfs/refs.go b/cmd/ipfs/refs.go deleted file mode 100644 index b3aaf85fc..000000000 --- a/cmd/ipfs/refs.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import ( - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" - commands "github.com/jbenet/go-ipfs/core/commands" -) - -var cmdIpfsRefs = &commander.Command{ - UsageLine: "refs", - Short: "List link hashes from an object.", - Long: `ipfs refs - List link hashes from an object.. - - Retrieves the object named by and displays the link - hashes it contains, with the following format: - - - - Note: list all refs recursively with -r. - -`, - Run: refCmd, - Flag: *flag.NewFlagSet("ipfs-refs", flag.ExitOnError), -} - -func init() { - cmdIpfsRefs.Flag.Bool("r", false, "recursive: list refs recursively") - cmdIpfsRefs.Flag.Bool("u", false, "unique: list each ref only once") -} - -var refCmd = makeCommand(command{ - name: "refs", - args: 1, - flags: []string{"r", "u"}, - cmdFn: commands.Refs, -}) diff --git a/cmd/ipfs/resolve.go b/cmd/ipfs/resolve.go deleted file mode 100644 index 9f5107ff8..000000000 --- a/cmd/ipfs/resolve.go +++ /dev/null @@ -1,42 +0,0 @@ -package main - -import ( - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" - "github.com/jbenet/go-ipfs/core/commands" -) - -var cmdIpfsResolve = &commander.Command{ - UsageLine: "resolve", - Short: "resolve an ipns name to a ", - Long: `ipfs resolve [] - Resolve an ipns name to a . - -IPNS is a PKI namespace, where names are the hashes of public keys, and -the private key enables publishing new (signed) values. In resolve, the -default value of is your own identity public key. - - -Examples: - -Resolve the value of your identity: - - > ipfs name resolve - QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - -Resolve te value of another name: - - > ipfs name resolve QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n - QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - -`, - Run: resolveCmd, - Flag: *flag.NewFlagSet("ipfs-resolve", flag.ExitOnError), -} - -var resolveCmd = makeCommand(command{ - name: "resolve", - args: 0, - flags: nil, - online: true, - cmdFn: commands.Resolve, -}) diff --git a/cmd/ipfs/run.go b/cmd/ipfs/run.go deleted file mode 100644 index b651ca9d9..000000000 --- a/cmd/ipfs/run.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import ( - "os" - "os/signal" - "syscall" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" -) - -var cmdIpfsRun = &commander.Command{ - UsageLine: "run", - Short: "run local ifps node.", - Long: `run a local ipfs node with no other interface. -`, - Run: runCmd, - Flag: *flag.NewFlagSet("ipfs-run", flag.ExitOnError), -} - -func runCmd(c *commander.Command, inp []string) error { - cc, err := setupCmdContext(c, true) - if err != nil { - return err - } - - sigc := make(chan os.Signal, 1) - signal.Notify(sigc, syscall.SIGHUP, syscall.SIGINT, - syscall.SIGTERM, syscall.SIGQUIT) - - // wait until we get a signal to exit. - <-sigc - - cc.daemon.Close() - return nil -} diff --git a/cmd/ipfs/serve.go b/cmd/ipfs/serve.go deleted file mode 100644 index fda3b3f22..000000000 --- a/cmd/ipfs/serve.go +++ /dev/null @@ -1,49 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - - h "github.com/jbenet/go-ipfs/server/http" -) - -var cmdIpfsServe = &commander.Command{ - UsageLine: "serve", - Short: "Serve an interface to ipfs", - Subcommands: []*commander.Command{ - cmdIpfsServeHTTP, - }, - Flag: *flag.NewFlagSet("ipfs-serve", flag.ExitOnError), -} - -var cmdIpfsServeHTTP = &commander.Command{ - UsageLine: "http", - Short: "Serve an HTTP API", - Long: `ipfs serve http - Serve an http gateway into ipfs.`, - Run: serveHTTPCmd, - Flag: *flag.NewFlagSet("ipfs-serve-http", flag.ExitOnError), -} - -func init() { - cmdIpfsServeHTTP.Flag.String("address", "/ip4/127.0.0.1/tcp/8080", "Listen Address") -} - -func serveHTTPCmd(c *commander.Command, _ []string) error { - cc, err := setupCmdContext(c, true) - if err != nil { - return err - } - defer cc.daemon.Close() - - address := c.Flag.Lookup("address").Value.Get().(string) - maddr, err := ma.NewMultiaddr(address) - if err != nil { - return err - } - - fmt.Printf("Serving on %s\n", address) - return h.Serve(maddr, cc.node) -} diff --git a/cmd/ipfs/update.go b/cmd/ipfs/update.go deleted file mode 100644 index e6d078253..000000000 --- a/cmd/ipfs/update.go +++ /dev/null @@ -1,62 +0,0 @@ -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" -) - -func init() { - cmdIpfsUpdate.Flag.Bool("force", false, "force shutdown of daemon when updating") -} - -var cmdIpfsUpdate = &commander.Command{ - UsageLine: "update", - Short: "check for updates and apply them", - Long: `ipfs update - check for updates and apply them - - ipfs update - apply - ipfs update check - just check - ipfs update log - list the changelogs - -ipfs update is a utility command used to check for updates and apply them. -I wont even try, @jbenet. You do this much better :)`, - Run: makeCommand(command{ - name: "updateApply", - args: 0, - flags: []string{"force"}, - online: true, - cmdFn: commands.UpdateApply, - }), - Subcommands: []*commander.Command{ - cmdIpfsUpdateCheck, - cmdIpfsUpdateLog, - }, - Flag: *flag.NewFlagSet("ipfs-update", flag.ExitOnError), -} - -var cmdIpfsUpdateCheck = &commander.Command{ - UsageLine: "check", - Short: "", - Long: `ipfs update check `, - Run: makeCommand(command{ - name: "updateCheck", - args: 0, - flags: nil, - online: false, - cmdFn: commands.UpdateCheck, - }), -} - -var cmdIpfsUpdateLog = &commander.Command{ - UsageLine: "log", - Short: "list the last versions and their changelog", - Long: `ipfs updage log - list the last versions and their changelog`, - Run: makeCommand(command{ - name: "updateLog", - args: 0, - flags: nil, - online: false, - cmdFn: commands.UpdateCheck, - }), -} diff --git a/cmd/ipfs/version.go b/cmd/ipfs/version.go deleted file mode 100644 index 1c02bf69f..000000000 --- a/cmd/ipfs/version.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" - updates "github.com/jbenet/go-ipfs/updates" - u "github.com/jbenet/go-ipfs/util" -) - -var cmdIpfsVersion = &commander.Command{ - UsageLine: "version", - Short: "Show ipfs version information.", - Long: `ipfs version - Show ipfs version information. - - Returns the current version of ipfs and exits. - `, - Run: versionCmd, -} - -func init() { - cmdIpfsVersion.Flag.Bool("number", false, "show only the number") -} - -func versionCmd(c *commander.Command, _ []string) error { - number := c.Flag.Lookup("number").Value.Get().(bool) - if !number { - u.POut("ipfs version ") - } - u.POut("%s\n", updates.Version) - return nil -} From c89826c1ef4f610df0cd645525761fc7e4337e77 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 23 Oct 2014 17:14:06 -0700 Subject: [PATCH 014/383] cmd/ipfs: Added HTTP RPC client to entry point --- cmd/ipfs/ipfs.go | 71 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index a37a44e7b..8cb583807 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -1,10 +1,13 @@ package main import ( + "encoding/json" "fmt" "io" + "net/http" "os" "runtime/pprof" + "strings" cmds "github.com/jbenet/go-ipfs/commands" "github.com/jbenet/go-ipfs/commands/cli" @@ -24,13 +27,19 @@ func main() { os.Exit(1) } + // TODO: call command locally if option tells us to, or if command is CLI-only (e.g. ipfs init) + cmd, err := commands.Root.Get(req.Path()) if err != nil { fmt.Println(err) os.Exit(1) } - // TODO: send request to daemon via HTTP API + res, err := sendRequest(req) + if err != nil { + fmt.Println(err) + os.Exit(1) + } if debug, ok := req.Option("debug"); ok && debug.(bool) { u.Debug = true @@ -48,7 +57,7 @@ func main() { } } - res := commands.Root.Call(req) + //res := commands.Root.Call(req) if res.Error() != nil { fmt.Println(res.Error().Error()) @@ -66,3 +75,61 @@ func main() { fmt.Println(err.Error()) } } + +func sendRequest(req cmds.Request) (cmds.Response, error) { + // TODO: load RPC host from config + url := "http://localhost:8080" + API_PATH + url += "/" + strings.Join(req.Path(), "/") + + // TODO: support other encodings once we have multicodec to decode response + // (we shouldn't have to set this here) + encoding := cmds.JSON + req.SetOption(cmds.EncShort, encoding) + + query := "?" + options := req.Options() + for k, v := range options { + query += "&" + k + "=" + v.(string) + } + + httpRes, err := http.Post(url+query, "application/octet-stream", req.Stream()) + if err != nil { + return nil, err + } + + res := cmds.NewResponse(req) + + contentType := httpRes.Header["Content-Type"][0] + contentType = strings.Split(contentType, ";")[0] + + if contentType == "application/octet-stream" { + res.SetValue(httpRes.Body) + return res, nil + } + + // TODO: decode based on `encoding`, using multicodec + dec := json.NewDecoder(httpRes.Body) + + if httpRes.StatusCode >= http.StatusBadRequest { + e := cmds.Error{} + err = dec.Decode(&e) + if err != nil { + fmt.Println(err) + return nil, err + } + + res.SetError(e, e.Code) + + } else { + var v interface{} + err = dec.Decode(&v) + if err != nil { + fmt.Println(err) + return nil, err + } + + res.SetValue(v) + } + + return res, nil +} From ccfb10dde01f903fe68e4f2201fc9c89d5f9148d Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 23 Oct 2014 19:25:16 -0700 Subject: [PATCH 015/383] cmd/ipfs: Added root command for CLI-specific subcommands (init, daemon, etc.) --- cmd/ipfs/root.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 cmd/ipfs/root.go diff --git a/cmd/ipfs/root.go b/cmd/ipfs/root.go new file mode 100644 index 000000000..154381cbb --- /dev/null +++ b/cmd/ipfs/root.go @@ -0,0 +1,19 @@ +package main + +import ( + cmds "github.com/jbenet/go-ipfs/commands" + "github.com/jbenet/go-ipfs/core/commands" +) + +var Root = &cmds.Command{ + Options: commands.Root.Options, + Help: commands.Root.Help, + Subcommands: map[string]*cmds.Command{ + "test": &cmds.Command{ + Run: func(req cmds.Request, res cmds.Response) { + v := "hello, world" + res.SetValue(v) + }, + }, + }, +} From 201ad30a7b947078646c030ebd65b2eb215b097f Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 23 Oct 2014 19:26:58 -0700 Subject: [PATCH 016/383] cmd/ipfs: Run CLI-specific subcommands locally in CLI entry point --- cmd/ipfs/ipfs.go | 50 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index 8cb583807..1779fec1b 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -21,26 +21,46 @@ var log = u.Logger("cmd/ipfs") const API_PATH = "/api/v0" func main() { - req, err := cli.Parse(os.Args[1:], commands.Root) + args := os.Args[1:] + + req, err := cli.Parse(args, Root) if err != nil { fmt.Println(err) os.Exit(1) } - // TODO: call command locally if option tells us to, or if command is CLI-only (e.g. ipfs init) - - cmd, err := commands.Root.Get(req.Path()) - if err != nil { - fmt.Println(err) - os.Exit(1) + if len(req.Path()) == 0 { + req, err = cli.Parse(args, commands.Root) + if err != nil { + fmt.Println(err) + os.Exit(1) + } } - res, err := sendRequest(req) - if err != nil { + var local bool // TODO: option to force local + var root *cmds.Command + cmd, err := Root.Get(req.Path()) + if err == nil { + local = true + root = Root + + } else if local { fmt.Println(err) os.Exit(1) + + } else { + cmd, err = commands.Root.Get(req.Path()) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + local = false + root = commands.Root } + // TODO: get converted options so we can use them here (e.g. --debug, --config) + if debug, ok := req.Option("debug"); ok && debug.(bool) { u.Debug = true @@ -57,7 +77,17 @@ func main() { } } - //res := commands.Root.Call(req) + var res cmds.Response + if local { + // TODO: spin up node + res = root.Call(req) + } else { + res, err = sendRequest(req) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + } if res.Error() != nil { fmt.Println(res.Error().Error()) From 36bdc456d2b0b46a6b4d1de80b931d72168c7d08 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 24 Oct 2014 14:16:53 -0700 Subject: [PATCH 017/383] commands/http: Moved HTTP RPC into commands/http package --- cmd/ipfs/ipfs.go | 72 ++++-------------------------------------------- 1 file changed, 5 insertions(+), 67 deletions(-) diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index 1779fec1b..be0025840 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -1,16 +1,14 @@ package main import ( - "encoding/json" "fmt" "io" - "net/http" "os" "runtime/pprof" - "strings" cmds "github.com/jbenet/go-ipfs/commands" - "github.com/jbenet/go-ipfs/commands/cli" + cmdsCli "github.com/jbenet/go-ipfs/commands/cli" + cmdsHttp "github.com/jbenet/go-ipfs/commands/http" "github.com/jbenet/go-ipfs/core/commands" u "github.com/jbenet/go-ipfs/util" ) @@ -18,19 +16,17 @@ import ( // log is the command logger var log = u.Logger("cmd/ipfs") -const API_PATH = "/api/v0" - func main() { args := os.Args[1:] - req, err := cli.Parse(args, Root) + req, err := cmdsCli.Parse(args, Root) if err != nil { fmt.Println(err) os.Exit(1) } if len(req.Path()) == 0 { - req, err = cli.Parse(args, commands.Root) + req, err = cmdsCli.Parse(args, commands.Root) if err != nil { fmt.Println(err) os.Exit(1) @@ -82,7 +78,7 @@ func main() { // TODO: spin up node res = root.Call(req) } else { - res, err = sendRequest(req) + res, err = cmdsHttp.Send(req) if err != nil { fmt.Println(err) os.Exit(1) @@ -105,61 +101,3 @@ func main() { fmt.Println(err.Error()) } } - -func sendRequest(req cmds.Request) (cmds.Response, error) { - // TODO: load RPC host from config - url := "http://localhost:8080" + API_PATH - url += "/" + strings.Join(req.Path(), "/") - - // TODO: support other encodings once we have multicodec to decode response - // (we shouldn't have to set this here) - encoding := cmds.JSON - req.SetOption(cmds.EncShort, encoding) - - query := "?" - options := req.Options() - for k, v := range options { - query += "&" + k + "=" + v.(string) - } - - httpRes, err := http.Post(url+query, "application/octet-stream", req.Stream()) - if err != nil { - return nil, err - } - - res := cmds.NewResponse(req) - - contentType := httpRes.Header["Content-Type"][0] - contentType = strings.Split(contentType, ";")[0] - - if contentType == "application/octet-stream" { - res.SetValue(httpRes.Body) - return res, nil - } - - // TODO: decode based on `encoding`, using multicodec - dec := json.NewDecoder(httpRes.Body) - - if httpRes.StatusCode >= http.StatusBadRequest { - e := cmds.Error{} - err = dec.Decode(&e) - if err != nil { - fmt.Println(err) - return nil, err - } - - res.SetError(e, e.Code) - - } else { - var v interface{} - err = dec.Decode(&v) - if err != nil { - fmt.Println(err) - return nil, err - } - - res.SetValue(v) - } - - return res, nil -} From e82532c7d158fce459326d46431934ab3eb78ed8 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 24 Oct 2014 14:31:08 -0700 Subject: [PATCH 018/383] cmd/ipfs: Added ipfs.exe to .gitignore --- cmd/ipfs/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/ipfs/.gitignore b/cmd/ipfs/.gitignore index 8de552d0c..1cc9fdff7 100644 --- a/cmd/ipfs/.gitignore +++ b/cmd/ipfs/.gitignore @@ -1 +1,2 @@ ./ipfs +./ipfs.exe From b499c90db3d51524b91b9eea4d6d1addc7bb2d56 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 24 Oct 2014 14:32:39 -0700 Subject: [PATCH 019/383] commands/http: Moved HTTP RPC handler into commands/http --- server/http/http.go | 72 --------------------------------------------- 1 file changed, 72 deletions(-) diff --git a/server/http/http.go b/server/http/http.go index 616b09325..f1c6cbb5d 100644 --- a/server/http/http.go +++ b/server/http/http.go @@ -12,25 +12,19 @@ import ( manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - cmds "github.com/jbenet/go-ipfs/commands" core "github.com/jbenet/go-ipfs/core" - "github.com/jbenet/go-ipfs/core/commands" ) type objectHandler struct { ipfs } -type apiHandler struct{} - // Serve starts the http server func Serve(address ma.Multiaddr, node *core.IpfsNode) error { r := mux.NewRouter() objectHandler := &objectHandler{&ipfsHandler{node}} apiHandler := &apiHandler{} - r.PathPrefix("/api/v0/").Handler(apiHandler).Methods("GET", "POST") - r.HandleFunc("/ipfs/", objectHandler.postHandler).Methods("POST") r.PathPrefix("/ipfs/").Handler(objectHandler).Methods("GET") @@ -84,69 +78,3 @@ func (i *objectHandler) postHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusCreated) w.Write([]byte(mh.Multihash(k).B58String())) } - -func (i *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - path := strings.Split(r.URL.Path, "/")[3:] - opts := getOptions(r) - - // TODO: get args - - // ensure the requested command exists, otherwise 404 - _, err := commands.Root.Get(path) - if err != nil { - w.WriteHeader(http.StatusNotFound) - w.Write([]byte("404 page not found")) - return - } - - // build the Request and call the command - req := cmds.NewRequest(path, opts, nil, nil) - res := commands.Root.Call(req) - - // set the Content-Type based on res output - if _, ok := res.Value().(io.Reader); ok { - // TODO: set based on actual Content-Type of file - w.Header().Set("Content-Type", "application/octet-stream") - } else { - // TODO: get proper MIME type for encoding from multicodec lib - enc, _ := req.Option(cmds.EncShort) - w.Header().Set("Content-Type", "application/"+enc.(string)) - } - - // if response contains an error, write an HTTP error status code - if e := res.Error(); e != nil { - if e.Code == cmds.ErrClient { - w.WriteHeader(http.StatusBadRequest) - } else { - w.WriteHeader(http.StatusInternalServerError) - } - } - - _, err = io.Copy(w, res) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Header().Set("Content-Type", "text/plain") - w.Write([]byte(err.Error())) - } -} - -// getOptions returns the command options in the given HTTP request -// (from the querystring and request body) -func getOptions(r *http.Request) map[string]interface{} { - opts := make(map[string]interface{}) - - query := r.URL.Query() - for k, v := range query { - opts[k] = v[0] - } - - // TODO: get more options from request body (formdata, json, etc) - - _, short := opts[cmds.EncShort] - _, long := opts[cmds.EncLong] - if !short && !long { - opts[cmds.EncShort] = cmds.JSON - } - - return opts -} From b4fc0dba9620c86ac4523843d55e1c2180a6b583 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 24 Oct 2014 15:13:34 -0700 Subject: [PATCH 020/383] cmd/ipfs: Added basic daemon command --- cmd/ipfs/daemon.go | 27 +++++++++++++++++++++++++++ cmd/ipfs/root.go | 7 +------ 2 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 cmd/ipfs/daemon.go diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go new file mode 100644 index 000000000..b294cdf98 --- /dev/null +++ b/cmd/ipfs/daemon.go @@ -0,0 +1,27 @@ +package main + +import ( + "net/http" + + cmds "github.com/jbenet/go-ipfs/commands" + cmdsHttp "github.com/jbenet/go-ipfs/commands/http" +) + +var Daemon = &cmds.Command{ + Options: []cmds.Option{}, + Help: "TODO", + Subcommands: map[string]*cmds.Command{}, + Run: daemonFunc, +} + +func daemonFunc(req cmds.Request, res cmds.Response) { + handler := cmdsHttp.Handler{} + http.Handle(cmdsHttp.ApiPath+"/", handler) + // TODO: load listen address/port from config/options + err := http.ListenAndServe(":8080", nil) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + // TODO: log to indicate that we are now listening +} diff --git a/cmd/ipfs/root.go b/cmd/ipfs/root.go index 154381cbb..bd84f43a8 100644 --- a/cmd/ipfs/root.go +++ b/cmd/ipfs/root.go @@ -9,11 +9,6 @@ var Root = &cmds.Command{ Options: commands.Root.Options, Help: commands.Root.Help, Subcommands: map[string]*cmds.Command{ - "test": &cmds.Command{ - Run: func(req cmds.Request, res cmds.Response) { - v := "hello, world" - res.SetValue(v) - }, - }, + "daemon": Daemon, }, } From d87aad1e3af5142692292cec78f6a833523f2aee Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 27 Oct 2014 15:30:13 -0700 Subject: [PATCH 021/383] cmd/ipfs: Do command option conversion in CLI entry point --- cmd/ipfs/ipfs.go | 53 +++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index be0025840..aa8eb93e2 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -18,46 +18,37 @@ var log = u.Logger("cmd/ipfs") func main() { args := os.Args[1:] + root := Root - req, err := cmdsCli.Parse(args, Root) + req, err := cmdsCli.Parse(args, root) if err != nil { fmt.Println(err) os.Exit(1) } + // if the CLI-specific root doesn't contain the command, use the general root if len(req.Path()) == 0 { - req, err = cmdsCli.Parse(args, commands.Root) + root = commands.Root + req, err = cmdsCli.Parse(args, root) if err != nil { fmt.Println(err) os.Exit(1) } } - var local bool // TODO: option to force local - var root *cmds.Command - cmd, err := Root.Get(req.Path()) - if err == nil { - local = true - root = Root - - } else if local { + cmd, err := root.Get(req.Path()) + if err != nil { fmt.Println(err) os.Exit(1) - - } else { - cmd, err = commands.Root.Get(req.Path()) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - local = false - root = commands.Root } - // TODO: get converted options so we can use them here (e.g. --debug, --config) + options, err := getOptions(req, root) + if err != nil { + fmt.Println(err) + os.Exit(1) + } - if debug, ok := req.Option("debug"); ok && debug.(bool) { + if debug, ok := options["debug"]; ok && debug.(bool) { u.Debug = true // if debugging, setup profiling. @@ -74,7 +65,7 @@ func main() { } var res cmds.Response - if local { + if root == Root { // TODO: spin up node res = root.Call(req) } else { @@ -101,3 +92,19 @@ func main() { fmt.Println(err.Error()) } } + +func getOptions(req cmds.Request, root *cmds.Command) (map[string]interface{}, error) { + tempReq := cmds.NewRequest(req.Path(), req.Options(), nil, nil) + + options, err := root.GetOptions(tempReq.Path()) + if err != nil { + return nil, err + } + + err = tempReq.ConvertOptions(options) + if err != nil { + return nil, err + } + + return tempReq.Options(), nil +} From e011d950bb71c4b0e32248058c31af43e32338a9 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 27 Oct 2014 16:20:48 -0700 Subject: [PATCH 022/383] cmd/ipfs: Obtain lock when starting daemon --- cmd/ipfs/daemon.go | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index b294cdf98..1ece04048 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -1,12 +1,19 @@ package main import ( + "fmt" "net/http" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/camlistore/lock" + cmds "github.com/jbenet/go-ipfs/commands" cmdsHttp "github.com/jbenet/go-ipfs/commands/http" + "github.com/jbenet/go-ipfs/config" ) +// DaemonLockFile is the filename of the daemon lock, relative to config dir +const DaemonLockFile = "daemon.lock" + var Daemon = &cmds.Command{ Options: []cmds.Option{}, Help: "TODO", @@ -15,13 +22,33 @@ var Daemon = &cmds.Command{ } func daemonFunc(req cmds.Request, res cmds.Response) { + configPath, err := getConfigPath(req) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + lockPath, err := config.Path(configPath, DaemonLockFile) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + lk, err := lock.Lock(lockPath) + if err != nil { + res.SetError(fmt.Errorf("Couldn't obtain lock. Is another daemon already running?"), cmds.ErrNormal) + return + } + defer lk.Close() + handler := cmdsHttp.Handler{} http.Handle(cmdsHttp.ApiPath+"/", handler) // TODO: load listen address/port from config/options - err := http.ListenAndServe(":8080", nil) + err = http.ListenAndServe(":8080", nil) if err != nil { res.SetError(err, cmds.ErrNormal) return } // TODO: log to indicate that we are now listening + } From e2ed10b85bf70be7d1c321e32d136a6c334a9710 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 27 Oct 2014 16:21:19 -0700 Subject: [PATCH 023/383] cmd/ipfs: Use lock to check if daemon is running, if not then run command locally --- cmd/ipfs/ipfs.go | 51 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index aa8eb93e2..e8316ca29 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -6,9 +6,12 @@ import ( "os" "runtime/pprof" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/camlistore/lock" + cmds "github.com/jbenet/go-ipfs/commands" cmdsCli "github.com/jbenet/go-ipfs/commands/cli" cmdsHttp "github.com/jbenet/go-ipfs/commands/http" + "github.com/jbenet/go-ipfs/config" "github.com/jbenet/go-ipfs/core/commands" u "github.com/jbenet/go-ipfs/util" ) @@ -48,7 +51,7 @@ func main() { os.Exit(1) } - if debug, ok := options["debug"]; ok && debug.(bool) { + if debug, found := options.Option("debug"); found && debug.(bool) { u.Debug = true // if debugging, setup profiling. @@ -66,14 +69,40 @@ func main() { var res cmds.Response if root == Root { - // TODO: spin up node res = root.Call(req) + } else { - res, err = cmdsHttp.Send(req) + local := true + + configPath, err := getConfigPath(options) if err != nil { fmt.Println(err) os.Exit(1) } + + lockFilePath, err := config.Path(configPath, DaemonLockFile) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + if lk, err := lock.Lock(lockFilePath); err != nil { + local = false + } else { + lk.Close() + } + + if !local { + res, err = cmdsHttp.Send(req) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + } else { + // TODO: spin up node + res = root.Call(req) + } } if res.Error() != nil { @@ -93,7 +122,7 @@ func main() { } } -func getOptions(req cmds.Request, root *cmds.Command) (map[string]interface{}, error) { +func getOptions(req cmds.Request, root *cmds.Command) (cmds.Request, error) { tempReq := cmds.NewRequest(req.Path(), req.Options(), nil, nil) options, err := root.GetOptions(tempReq.Path()) @@ -106,5 +135,17 @@ func getOptions(req cmds.Request, root *cmds.Command) (map[string]interface{}, e return nil, err } - return tempReq.Options(), nil + return tempReq, nil +} + +func getConfigPath(req cmds.Request) (string, error) { + if opt, found := req.Option("config"); found { + return opt.(string), nil + } + + configPath, err := config.PathRoot() + if err != nil { + return "", err + } + return configPath, nil } From cf60fdd3d9e266dd56d1fb45bfab4fd5b50a6136 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 27 Oct 2014 17:01:00 -0700 Subject: [PATCH 024/383] cmd/ipfs: Load API address from config --- cmd/ipfs/daemon.go | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index 1ece04048..79a4b4df3 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -5,6 +5,8 @@ import ( "net/http" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/camlistore/lock" + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" cmds "github.com/jbenet/go-ipfs/commands" cmdsHttp "github.com/jbenet/go-ipfs/commands/http" @@ -41,10 +43,33 @@ func daemonFunc(req cmds.Request, res cmds.Response) { } defer lk.Close() + configFile, err := config.Filename(configPath) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + config, err := config.Load(configFile) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + addr, err := ma.NewMultiaddr(config.Addresses.API) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + _, host, err := manet.DialArgs(addr) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + handler := cmdsHttp.Handler{} http.Handle(cmdsHttp.ApiPath+"/", handler) - // TODO: load listen address/port from config/options - err = http.ListenAndServe(":8080", nil) + err = http.ListenAndServe(host, nil) if err != nil { res.SetError(err, cmds.ErrNormal) return From 7d53e736cf0a525808864585b52e5df0c4107343 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 27 Oct 2014 17:05:48 -0700 Subject: [PATCH 025/383] cmd/ipfs: Added getConfig function --- cmd/ipfs/daemon.go | 8 +------- cmd/ipfs/ipfs.go | 9 +++++++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index 79a4b4df3..88fa1f508 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -43,13 +43,7 @@ func daemonFunc(req cmds.Request, res cmds.Response) { } defer lk.Close() - configFile, err := config.Filename(configPath) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - config, err := config.Load(configFile) + config, err := getConfig(configPath) if err != nil { res.SetError(err, cmds.ErrNormal) return diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index e8316ca29..dbff801ed 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -149,3 +149,12 @@ func getConfigPath(req cmds.Request) (string, error) { } return configPath, nil } + +func getConfig(path string) (*config.Config, error) { + configFile, err := config.Filename(path) + if err != nil { + return nil, err + } + + return config.Load(configFile) +} From 2419ffdb2d074e5906922072942fda12d486a51a Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 27 Oct 2014 17:32:19 -0700 Subject: [PATCH 026/383] cmd/ipfs: Load config and put in request context --- cmd/ipfs/daemon.go | 2 +- cmd/ipfs/ipfs.go | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index 88fa1f508..be74e8243 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -24,7 +24,7 @@ var Daemon = &cmds.Command{ } func daemonFunc(req cmds.Request, res cmds.Response) { - configPath, err := getConfigPath(req) + configPath, err := getConfigRoot(req) if err != nil { res.SetError(err, cmds.ErrNormal) return diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index dbff801ed..87d331986 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -67,6 +67,22 @@ func main() { } } + configPath, err := getConfigRoot(options) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + conf, err := getConfig(configPath) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + ctx := req.Context() + ctx.ConfigRoot = configPath + ctx.Config = conf + var res cmds.Response if root == Root { res = root.Call(req) @@ -74,12 +90,6 @@ func main() { } else { local := true - configPath, err := getConfigPath(options) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - lockFilePath, err := config.Path(configPath, DaemonLockFile) if err != nil { fmt.Println(err) @@ -138,7 +148,7 @@ func getOptions(req cmds.Request, root *cmds.Command) (cmds.Request, error) { return tempReq, nil } -func getConfigPath(req cmds.Request) (string, error) { +func getConfigRoot(req cmds.Request) (string, error) { if opt, found := req.Option("config"); found { return opt.(string), nil } From 7efa174f2d7b59ed7cd5b6bdd9202e113e08ecb0 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 27 Oct 2014 17:49:18 -0700 Subject: [PATCH 027/383] cmd/ipfs: Made daemon command use request context instead of loading config --- cmd/ipfs/daemon.go | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index be74e8243..1912b209b 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -24,13 +24,9 @@ var Daemon = &cmds.Command{ } func daemonFunc(req cmds.Request, res cmds.Response) { - configPath, err := getConfigRoot(req) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } + ctx := req.Context() - lockPath, err := config.Path(configPath, DaemonLockFile) + lockPath, err := config.Path(ctx.ConfigRoot, DaemonLockFile) if err != nil { res.SetError(err, cmds.ErrNormal) return @@ -43,13 +39,7 @@ func daemonFunc(req cmds.Request, res cmds.Response) { } defer lk.Close() - config, err := getConfig(configPath) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - addr, err := ma.NewMultiaddr(config.Addresses.API) + addr, err := ma.NewMultiaddr(ctx.Config.Addresses.API) if err != nil { res.SetError(err, cmds.ErrNormal) return From d67e1e50683dafd3fcb8786a122dd101908278b4 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 27 Oct 2014 17:49:31 -0700 Subject: [PATCH 028/383] daemon: Replaced daemon package with lock functions --- daemon/daemon.go | 163 +++------------------------------------- daemon/daemon_client.go | 107 -------------------------- daemon/daemon_test.go | 86 --------------------- 3 files changed, 11 insertions(+), 345 deletions(-) delete mode 100644 daemon/daemon_client.go delete mode 100644 daemon/daemon_test.go diff --git a/daemon/daemon.go b/daemon/daemon.go index c8c150a8d..4e5bd28d5 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -1,166 +1,25 @@ package daemon import ( - "encoding/json" - "fmt" "io" - "os" "path" - "sync" - - core "github.com/jbenet/go-ipfs/core" - "github.com/jbenet/go-ipfs/core/commands" - u "github.com/jbenet/go-ipfs/util" lock "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/camlistore/lock" - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" ) -var log = u.Logger("daemon") - // LockFile is the filename of the daemon lock, relative to config dir const LockFile = "daemon.lock" -// DaemonListener listens to an initialized IPFS node and can send it commands instead of -// starting up a new set of connections -type DaemonListener struct { - node *core.IpfsNode - list manet.Listener - closed bool - wg sync.WaitGroup - lk io.Closer -} - -//Command accepts user input and can be sent to the running IPFS node -type Command struct { - Command string - Args []string - Opts map[string]interface{} -} - -func NewDaemonListener(ipfsnode *core.IpfsNode, addr ma.Multiaddr, confdir string) (*DaemonListener, error) { - var err error - confdir, err = u.TildeExpansion(confdir) - if err != nil { - return nil, err - } - - lk, err := daemonLock(confdir) - if err != nil { - return nil, err - } - - ofi, err := os.Create(confdir + "/rpcaddress") - if err != nil { - log.Warningf("Could not create rpcaddress file: %s", err) - return nil, err - } - - _, err = ofi.Write([]byte(addr.String())) - if err != nil { - log.Warningf("Could not write to rpcaddress file: %s", err) - return nil, err - } - ofi.Close() - - list, err := manet.Listen(addr) - if err != nil { - return nil, err - } - log.Info("New daemon listener initialized.") - - return &DaemonListener{ - node: ipfsnode, - list: list, - lk: lk, - }, nil -} - -func NewCommand() *Command { - return &Command{ - Opts: make(map[string]interface{}), - } -} - -func (dl *DaemonListener) Listen() { - if dl.closed { - panic("attempting to listen on a closed daemon Listener") - } - - // add ourselves to workgroup. and remove ourselves when done. - dl.wg.Add(1) - defer dl.wg.Done() - - log.Info("daemon listening") - for { - conn, err := dl.list.Accept() - if err != nil { - if !dl.closed { - log.Warning("DaemonListener Accept: %v", err) - } - return - } - go dl.handleConnection(conn) - } -} - -func (dl *DaemonListener) handleConnection(conn manet.Conn) { - defer conn.Close() - - dec := json.NewDecoder(conn) - - var command Command - err := dec.Decode(&command) - if err != nil { - fmt.Fprintln(conn, err) - return - } - - log.Debug("Got command: %v", command) - switch command.Command { - case "add": - err = commands.Add(dl.node, command.Args, command.Opts, conn) - case "cat": - err = commands.Cat(dl.node, command.Args, command.Opts, conn) - case "ls": - err = commands.Ls(dl.node, command.Args, command.Opts, conn) - case "pin": - err = commands.Pin(dl.node, command.Args, command.Opts, conn) - case "publish": - err = commands.Publish(dl.node, command.Args, command.Opts, conn) - case "resolve": - err = commands.Resolve(dl.node, command.Args, command.Opts, conn) - case "diag": - err = commands.Diag(dl.node, command.Args, command.Opts, conn) - case "blockGet": - err = commands.BlockGet(dl.node, command.Args, command.Opts, conn) - case "blockPut": - err = commands.BlockPut(dl.node, command.Args, command.Opts, conn) - case "log": - err = commands.Log(dl.node, command.Args, command.Opts, conn) - case "unpin": - err = commands.Unpin(dl.node, command.Args, command.Opts, conn) - case "updateApply": - command.Opts["onDaemon"] = true - err = commands.UpdateApply(dl.node, command.Args, command.Opts, conn) - default: - err = fmt.Errorf("Invalid Command: '%s'", command.Command) - } - if err != nil { - log.Errorf("%s: %s", command.Command, err) - fmt.Fprintln(conn, err) - } -} - -func (dl *DaemonListener) Close() error { - dl.closed = true - err := dl.list.Close() - dl.wg.Wait() // wait till done before releasing lock. - dl.lk.Close() - return err -} - -func daemonLock(confdir string) (io.Closer, error) { +func Lock(confdir string) (io.Closer, error) { return lock.Lock(path.Join(confdir, LockFile)) } + +func Locked(confdir string) bool { + if lk, err := Lock(confdir); err != nil { + return true + + } else { + lk.Close() + return false + } +} diff --git a/daemon/daemon_client.go b/daemon/daemon_client.go deleted file mode 100644 index 031d439c4..000000000 --- a/daemon/daemon_client.go +++ /dev/null @@ -1,107 +0,0 @@ -package daemon - -import ( - "bufio" - "encoding/json" - "errors" - "io" - "os" - - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" - - u "github.com/jbenet/go-ipfs/util" -) - -// ErrDaemonNotRunning is returned when attempting to retrieve the daemon's -// address and the daemon is not actually running. -var ErrDaemonNotRunning = errors.New("daemon not running") - -func getDaemonAddr(confdir string) (string, error) { - var err error - confdir, err = u.TildeExpansion(confdir) - if err != nil { - return "", err - } - fi, err := os.Open(confdir + "/rpcaddress") - if err != nil { - log.Debug("getDaemonAddr failed: %s", err) - if err == os.ErrNotExist { - return "", ErrDaemonNotRunning - } - return "", err - } - - read := bufio.NewReader(fi) - - // TODO: operating system agostic line delim - line, err := read.ReadBytes('\n') - if err != nil && err != io.EOF { - return "", err - } - return string(line), nil -} - -// SendCommand attempts to run the command over a currently-running daemon. -// If there is no running daemon, returns ErrDaemonNotRunning. This is done -// over network RPC API. The address of the daemon is retrieved from the config -// directory, where live daemons write their addresses to special files. -func SendCommand(command *Command, confdir string) error { - server := os.Getenv("IPFS_ADDRESS_RPC") - - if server == "" { - //check if daemon is running - log.Info("Checking if daemon is running...") - if !serverIsRunning(confdir) { - return ErrDaemonNotRunning - } - - log.Info("Daemon is running!") - - var err error - server, err = getDaemonAddr(confdir) - if err != nil { - return err - } - } - - return serverComm(server, command) -} - -func serverIsRunning(confdir string) bool { - var err error - confdir, err = u.TildeExpansion(confdir) - if err != nil { - log.Errorf("Tilde Expansion Failed: %s", err) - return false - } - lk, err := daemonLock(confdir) - if err == nil { - lk.Close() - return false - } - return true -} - -func serverComm(server string, command *Command) error { - log.Info("Daemon address: %s", server) - maddr, err := ma.NewMultiaddr(server) - if err != nil { - return err - } - - conn, err := manet.Dial(maddr) - if err != nil { - return err - } - - enc := json.NewEncoder(conn) - err = enc.Encode(command) - if err != nil { - return err - } - - io.Copy(os.Stdout, conn) - - return nil -} diff --git a/daemon/daemon_test.go b/daemon/daemon_test.go deleted file mode 100644 index 7fba74269..000000000 --- a/daemon/daemon_test.go +++ /dev/null @@ -1,86 +0,0 @@ -package daemon - -import ( - "encoding/base64" - "os" - "testing" - - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - config "github.com/jbenet/go-ipfs/config" - core "github.com/jbenet/go-ipfs/core" - ci "github.com/jbenet/go-ipfs/crypto" - peer "github.com/jbenet/go-ipfs/peer" -) - -func TestInitializeDaemonListener(t *testing.T) { - - priv, pub, err := ci.GenerateKeyPair(ci.RSA, 512) - if err != nil { - t.Fatal(err) - } - prbytes, err := priv.Bytes() - if err != nil { - t.Fatal(err) - } - - ident, _ := peer.IDFromPubKey(pub) - privKey := base64.StdEncoding.EncodeToString(prbytes) - pID := ident.Pretty() - - id := config.Identity{ - PeerID: pID, - PrivKey: privKey, - } - - nodeConfigs := []*config.Config{ - &config.Config{ - Identity: id, - Datastore: config.Datastore{ - Type: "memory", - }, - Addresses: config.Addresses{ - Swarm: "/ip4/0.0.0.0/tcp/4001", - API: "/ip4/127.0.0.1/tcp/8000", - }, - }, - - &config.Config{ - Identity: id, - Datastore: config.Datastore{ - Type: "leveldb", - Path: ".test/datastore", - }, - Addresses: config.Addresses{ - Swarm: "/ip4/0.0.0.0/tcp/4001", - API: "/ip4/127.0.0.1/tcp/8000", - }, - }, - } - - var tempConfigDir = ".test" - err = os.MkdirAll(tempConfigDir, os.ModeDir|0777) - if err != nil { - t.Fatalf("error making temp config dir: %v", err) - } - - for _, c := range nodeConfigs { - - node, _ := core.NewIpfsNode(c, false) - addr, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/1327") - if err != nil { - t.Fatal(err) - } - - dl, initErr := NewDaemonListener(node, addr, tempConfigDir) - if initErr != nil { - t.Fatal(initErr) - } - - closeErr := dl.Close() - if closeErr != nil { - t.Fatal(closeErr) - } - - } - -} From 3f4da97c58e613cf015ef56cc0e4b2a130686100 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 27 Oct 2014 17:50:32 -0700 Subject: [PATCH 029/383] cmd/ipfs: Use daemon lock functions --- cmd/ipfs/daemon.go | 14 ++------------ cmd/ipfs/ipfs.go | 19 ++----------------- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index 1912b209b..7aba1e94f 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -4,18 +4,14 @@ import ( "fmt" "net/http" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/camlistore/lock" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" cmds "github.com/jbenet/go-ipfs/commands" cmdsHttp "github.com/jbenet/go-ipfs/commands/http" - "github.com/jbenet/go-ipfs/config" + "github.com/jbenet/go-ipfs/daemon" ) -// DaemonLockFile is the filename of the daemon lock, relative to config dir -const DaemonLockFile = "daemon.lock" - var Daemon = &cmds.Command{ Options: []cmds.Option{}, Help: "TODO", @@ -26,13 +22,7 @@ var Daemon = &cmds.Command{ func daemonFunc(req cmds.Request, res cmds.Response) { ctx := req.Context() - lockPath, err := config.Path(ctx.ConfigRoot, DaemonLockFile) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - lk, err := lock.Lock(lockPath) + lk, err := daemon.Lock(ctx.ConfigRoot) if err != nil { res.SetError(fmt.Errorf("Couldn't obtain lock. Is another daemon already running?"), cmds.ErrNormal) return diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index 87d331986..3a99884a9 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -6,13 +6,12 @@ import ( "os" "runtime/pprof" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/camlistore/lock" - cmds "github.com/jbenet/go-ipfs/commands" cmdsCli "github.com/jbenet/go-ipfs/commands/cli" cmdsHttp "github.com/jbenet/go-ipfs/commands/http" "github.com/jbenet/go-ipfs/config" "github.com/jbenet/go-ipfs/core/commands" + "github.com/jbenet/go-ipfs/daemon" u "github.com/jbenet/go-ipfs/util" ) @@ -88,21 +87,7 @@ func main() { res = root.Call(req) } else { - local := true - - lockFilePath, err := config.Path(configPath, DaemonLockFile) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - if lk, err := lock.Lock(lockFilePath); err != nil { - local = false - } else { - lk.Close() - } - - if !local { + if daemon.Locked(configPath) { res, err = cmdsHttp.Send(req) if err != nil { fmt.Println(err) From 0b73a48b843cbed89b5081f2e564d7f67daaeb80 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 27 Oct 2014 18:14:24 -0700 Subject: [PATCH 030/383] commands/http: Made Handler set request contexts --- cmd/ipfs/daemon.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index 7aba1e94f..acdbf18d8 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -20,6 +20,8 @@ var Daemon = &cmds.Command{ } func daemonFunc(req cmds.Request, res cmds.Response) { + // TODO: spin up a core.IpfsNode + ctx := req.Context() lk, err := daemon.Lock(ctx.ConfigRoot) @@ -41,7 +43,7 @@ func daemonFunc(req cmds.Request, res cmds.Response) { return } - handler := cmdsHttp.Handler{} + handler := cmdsHttp.Handler{*ctx} http.Handle(cmdsHttp.ApiPath+"/", handler) err = http.ListenAndServe(host, nil) if err != nil { From c67978308453cc83f876af5fe633bae35a0bbc25 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 27 Oct 2014 18:35:51 -0700 Subject: [PATCH 031/383] cmd/ipfs: Log to show API server is listening --- cmd/ipfs/daemon.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index acdbf18d8..102dadfc1 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -45,11 +45,12 @@ func daemonFunc(req cmds.Request, res cmds.Response) { handler := cmdsHttp.Handler{*ctx} http.Handle(cmdsHttp.ApiPath+"/", handler) + + fmt.Printf("API server listening on '%s'\n", host) + err = http.ListenAndServe(host, nil) if err != nil { res.SetError(err, cmds.ErrNormal) return } - // TODO: log to indicate that we are now listening - } From 8edfee2a582b559d0aa73ca6f19a3ace0c084ea7 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 27 Oct 2014 18:46:01 -0700 Subject: [PATCH 032/383] cmd/ipfs: Removed new commands (to be refactored later) --- cmd/ipfs/bootstrap.go | 236 ------------------------------------------ cmd/ipfs/config.go | 143 ------------------------- cmd/ipfs/tour.go | 134 ------------------------ 3 files changed, 513 deletions(-) delete mode 100644 cmd/ipfs/bootstrap.go delete mode 100644 cmd/ipfs/config.go delete mode 100644 cmd/ipfs/tour.go diff --git a/cmd/ipfs/bootstrap.go b/cmd/ipfs/bootstrap.go deleted file mode 100644 index 6f6a78574..000000000 --- a/cmd/ipfs/bootstrap.go +++ /dev/null @@ -1,236 +0,0 @@ -package main - -import ( - "errors" - "strings" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" - ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - - config "github.com/jbenet/go-ipfs/config" - peer "github.com/jbenet/go-ipfs/peer" - u "github.com/jbenet/go-ipfs/util" -) - -var cmdIpfsBootstrap = &commander.Command{ - UsageLine: "bootstrap", - Short: "Show a list of bootstrapped addresses.", - Long: `ipfs bootstrap - show, or manipulate bootstrap node addresses - -Running 'ipfs bootstrap' with no arguments will run 'ipfs bootstrap list'. - -Commands: - - list Show the boostrap list. - add
Add a node's address to the bootstrap list. - remove
Remove an address from the bootstrap list. - -` + bootstrapSecurityWarning, - Run: bootstrapListCmd, - Subcommands: []*commander.Command{ - cmdIpfsBootstrapRemove, - cmdIpfsBootstrapAdd, - cmdIpfsBootstrapList, - }, - Flag: *flag.NewFlagSet("ipfs-bootstrap", flag.ExitOnError), -} - -var cmdIpfsBootstrapRemove = &commander.Command{ - UsageLine: "remove
", - Short: "Remove addresses from the bootstrap list.", - Long: `ipfs bootstrap remove - remove addresses from the bootstrap list -` + bootstrapSecurityWarning, - Run: bootstrapRemoveCmd, - Flag: *flag.NewFlagSet("ipfs-bootstrap-remove", flag.ExitOnError), -} - -var cmdIpfsBootstrapAdd = &commander.Command{ - UsageLine: "add
", - Short: "Add addresses to the bootstrap list.", - Long: `ipfs bootstrap add - add addresses to the bootstrap list -` + bootstrapSecurityWarning, - Run: bootstrapAddCmd, - Flag: *flag.NewFlagSet("ipfs-bootstrap-add", flag.ExitOnError), -} - -var cmdIpfsBootstrapList = &commander.Command{ - UsageLine: "list", - Short: "Show addresses in the bootstrap list.", - Run: bootstrapListCmd, - Flag: *flag.NewFlagSet("ipfs-bootstrap-list", flag.ExitOnError), -} - -func bootstrapRemoveCmd(c *commander.Command, inp []string) error { - - if len(inp) == 0 { - return errors.New("remove: no address or peerid specified") - } - - toRemove, err := bootstrapInputToPeers(inp) - if err != nil { - return err - } - - cfg, err := getConfig(c) - if err != nil { - return err - } - - keep := []*config.BootstrapPeer{} - remove := []*config.BootstrapPeer{} - - // function to filer what to keep - shouldKeep := func(bp *config.BootstrapPeer) bool { - for _, skipBP := range toRemove { - - // IDs must match to skip. - if bp.PeerID != skipBP.PeerID { - continue - } - - // if Addresses match, or skipBP has no addr (wildcard) - if skipBP.Address == bp.Address || skipBP.Address == "" { - return false - } - } - return true - } - - // filter all the existing peers - for _, currBP := range cfg.Bootstrap { - if shouldKeep(currBP) { - keep = append(keep, currBP) - } else { - remove = append(remove, currBP) - } - } - - // if didn't remove anyone, bail. - if len(keep) == len(cfg.Bootstrap) { - return errors.New("remove: peer given did not match any in list") - } - - // write new config - cfg.Bootstrap = keep - if err := writeConfig(c, cfg); err != nil { - return err - } - - for _, bp := range remove { - u.POut("removed %s\n", bp) - } - return nil -} - -func bootstrapAddCmd(c *commander.Command, inp []string) error { - - if len(inp) == 0 { - return errors.New("add: no address specified") - } - - toAdd, err := bootstrapInputToPeers(inp) - if err != nil { - return err - } - - cfg, err := getConfig(c) - if err != nil { - return err - } - - // function to check whether a peer is already in the list. - combine := func(lists ...[]*config.BootstrapPeer) []*config.BootstrapPeer { - - set := map[string]struct{}{} - final := []*config.BootstrapPeer{} - - for _, list := range lists { - for _, peer := range list { - // if already in the set, continue - _, found := set[peer.String()] - if found { - continue - } - - set[peer.String()] = struct{}{} - final = append(final, peer) - } - } - return final - } - - // combine both lists, removing dups. - cfg.Bootstrap = combine(cfg.Bootstrap, toAdd) - if err := writeConfig(c, cfg); err != nil { - return err - } - - for _, bp := range toAdd { - u.POut("added %s\n", bp) - } - return nil -} - -func bootstrapListCmd(c *commander.Command, inp []string) error { - - cfg, err := getConfig(c) - if err != nil { - return err - } - - for _, bp := range cfg.Bootstrap { - u.POut("%s\n", bp) - } - - return nil -} - -func bootstrapInputToPeers(input []string) ([]*config.BootstrapPeer, error) { - split := func(addr string) (string, string) { - idx := strings.LastIndex(addr, "/") - if idx == -1 { - return "", addr - } - return addr[:idx], addr[idx+1:] - } - - peers := []*config.BootstrapPeer{} - for _, addr := range input { - addrS, peeridS := split(addr) - - // make sure addrS parses as a multiaddr. - if len(addrS) > 0 { - maddr, err := ma.NewMultiaddr(addrS) - if err != nil { - return nil, err - } - - addrS = maddr.String() - } - - // make sure idS parses as a peer.ID - peerid, err := mh.FromB58String(peeridS) - if err != nil { - return nil, err - } - - // construct config entry - peers = append(peers, &config.BootstrapPeer{ - Address: addrS, - PeerID: peer.ID(peerid).Pretty(), - }) - } - return peers, nil -} - -const bootstrapSecurityWarning = ` -SECURITY WARNING: - -The bootstrap command manipulates the "bootstrap list", which contains -the addresses of bootstrap nodes. These are the *trusted peers* from -which to learn about other peers in the network. Only edit this list -if you understand the risks of adding or removing nodes from this list. - -` diff --git a/cmd/ipfs/config.go b/cmd/ipfs/config.go deleted file mode 100644 index f1f8be92e..000000000 --- a/cmd/ipfs/config.go +++ /dev/null @@ -1,143 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "io" - "os" - "os/exec" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag" - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" - config "github.com/jbenet/go-ipfs/config" - u "github.com/jbenet/go-ipfs/util" -) - -var cmdIpfsConfig = &commander.Command{ - UsageLine: "config", - Short: "Get/Set ipfs config values", - Long: `ipfs config [] [] - Get/Set ipfs config values. - - ipfs config - Get value of - ipfs config - Set value of to - ipfs config --show - Show config file - ipfs config --edit - Edit config file in $EDITOR - -Examples: - - Get the value of the 'datastore.path' key: - - ipfs config datastore.path - - Set the value of the 'datastore.path' key: - - ipfs config datastore.path ~/.go-ipfs/datastore - -`, - Run: configCmd, - Flag: *flag.NewFlagSet("ipfs-config", flag.ExitOnError), -} - -func init() { - cmdIpfsConfig.Flag.Bool("edit", false, "Edit config file in $EDITOR") - cmdIpfsConfig.Flag.Bool("show", false, "Show config file") -} - -func configCmd(c *commander.Command, inp []string) error { - - confdir, err := getConfigDir(c.Parent) - if err != nil { - return err - } - - filename, err := config.Filename(confdir) - if err != nil { - return err - } - - // if editing, open the editor - if c.Flag.Lookup("edit").Value.Get().(bool) { - return configEditor(filename) - } - - // if showing, cat the file - if c.Flag.Lookup("show").Value.Get().(bool) { - return configCat(filename) - } - - if len(inp) == 0 { - // "ipfs config" run without parameters - u.POut(c.Long) - return nil - } - - // Getter (1 param) - if len(inp) == 1 { - value, err := config.ReadConfigKey(filename, inp[0]) - if err != nil { - return fmt.Errorf("Failed to get config value: %s", err) - } - - strval, ok := value.(string) - if ok { - u.POut("%s\n", strval) - return nil - } - - if err := config.Encode(os.Stdout, value); err != nil { - return fmt.Errorf("Failed to encode config value: %s", err) - } - u.POut("\n") - return nil - } - - // Setter (>1 params) - err = config.WriteConfigKey(filename, inp[0], inp[1]) - if err != nil { - return fmt.Errorf("Failed to set config value: %s", err) - } - - return nil -} - -func configCat(filename string) error { - - file, err := os.Open(filename) - if err != nil { - return err - } - defer file.Close() - - if _, err = io.Copy(os.Stdout, file); err != nil { - return err - } - u.POut("\n") - return nil -} - -func configEditor(filename string) error { - - editor := os.Getenv("EDITOR") - if editor == "" { - return errors.New("ENV variable $EDITOR not set") - } - - cmd := exec.Command("sh", "-c", editor+" "+filename) - cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr - return cmd.Run() -} - -func writeConfig(c *commander.Command, cfg *config.Config) error { - - confdir, err := getConfigDir(c) - if err != nil { - return err - } - - filename, err := config.Filename(confdir) - if err != nil { - return err - } - - return config.WriteConfigFile(filename, cfg) -} diff --git a/cmd/ipfs/tour.go b/cmd/ipfs/tour.go deleted file mode 100644 index 0656625e8..000000000 --- a/cmd/ipfs/tour.go +++ /dev/null @@ -1,134 +0,0 @@ -// +build linux darwin freebsd - -package main - -import ( - "fmt" - - config "github.com/jbenet/go-ipfs/config" - tour "github.com/jbenet/go-ipfs/tour" - - commander "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander" -) - -var cmdIpfsTour = &commander.Command{ - UsageLine: "tour []", - Short: "Take the IPFS Tour.", - Long: `ipfs tour - Take the IPFS Tour. - - ipfs tour [] - Show tour topic. Default to current. - ipfs tour next - Show the next tour topic. - ipfs tour list - Show a list of topics. - ipfs tour restart - Restart the tour. - -This is a tour that takes you through various IPFS concepts, -features, and tools to make sure you get up to speed with -IPFS very quickly. To start, run: - - ipfs tour -`, - Run: tourCmd, - Subcommands: []*commander.Command{ - cmdIpfsTourNext, - cmdIpfsTourList, - cmdIpfsTourRestart, - }, -} - -var cmdIpfsTourNext = &commander.Command{ - UsageLine: "next", - Short: "Show the next IPFS Tour topic.", - Run: tourNextCmd, -} - -var cmdIpfsTourList = &commander.Command{ - UsageLine: "list", - Short: "Show a list of IPFS Tour topics.", - Run: tourListCmd, -} - -var cmdIpfsTourRestart = &commander.Command{ - UsageLine: "restart", - Short: "Restart the IPFS Tour.", - Run: tourRestartCmd, -} - -func tourCmd(c *commander.Command, inp []string) error { - cfg, err := getConfig(c) - if err != nil { - return err - } - - topic := tour.TopicID(cfg.Tour.Last) - if len(inp) > 0 { - topic = tour.TopicID(inp[0]) - } - return tourShow(topic) -} - -func tourNextCmd(c *commander.Command, _ []string) error { - cfg, err := getConfig(c) - if err != nil { - return err - } - - topic := tour.NextTopic(tour.TopicID(cfg.Tour.Last)) - if err := tourShow(topic); err != nil { - return err - } - - // if topic didn't change (last) done - if string(topic) == cfg.Tour.Last { - return nil - } - - // topic changed, not last. write it out. - cfg.Tour.Last = string(topic) - return writeConfig(c, cfg) -} - -func tourListCmd(c *commander.Command, _ []string) error { - cfg, err := getConfig(c) - if err != nil { - return err - } - lastid := tour.TopicID(cfg.Tour.Last) - - for _, id := range tour.IDs { - c := ' ' - switch { - case id == lastid: - c = '*' - case id.LessThan(lastid): - c = '✓' - } - - t := tour.Topics[id] - fmt.Printf("- %c %-5.5s %s\n", c, id, t.Title) - } - return nil -} - -func tourRestartCmd(c *commander.Command, _ []string) error { - cfg, err := getConfig(c) - if err != nil { - return err - } - - cfg.Tour.Last = "" - return writeConfig(c, cfg) -} - -func tourShow(id tour.ID) error { - t, found := tour.Topics[id] - if !found { - return fmt.Errorf("no topic with id: %s", id) - } - - fmt.Printf("Tour %s - %s\n\n%s\n", t.ID, t.Title, t.Text) - return nil -} - -func lastTour(cfg *config.Config) string { - return "" -} From 063cb536df86a0fde304478d97d12b263cd6a132 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 27 Oct 2014 18:52:44 -0700 Subject: [PATCH 033/383] cmd/ipfs: Display help text when help flag is set --- cmd/ipfs/ipfs.go | 5 +++++ core/commands/root.go | 1 + 2 files changed, 6 insertions(+) diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index 3a99884a9..a0d4f4082 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -50,6 +50,11 @@ func main() { os.Exit(1) } + if help, found := options.Option("help"); found && help.(bool) { + fmt.Println(cmd.Help) + os.Exit(0) + } + if debug, found := options.Option("debug"); found && debug.(bool) { u.Debug = true diff --git a/core/commands/root.go b/core/commands/root.go index 69d093281..f1dd03f59 100644 --- a/core/commands/root.go +++ b/core/commands/root.go @@ -15,6 +15,7 @@ var Root = &cmds.Command{ Options: []cmds.Option{ cmds.Option{[]string{"config", "c"}, cmds.String}, cmds.Option{[]string{"debug", "D"}, cmds.Bool}, + cmds.Option{[]string{"help", "h"}, cmds.Bool}, }, Help: `ipfs - global versioned p2p merkledag file system From 436462c86a930726ac23dcea3a0287cd4a759908 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 27 Oct 2014 18:59:16 -0700 Subject: [PATCH 034/383] server/http: Fixed build error --- server/http/http.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/server/http/http.go b/server/http/http.go index f1c6cbb5d..1fcfc1686 100644 --- a/server/http/http.go +++ b/server/http/http.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "net/http" - "strings" "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gorilla/mux" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" @@ -15,18 +14,17 @@ import ( core "github.com/jbenet/go-ipfs/core" ) -type objectHandler struct { +type handler struct { ipfs } // Serve starts the http server func Serve(address ma.Multiaddr, node *core.IpfsNode) error { r := mux.NewRouter() - objectHandler := &objectHandler{&ipfsHandler{node}} - apiHandler := &apiHandler{} + handler := &handler{&ipfsHandler{node}} - r.HandleFunc("/ipfs/", objectHandler.postHandler).Methods("POST") - r.PathPrefix("/ipfs/").Handler(objectHandler).Methods("GET") + r.HandleFunc("/ipfs/", handler.postHandler).Methods("POST") + r.PathPrefix("/ipfs/").Handler(handler).Methods("GET") http.Handle("/", r) @@ -38,7 +36,7 @@ func Serve(address ma.Multiaddr, node *core.IpfsNode) error { return http.ListenAndServe(host, nil) } -func (i *objectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (i *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { path := r.URL.Path[5:] nd, err := i.ResolvePath(path) @@ -59,7 +57,7 @@ func (i *objectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { io.Copy(w, dr) } -func (i *objectHandler) postHandler(w http.ResponseWriter, r *http.Request) { +func (i *handler) postHandler(w http.ResponseWriter, r *http.Request) { nd, err := i.NewDagFromReader(r.Body) if err != nil { w.WriteHeader(http.StatusInternalServerError) From 4bbd5790681184143cee87b7e5c82a9fdb27db9e Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 27 Oct 2014 19:20:51 -0700 Subject: [PATCH 035/383] cmd/ipfs: Added 'local' flag to call command locally instead of executing on daemon --- cmd/ipfs/ipfs.go | 4 +++- core/commands/root.go | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index a0d4f4082..5073fbead 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -92,7 +92,9 @@ func main() { res = root.Call(req) } else { - if daemon.Locked(configPath) { + local, found := options.Option("local") + + if (!found || !local.(bool)) && daemon.Locked(configPath) { res, err = cmdsHttp.Send(req) if err != nil { fmt.Println(err) diff --git a/core/commands/root.go b/core/commands/root.go index f1dd03f59..1393eb2fb 100644 --- a/core/commands/root.go +++ b/core/commands/root.go @@ -16,6 +16,7 @@ var Root = &cmds.Command{ cmds.Option{[]string{"config", "c"}, cmds.String}, cmds.Option{[]string{"debug", "D"}, cmds.Bool}, cmds.Option{[]string{"help", "h"}, cmds.Bool}, + cmds.Option{[]string{"local", "L"}, cmds.Bool}, }, Help: `ipfs - global versioned p2p merkledag file system From e297e2da419fd74d69b9c815313fb9a499efed24 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 28 Oct 2014 17:24:22 -0700 Subject: [PATCH 036/383] core/commands: Added a 'args' command to test argument parsing --- core/commands/root.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/commands/root.go b/core/commands/root.go index 1393eb2fb..ba5129441 100644 --- a/core/commands/root.go +++ b/core/commands/root.go @@ -74,5 +74,10 @@ Use "ipfs help " for more information about a command. } }, }, + "args": &cmds.Command{ + Run: func(req cmds.Request, res cmds.Response) { + res.SetValue(req.Arguments()) + }, + }, }, } From be3cb39ed5ac6f5239c3f6e825b372f15ad72936 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 28 Oct 2014 17:52:40 -0700 Subject: [PATCH 037/383] core/commands: Added 'echo' command that writes input stream to output stream --- core/commands/root.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/commands/root.go b/core/commands/root.go index ba5129441..4a53606f8 100644 --- a/core/commands/root.go +++ b/core/commands/root.go @@ -79,5 +79,10 @@ Use "ipfs help " for more information about a command. res.SetValue(req.Arguments()) }, }, + "echo": &cmds.Command{ + Run: func(req cmds.Request, res cmds.Response) { + res.SetValue(req.Stream()) + }, + }, }, } From b3ff407d3819a0f364ea1234c7c48b38d271f50f Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 28 Oct 2014 19:27:07 -0700 Subject: [PATCH 038/383] commands: Gave Requests a reference to the command they are being called on --- cmd/ipfs/ipfs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index 5073fbead..aceb3c6f8 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -125,7 +125,7 @@ func main() { } func getOptions(req cmds.Request, root *cmds.Command) (cmds.Request, error) { - tempReq := cmds.NewRequest(req.Path(), req.Options(), nil, nil) + tempReq := cmds.NewRequest(req.Path(), req.Options(), nil, nil, nil) options, err := root.GetOptions(tempReq.Path()) if err != nil { From edb632a644f7d3ac8a880c8d009e798ccc9a0c99 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 28 Oct 2014 19:31:50 -0700 Subject: [PATCH 039/383] core/commands: Added a Format function for the 'beep' command --- core/commands/root.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/commands/root.go b/core/commands/root.go index 4a53606f8..245a955b0 100644 --- a/core/commands/root.go +++ b/core/commands/root.go @@ -48,6 +48,12 @@ Use "ipfs help " for more information about a command. v := TestOutput{"hello, world", 1337} res.SetValue(v) }, + Format: func(res cmds.Response) (string, error) { + v := res.Value().(TestOutput) + s := fmt.Sprintf("Foo: %s\n", v.Foo) + s += fmt.Sprintf("Bar: %v\n", v.Bar) + return s, nil + }, }, "boop": &cmds.Command{ Run: func(req cmds.Request, res cmds.Response) { From 54587897c13211e0baa2c03c04d24aec595da653 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 28 Oct 2014 20:18:00 -0700 Subject: [PATCH 040/383] cmd/ipfs: Default to plaintext encoding if avilable, otherwise JSON --- cmd/ipfs/ipfs.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index aceb3c6f8..e847e623f 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -87,6 +87,14 @@ func main() { ctx.ConfigRoot = configPath ctx.Config = conf + if _, found := options.Option("encoding"); !found { + if req.Command().Format != nil { + req.SetOption("encoding", cmds.Text) + } else { + req.SetOption("encoding", cmds.JSON) + } + } + var res cmds.Response if root == Root { res = root.Call(req) From 7b2f4488b7430f0a1d28ecf6a76b46feaeff5864 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 28 Oct 2014 22:20:35 -0700 Subject: [PATCH 041/383] core/commands: Added Type to 'beep' command --- core/commands/root.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/commands/root.go b/core/commands/root.go index 245a955b0..e957e7a90 100644 --- a/core/commands/root.go +++ b/core/commands/root.go @@ -45,15 +45,16 @@ Use "ipfs help " for more information about a command. Subcommands: map[string]*cmds.Command{ "beep": &cmds.Command{ Run: func(req cmds.Request, res cmds.Response) { - v := TestOutput{"hello, world", 1337} + v := &TestOutput{"hello, world", 1337} res.SetValue(v) }, Format: func(res cmds.Response) (string, error) { - v := res.Value().(TestOutput) + v := res.Value().(*TestOutput) s := fmt.Sprintf("Foo: %s\n", v.Foo) s += fmt.Sprintf("Bar: %v\n", v.Bar) return s, nil }, + Type: &TestOutput{}, }, "boop": &cmds.Command{ Run: func(req cmds.Request, res cmds.Response) { From aea52132cb9a4fcb20e8f9bb53f4918227b4391b Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 28 Oct 2014 23:10:59 -0700 Subject: [PATCH 042/383] cmd/ipfs: Initialize IpfsNode when running locally or starting daemon --- cmd/ipfs/daemon.go | 10 ++++++++-- cmd/ipfs/ipfs.go | 9 ++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go index 102dadfc1..d39ef3c48 100644 --- a/cmd/ipfs/daemon.go +++ b/cmd/ipfs/daemon.go @@ -9,6 +9,7 @@ import ( cmds "github.com/jbenet/go-ipfs/commands" cmdsHttp "github.com/jbenet/go-ipfs/commands/http" + "github.com/jbenet/go-ipfs/core" "github.com/jbenet/go-ipfs/daemon" ) @@ -20,10 +21,15 @@ var Daemon = &cmds.Command{ } func daemonFunc(req cmds.Request, res cmds.Response) { - // TODO: spin up a core.IpfsNode - ctx := req.Context() + node, err := core.NewIpfsNode(ctx.Config, true) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + ctx.Node = node + lk, err := daemon.Lock(ctx.ConfigRoot) if err != nil { res.SetError(fmt.Errorf("Couldn't obtain lock. Is another daemon already running?"), cmds.ErrNormal) diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go index e847e623f..6fae87cdb 100644 --- a/cmd/ipfs/ipfs.go +++ b/cmd/ipfs/ipfs.go @@ -10,6 +10,7 @@ import ( cmdsCli "github.com/jbenet/go-ipfs/commands/cli" cmdsHttp "github.com/jbenet/go-ipfs/commands/http" "github.com/jbenet/go-ipfs/config" + "github.com/jbenet/go-ipfs/core" "github.com/jbenet/go-ipfs/core/commands" "github.com/jbenet/go-ipfs/daemon" u "github.com/jbenet/go-ipfs/util" @@ -110,7 +111,13 @@ func main() { } } else { - // TODO: spin up node + node, err := core.NewIpfsNode(conf, false) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + ctx.Node = node + res = root.Call(req) } } From afd8fadba8f53b8f08f8cbb583f4db064b3e1f31 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 28 Oct 2014 23:15:55 -0700 Subject: [PATCH 043/383] core/commands: Added 'cat' command --- core/commands/cat.go | 42 +++++++++++++++++++++++++----------------- core/commands/root.go | 4 ++++ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/core/commands/cat.go b/core/commands/cat.go index 7ebae801c..a81ff4eec 100644 --- a/core/commands/cat.go +++ b/core/commands/cat.go @@ -4,26 +4,34 @@ import ( "fmt" "io" - "github.com/jbenet/go-ipfs/core" + cmds "github.com/jbenet/go-ipfs/commands" uio "github.com/jbenet/go-ipfs/unixfs/io" ) -func Cat(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { - for _, fn := range args { - dagnode, err := n.Resolver.ResolvePath(fn) - if err != nil { - return fmt.Errorf("catFile error: %v", err) +var cat = &cmds.Command{ + Help: "TODO", + Run: func(req cmds.Request, res cmds.Response) { + node := req.Context().Node + fmt.Println(node.Resolver) + readers := make([]io.Reader, 0, len(req.Arguments())) + + for _, path := range req.Arguments() { + dagnode, err := node.Resolver.ResolvePath(path) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + read, err := uio.NewDagReader(dagnode, node.DAG) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + readers = append(readers, read) } - read, err := uio.NewDagReader(dagnode, n.DAG) - if err != nil { - return fmt.Errorf("cat error: %v", err) - } - - _, err = io.Copy(out, read) - if err != nil { - return fmt.Errorf("cat error: %v", err) - } - } - return nil + reader := io.MultiReader(readers...) + res.SetValue(reader) + }, } diff --git a/core/commands/root.go b/core/commands/root.go index e957e7a90..673d874d9 100644 --- a/core/commands/root.go +++ b/core/commands/root.go @@ -43,6 +43,10 @@ Advanced Commands: Use "ipfs help " for more information about a command. `, Subcommands: map[string]*cmds.Command{ + "cat": cat, + + // test subcommands + // TODO: remove these when we don't need them anymore "beep": &cmds.Command{ Run: func(req cmds.Request, res cmds.Response) { v := &TestOutput{"hello, world", 1337} From b3a96d2bb3284ae3a0dacab687946d7ac1823292 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 28 Oct 2014 23:54:33 -0700 Subject: [PATCH 044/383] move temporarily --- cmd/{ipfs => ipfs3}/.gitignore | 0 cmd/{ipfs => ipfs3}/Makefile | 0 cmd/{ipfs => ipfs3}/README.md | 0 cmd/{ipfs => ipfs3}/daemon.go | 0 cmd/{ipfs => ipfs3}/equinox.yaml | 0 cmd/{ipfs => ipfs3}/ipfs.go | 0 cmd/{ipfs => ipfs3}/root.go | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename cmd/{ipfs => ipfs3}/.gitignore (100%) rename cmd/{ipfs => ipfs3}/Makefile (100%) rename cmd/{ipfs => ipfs3}/README.md (100%) rename cmd/{ipfs => ipfs3}/daemon.go (100%) rename cmd/{ipfs => ipfs3}/equinox.yaml (100%) rename cmd/{ipfs => ipfs3}/ipfs.go (100%) rename cmd/{ipfs => ipfs3}/root.go (100%) diff --git a/cmd/ipfs/.gitignore b/cmd/ipfs3/.gitignore similarity index 100% rename from cmd/ipfs/.gitignore rename to cmd/ipfs3/.gitignore diff --git a/cmd/ipfs/Makefile b/cmd/ipfs3/Makefile similarity index 100% rename from cmd/ipfs/Makefile rename to cmd/ipfs3/Makefile diff --git a/cmd/ipfs/README.md b/cmd/ipfs3/README.md similarity index 100% rename from cmd/ipfs/README.md rename to cmd/ipfs3/README.md diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs3/daemon.go similarity index 100% rename from cmd/ipfs/daemon.go rename to cmd/ipfs3/daemon.go diff --git a/cmd/ipfs/equinox.yaml b/cmd/ipfs3/equinox.yaml similarity index 100% rename from cmd/ipfs/equinox.yaml rename to cmd/ipfs3/equinox.yaml diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs3/ipfs.go similarity index 100% rename from cmd/ipfs/ipfs.go rename to cmd/ipfs3/ipfs.go diff --git a/cmd/ipfs/root.go b/cmd/ipfs3/root.go similarity index 100% rename from cmd/ipfs/root.go rename to cmd/ipfs3/root.go From 3d9331512d2d8f8427a0d169658436f68adf1ea0 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 28 Oct 2014 23:55:01 -0700 Subject: [PATCH 045/383] move originals back to ipfs --- cmd/{ipfs2 => ipfs}/.gitignore | 0 cmd/{ipfs2 => ipfs}/Makefile | 0 cmd/{ipfs2 => ipfs}/README.md | 0 cmd/{ipfs2 => ipfs}/add.go | 0 cmd/{ipfs2 => ipfs}/block.go | 0 cmd/{ipfs2 => ipfs}/bootstrap.go | 0 cmd/{ipfs2 => ipfs}/cat.go | 0 cmd/{ipfs2 => ipfs}/commands.go | 0 cmd/{ipfs2 => ipfs}/config.go | 0 cmd/{ipfs2 => ipfs}/diag.go | 0 cmd/{ipfs2 => ipfs}/equinox.yaml | 0 cmd/{ipfs2 => ipfs}/gen.go | 0 cmd/{ipfs2 => ipfs}/init.go | 0 cmd/{ipfs2 => ipfs}/ipfs.go | 0 cmd/{ipfs2 => ipfs}/log.go | 0 cmd/{ipfs2 => ipfs}/ls.go | 0 cmd/{ipfs2 => ipfs}/mount_unix.go | 0 cmd/{ipfs2 => ipfs}/mount_windows.go | 0 cmd/{ipfs2 => ipfs}/name.go | 0 cmd/{ipfs2 => ipfs}/objects.go | 0 cmd/{ipfs2 => ipfs}/pin.go | 0 cmd/{ipfs2 => ipfs}/publish.go | 0 cmd/{ipfs2 => ipfs}/refs.go | 0 cmd/{ipfs2 => ipfs}/resolve.go | 0 cmd/{ipfs2 => ipfs}/run.go | 0 cmd/{ipfs2 => ipfs}/serve.go | 0 cmd/{ipfs2 => ipfs}/tour.go | 0 cmd/{ipfs2 => ipfs}/update.go | 0 cmd/{ipfs2 => ipfs}/version.go | 0 29 files changed, 0 insertions(+), 0 deletions(-) rename cmd/{ipfs2 => ipfs}/.gitignore (100%) rename cmd/{ipfs2 => ipfs}/Makefile (100%) rename cmd/{ipfs2 => ipfs}/README.md (100%) rename cmd/{ipfs2 => ipfs}/add.go (100%) rename cmd/{ipfs2 => ipfs}/block.go (100%) rename cmd/{ipfs2 => ipfs}/bootstrap.go (100%) rename cmd/{ipfs2 => ipfs}/cat.go (100%) rename cmd/{ipfs2 => ipfs}/commands.go (100%) rename cmd/{ipfs2 => ipfs}/config.go (100%) rename cmd/{ipfs2 => ipfs}/diag.go (100%) rename cmd/{ipfs2 => ipfs}/equinox.yaml (100%) rename cmd/{ipfs2 => ipfs}/gen.go (100%) rename cmd/{ipfs2 => ipfs}/init.go (100%) rename cmd/{ipfs2 => ipfs}/ipfs.go (100%) rename cmd/{ipfs2 => ipfs}/log.go (100%) rename cmd/{ipfs2 => ipfs}/ls.go (100%) rename cmd/{ipfs2 => ipfs}/mount_unix.go (100%) rename cmd/{ipfs2 => ipfs}/mount_windows.go (100%) rename cmd/{ipfs2 => ipfs}/name.go (100%) rename cmd/{ipfs2 => ipfs}/objects.go (100%) rename cmd/{ipfs2 => ipfs}/pin.go (100%) rename cmd/{ipfs2 => ipfs}/publish.go (100%) rename cmd/{ipfs2 => ipfs}/refs.go (100%) rename cmd/{ipfs2 => ipfs}/resolve.go (100%) rename cmd/{ipfs2 => ipfs}/run.go (100%) rename cmd/{ipfs2 => ipfs}/serve.go (100%) rename cmd/{ipfs2 => ipfs}/tour.go (100%) rename cmd/{ipfs2 => ipfs}/update.go (100%) rename cmd/{ipfs2 => ipfs}/version.go (100%) diff --git a/cmd/ipfs2/.gitignore b/cmd/ipfs/.gitignore similarity index 100% rename from cmd/ipfs2/.gitignore rename to cmd/ipfs/.gitignore diff --git a/cmd/ipfs2/Makefile b/cmd/ipfs/Makefile similarity index 100% rename from cmd/ipfs2/Makefile rename to cmd/ipfs/Makefile diff --git a/cmd/ipfs2/README.md b/cmd/ipfs/README.md similarity index 100% rename from cmd/ipfs2/README.md rename to cmd/ipfs/README.md diff --git a/cmd/ipfs2/add.go b/cmd/ipfs/add.go similarity index 100% rename from cmd/ipfs2/add.go rename to cmd/ipfs/add.go diff --git a/cmd/ipfs2/block.go b/cmd/ipfs/block.go similarity index 100% rename from cmd/ipfs2/block.go rename to cmd/ipfs/block.go diff --git a/cmd/ipfs2/bootstrap.go b/cmd/ipfs/bootstrap.go similarity index 100% rename from cmd/ipfs2/bootstrap.go rename to cmd/ipfs/bootstrap.go diff --git a/cmd/ipfs2/cat.go b/cmd/ipfs/cat.go similarity index 100% rename from cmd/ipfs2/cat.go rename to cmd/ipfs/cat.go diff --git a/cmd/ipfs2/commands.go b/cmd/ipfs/commands.go similarity index 100% rename from cmd/ipfs2/commands.go rename to cmd/ipfs/commands.go diff --git a/cmd/ipfs2/config.go b/cmd/ipfs/config.go similarity index 100% rename from cmd/ipfs2/config.go rename to cmd/ipfs/config.go diff --git a/cmd/ipfs2/diag.go b/cmd/ipfs/diag.go similarity index 100% rename from cmd/ipfs2/diag.go rename to cmd/ipfs/diag.go diff --git a/cmd/ipfs2/equinox.yaml b/cmd/ipfs/equinox.yaml similarity index 100% rename from cmd/ipfs2/equinox.yaml rename to cmd/ipfs/equinox.yaml diff --git a/cmd/ipfs2/gen.go b/cmd/ipfs/gen.go similarity index 100% rename from cmd/ipfs2/gen.go rename to cmd/ipfs/gen.go diff --git a/cmd/ipfs2/init.go b/cmd/ipfs/init.go similarity index 100% rename from cmd/ipfs2/init.go rename to cmd/ipfs/init.go diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs/ipfs.go similarity index 100% rename from cmd/ipfs2/ipfs.go rename to cmd/ipfs/ipfs.go diff --git a/cmd/ipfs2/log.go b/cmd/ipfs/log.go similarity index 100% rename from cmd/ipfs2/log.go rename to cmd/ipfs/log.go diff --git a/cmd/ipfs2/ls.go b/cmd/ipfs/ls.go similarity index 100% rename from cmd/ipfs2/ls.go rename to cmd/ipfs/ls.go diff --git a/cmd/ipfs2/mount_unix.go b/cmd/ipfs/mount_unix.go similarity index 100% rename from cmd/ipfs2/mount_unix.go rename to cmd/ipfs/mount_unix.go diff --git a/cmd/ipfs2/mount_windows.go b/cmd/ipfs/mount_windows.go similarity index 100% rename from cmd/ipfs2/mount_windows.go rename to cmd/ipfs/mount_windows.go diff --git a/cmd/ipfs2/name.go b/cmd/ipfs/name.go similarity index 100% rename from cmd/ipfs2/name.go rename to cmd/ipfs/name.go diff --git a/cmd/ipfs2/objects.go b/cmd/ipfs/objects.go similarity index 100% rename from cmd/ipfs2/objects.go rename to cmd/ipfs/objects.go diff --git a/cmd/ipfs2/pin.go b/cmd/ipfs/pin.go similarity index 100% rename from cmd/ipfs2/pin.go rename to cmd/ipfs/pin.go diff --git a/cmd/ipfs2/publish.go b/cmd/ipfs/publish.go similarity index 100% rename from cmd/ipfs2/publish.go rename to cmd/ipfs/publish.go diff --git a/cmd/ipfs2/refs.go b/cmd/ipfs/refs.go similarity index 100% rename from cmd/ipfs2/refs.go rename to cmd/ipfs/refs.go diff --git a/cmd/ipfs2/resolve.go b/cmd/ipfs/resolve.go similarity index 100% rename from cmd/ipfs2/resolve.go rename to cmd/ipfs/resolve.go diff --git a/cmd/ipfs2/run.go b/cmd/ipfs/run.go similarity index 100% rename from cmd/ipfs2/run.go rename to cmd/ipfs/run.go diff --git a/cmd/ipfs2/serve.go b/cmd/ipfs/serve.go similarity index 100% rename from cmd/ipfs2/serve.go rename to cmd/ipfs/serve.go diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs/tour.go similarity index 100% rename from cmd/ipfs2/tour.go rename to cmd/ipfs/tour.go diff --git a/cmd/ipfs2/update.go b/cmd/ipfs/update.go similarity index 100% rename from cmd/ipfs2/update.go rename to cmd/ipfs/update.go diff --git a/cmd/ipfs2/version.go b/cmd/ipfs/version.go similarity index 100% rename from cmd/ipfs2/version.go rename to cmd/ipfs/version.go From 510a4a90937939d422aeb58b9ac4525506f5cd22 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 28 Oct 2014 23:56:21 -0700 Subject: [PATCH 046/383] move new commands to new place --- cmd/{ipfs3 => ipfs2}/.gitignore | 0 cmd/{ipfs3 => ipfs2}/Makefile | 0 cmd/{ipfs3 => ipfs2}/README.md | 0 cmd/{ipfs3 => ipfs2}/daemon.go | 0 cmd/{ipfs3 => ipfs2}/equinox.yaml | 0 cmd/{ipfs3 => ipfs2}/ipfs.go | 0 cmd/{ipfs3 => ipfs2}/root.go | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename cmd/{ipfs3 => ipfs2}/.gitignore (100%) rename cmd/{ipfs3 => ipfs2}/Makefile (100%) rename cmd/{ipfs3 => ipfs2}/README.md (100%) rename cmd/{ipfs3 => ipfs2}/daemon.go (100%) rename cmd/{ipfs3 => ipfs2}/equinox.yaml (100%) rename cmd/{ipfs3 => ipfs2}/ipfs.go (100%) rename cmd/{ipfs3 => ipfs2}/root.go (100%) diff --git a/cmd/ipfs3/.gitignore b/cmd/ipfs2/.gitignore similarity index 100% rename from cmd/ipfs3/.gitignore rename to cmd/ipfs2/.gitignore diff --git a/cmd/ipfs3/Makefile b/cmd/ipfs2/Makefile similarity index 100% rename from cmd/ipfs3/Makefile rename to cmd/ipfs2/Makefile diff --git a/cmd/ipfs3/README.md b/cmd/ipfs2/README.md similarity index 100% rename from cmd/ipfs3/README.md rename to cmd/ipfs2/README.md diff --git a/cmd/ipfs3/daemon.go b/cmd/ipfs2/daemon.go similarity index 100% rename from cmd/ipfs3/daemon.go rename to cmd/ipfs2/daemon.go diff --git a/cmd/ipfs3/equinox.yaml b/cmd/ipfs2/equinox.yaml similarity index 100% rename from cmd/ipfs3/equinox.yaml rename to cmd/ipfs2/equinox.yaml diff --git a/cmd/ipfs3/ipfs.go b/cmd/ipfs2/ipfs.go similarity index 100% rename from cmd/ipfs3/ipfs.go rename to cmd/ipfs2/ipfs.go diff --git a/cmd/ipfs3/root.go b/cmd/ipfs2/root.go similarity index 100% rename from cmd/ipfs3/root.go rename to cmd/ipfs2/root.go From e1eb71fce532a2fa4687e3f8e998352ade2ae0d5 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 29 Oct 2014 00:04:23 -0700 Subject: [PATCH 047/383] new commands to temp dir --- core/{commands => commands3}/add.go | 0 core/{commands => commands3}/block.go | 0 core/{commands => commands3}/cat.go | 0 core/{commands => commands3}/commands.go | 0 core/{commands => commands3}/diag.go | 0 core/{commands => commands3}/log.go | 0 core/{commands => commands3}/ls.go | 0 core/{commands => commands3}/object.go | 0 core/{commands => commands3}/pin.go | 0 core/{commands => commands3}/publish.go | 0 core/{commands => commands3}/refs.go | 0 core/{commands => commands3}/resolve.go | 0 core/{commands => commands3}/root.go | 0 core/{commands => commands3}/update.go | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename core/{commands => commands3}/add.go (100%) rename core/{commands => commands3}/block.go (100%) rename core/{commands => commands3}/cat.go (100%) rename core/{commands => commands3}/commands.go (100%) rename core/{commands => commands3}/diag.go (100%) rename core/{commands => commands3}/log.go (100%) rename core/{commands => commands3}/ls.go (100%) rename core/{commands => commands3}/object.go (100%) rename core/{commands => commands3}/pin.go (100%) rename core/{commands => commands3}/publish.go (100%) rename core/{commands => commands3}/refs.go (100%) rename core/{commands => commands3}/resolve.go (100%) rename core/{commands => commands3}/root.go (100%) rename core/{commands => commands3}/update.go (100%) diff --git a/core/commands/add.go b/core/commands3/add.go similarity index 100% rename from core/commands/add.go rename to core/commands3/add.go diff --git a/core/commands/block.go b/core/commands3/block.go similarity index 100% rename from core/commands/block.go rename to core/commands3/block.go diff --git a/core/commands/cat.go b/core/commands3/cat.go similarity index 100% rename from core/commands/cat.go rename to core/commands3/cat.go diff --git a/core/commands/commands.go b/core/commands3/commands.go similarity index 100% rename from core/commands/commands.go rename to core/commands3/commands.go diff --git a/core/commands/diag.go b/core/commands3/diag.go similarity index 100% rename from core/commands/diag.go rename to core/commands3/diag.go diff --git a/core/commands/log.go b/core/commands3/log.go similarity index 100% rename from core/commands/log.go rename to core/commands3/log.go diff --git a/core/commands/ls.go b/core/commands3/ls.go similarity index 100% rename from core/commands/ls.go rename to core/commands3/ls.go diff --git a/core/commands/object.go b/core/commands3/object.go similarity index 100% rename from core/commands/object.go rename to core/commands3/object.go diff --git a/core/commands/pin.go b/core/commands3/pin.go similarity index 100% rename from core/commands/pin.go rename to core/commands3/pin.go diff --git a/core/commands/publish.go b/core/commands3/publish.go similarity index 100% rename from core/commands/publish.go rename to core/commands3/publish.go diff --git a/core/commands/refs.go b/core/commands3/refs.go similarity index 100% rename from core/commands/refs.go rename to core/commands3/refs.go diff --git a/core/commands/resolve.go b/core/commands3/resolve.go similarity index 100% rename from core/commands/resolve.go rename to core/commands3/resolve.go diff --git a/core/commands/root.go b/core/commands3/root.go similarity index 100% rename from core/commands/root.go rename to core/commands3/root.go diff --git a/core/commands/update.go b/core/commands3/update.go similarity index 100% rename from core/commands/update.go rename to core/commands3/update.go From a5db13f3ae75da38b3145258f77696f22572fe01 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 29 Oct 2014 00:05:33 -0700 Subject: [PATCH 048/383] original commands to original dir --- core/commands/add.go | 122 ++++++++++++++++++++++++++++++++++ core/commands/block.go | 60 +++++++++++++++++ core/commands/cat.go | 29 ++++++++ core/commands/commands.go | 12 ++++ core/commands/diag.go | 49 ++++++++++++++ core/commands/log.go | 18 +++++ core/commands/ls.go | 22 +++++++ core/commands/object.go | 135 ++++++++++++++++++++++++++++++++++++++ core/commands/pin.go | 58 ++++++++++++++++ core/commands/publish.go | 53 +++++++++++++++ core/commands/refs.go | 65 ++++++++++++++++++ core/commands/resolve.go | 35 ++++++++++ core/commands/update.go | 76 +++++++++++++++++++++ 13 files changed, 734 insertions(+) create mode 100644 core/commands/add.go create mode 100644 core/commands/block.go create mode 100644 core/commands/cat.go create mode 100644 core/commands/commands.go create mode 100644 core/commands/diag.go create mode 100644 core/commands/log.go create mode 100644 core/commands/ls.go create mode 100644 core/commands/object.go create mode 100644 core/commands/pin.go create mode 100644 core/commands/publish.go create mode 100644 core/commands/refs.go create mode 100644 core/commands/resolve.go create mode 100644 core/commands/update.go diff --git a/core/commands/add.go b/core/commands/add.go new file mode 100644 index 000000000..b6d1708f8 --- /dev/null +++ b/core/commands/add.go @@ -0,0 +1,122 @@ +package commands + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + + "github.com/jbenet/go-ipfs/core" + "github.com/jbenet/go-ipfs/importer" + dag "github.com/jbenet/go-ipfs/merkledag" + ft "github.com/jbenet/go-ipfs/unixfs" +) + +// Error indicating the max depth has been exceded. +var ErrDepthLimitExceeded = fmt.Errorf("depth limit exceeded") + +// Add is a command that imports files and directories -- given as arguments -- into ipfs. +func Add(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { + depth := 1 + + // if recursive, set depth to reflect so + if r, ok := opts["r"].(bool); r && ok { + depth = -1 + } + + // add every path in args + for _, path := range args { + + // Add the file + _, err := AddPath(n, path, depth, out) + if err != nil { + if err == ErrDepthLimitExceeded && depth == 1 { + err = errors.New("use -r to recursively add directories") + } + return fmt.Errorf("addFile error: %v", err) + } + + } + return nil +} + +// AddPath adds a particular path to ipfs. +func AddPath(n *core.IpfsNode, fpath string, depth int, out io.Writer) (*dag.Node, error) { + if depth == 0 { + return nil, ErrDepthLimitExceeded + } + + fi, err := os.Stat(fpath) + if err != nil { + return nil, err + } + + if fi.IsDir() { + return addDir(n, fpath, depth, out) + } + + return addFile(n, fpath, depth, out) +} + +func addDir(n *core.IpfsNode, fpath string, depth int, out io.Writer) (*dag.Node, error) { + tree := &dag.Node{Data: ft.FolderPBData()} + + files, err := ioutil.ReadDir(fpath) + if err != nil { + return nil, err + } + + // construct nodes for containing files. + for _, f := range files { + fp := filepath.Join(fpath, f.Name()) + nd, err := AddPath(n, fp, depth-1, out) + if err != nil { + return nil, err + } + + if err = tree.AddNodeLink(f.Name(), nd); err != nil { + return nil, err + } + } + + log.Info("adding dir: %s", fpath) + + return tree, addNode(n, tree, fpath, out) +} + +func addFile(n *core.IpfsNode, fpath string, depth int, out io.Writer) (*dag.Node, error) { + root, err := importer.NewDagFromFile(fpath) + if err != nil { + return nil, err + } + + log.Info("adding file: %s", fpath) + + for _, l := range root.Links { + log.Info("adding subblock: %s %s", l.Name, l.Hash.B58String()) + } + + return root, addNode(n, root, fpath, out) +} + +// addNode adds the node to the graph + local storage +func addNode(n *core.IpfsNode, nd *dag.Node, fpath string, out io.Writer) error { + // add the file to the graph + local storage + err := n.DAG.AddRecursive(nd) + if err != nil { + return err + } + + k, err := nd.Key() + if err != nil { + return err + } + + // output that we've added this node + fmt.Fprintf(out, "added %s %s\n", k, fpath) + + // ensure we keep it + return n.Pinning.Pin(nd, true) +} diff --git a/core/commands/block.go b/core/commands/block.go new file mode 100644 index 000000000..089ed3734 --- /dev/null +++ b/core/commands/block.go @@ -0,0 +1,60 @@ +package commands + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "time" + + "code.google.com/p/go.net/context" + + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + "github.com/jbenet/go-ipfs/blocks" + "github.com/jbenet/go-ipfs/core" + u "github.com/jbenet/go-ipfs/util" +) + +// BlockGet retrives a raw ipfs block from the node's BlockService +func BlockGet(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { + + if !u.IsValidHash(args[0]) { + return fmt.Errorf("block get: not a valid hash") + } + + h, err := mh.FromB58String(args[0]) + if err != nil { + return fmt.Errorf("block get: %v", err) + } + + k := u.Key(h) + log.Debug("BlockGet key: '%q'", k) + ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) + b, err := n.Blocks.GetBlock(ctx, k) + if err != nil { + return fmt.Errorf("block get: %v", err) + } + + _, err = out.Write(b.Data) + return err +} + +// BlockPut reads everything from conn and saves the data to the nodes BlockService +func BlockPut(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { + // TODO: this should read from an io.Reader arg + data, err := ioutil.ReadAll(os.Stdin) + if err != nil { + return err + } + + b := blocks.NewBlock(data) + log.Debug("BlockPut key: '%q'", b.Key()) + + k, err := n.Blocks.AddBlock(b) + if err != nil { + return err + } + fmt.Fprintf(out, "added as '%s'\n", k) + + return nil +} diff --git a/core/commands/cat.go b/core/commands/cat.go new file mode 100644 index 000000000..7ebae801c --- /dev/null +++ b/core/commands/cat.go @@ -0,0 +1,29 @@ +package commands + +import ( + "fmt" + "io" + + "github.com/jbenet/go-ipfs/core" + uio "github.com/jbenet/go-ipfs/unixfs/io" +) + +func Cat(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { + for _, fn := range args { + dagnode, err := n.Resolver.ResolvePath(fn) + if err != nil { + return fmt.Errorf("catFile error: %v", err) + } + + read, err := uio.NewDagReader(dagnode, n.DAG) + if err != nil { + return fmt.Errorf("cat error: %v", err) + } + + _, err = io.Copy(out, read) + if err != nil { + return fmt.Errorf("cat error: %v", err) + } + } + return nil +} diff --git a/core/commands/commands.go b/core/commands/commands.go new file mode 100644 index 000000000..0c2541146 --- /dev/null +++ b/core/commands/commands.go @@ -0,0 +1,12 @@ +package commands + +import ( + "io" + + "github.com/jbenet/go-ipfs/core" + u "github.com/jbenet/go-ipfs/util" +) + +var log = u.Logger("commands") + +type CmdFunc func(*core.IpfsNode, []string, map[string]interface{}, io.Writer) error diff --git a/core/commands/diag.go b/core/commands/diag.go new file mode 100644 index 000000000..fdb84ecf4 --- /dev/null +++ b/core/commands/diag.go @@ -0,0 +1,49 @@ +package commands + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "time" + + "github.com/jbenet/go-ipfs/core" + diagn "github.com/jbenet/go-ipfs/diagnostics" +) + +func PrintDiagnostics(info []*diagn.DiagInfo, out io.Writer) { + for _, i := range info { + fmt.Fprintf(out, "Peer: %s\n", i.ID) + fmt.Fprintf(out, "\tUp for: %s\n", i.LifeSpan.String()) + fmt.Fprintf(out, "\tConnected To:\n") + for _, c := range i.Connections { + fmt.Fprintf(out, "\t%s\n\t\tLatency = %s\n", c.ID, c.Latency.String()) + } + fmt.Fprintln(out) + } + +} + +func Diag(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { + if n.Diagnostics == nil { + return errors.New("Cannot run diagnostic in offline mode!") + } + info, err := n.Diagnostics.GetDiagnostic(time.Second * 20) + if err != nil { + return err + } + raw, ok := opts["raw"].(bool) + if !ok { + return errors.New("incorrect value to parameter 'raw'") + } + if raw { + enc := json.NewEncoder(out) + err = enc.Encode(info) + if err != nil { + return err + } + } else { + PrintDiagnostics(info, out) + } + return nil +} diff --git a/core/commands/log.go b/core/commands/log.go new file mode 100644 index 000000000..1dc3793b6 --- /dev/null +++ b/core/commands/log.go @@ -0,0 +1,18 @@ +package commands + +import ( + "io" + + "github.com/jbenet/go-ipfs/core" + u "github.com/jbenet/go-ipfs/util" +) + +// Log changes the log level of a subsystem +func Log(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { + if err := u.SetLogLevel(args[0], args[1]); err != nil { + return err + } + + log.Info("Changed LogLevel of %q to %q", args[0], args[1]) + return nil +} diff --git a/core/commands/ls.go b/core/commands/ls.go new file mode 100644 index 000000000..d87cf57b0 --- /dev/null +++ b/core/commands/ls.go @@ -0,0 +1,22 @@ +package commands + +import ( + "fmt" + "io" + + "github.com/jbenet/go-ipfs/core" +) + +func Ls(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { + for _, fn := range args { + dagnode, err := n.Resolver.ResolvePath(fn) + if err != nil { + return fmt.Errorf("ls error: %v", err) + } + + for _, link := range dagnode.Links { + fmt.Fprintf(out, "%s %d %s\n", link.Hash.B58String(), link.Size, link.Name) + } + } + return nil +} diff --git a/core/commands/object.go b/core/commands/object.go new file mode 100644 index 000000000..5d0ccddab --- /dev/null +++ b/core/commands/object.go @@ -0,0 +1,135 @@ +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) +} diff --git a/core/commands/pin.go b/core/commands/pin.go new file mode 100644 index 000000000..cbca3bf92 --- /dev/null +++ b/core/commands/pin.go @@ -0,0 +1,58 @@ +package commands + +import ( + "fmt" + "io" + + "github.com/jbenet/go-ipfs/core" +) + +func Pin(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { + + // set recursive flag + recursive, _ := opts["r"].(bool) // false if cast fails. + + // if recursive, set depth flag + depth := 1 // default (non recursive) + if d, ok := opts["d"].(int); recursive && ok { + depth = d + } + if depth < -1 { + return fmt.Errorf("ipfs pin: called with invalid depth: %v", depth) + } + + fmt.Printf("recursive, depth: %v, %v\n", recursive, depth) + + for _, fn := range args { + dagnode, err := n.Resolver.ResolvePath(fn) + if err != nil { + return fmt.Errorf("pin error: %v", err) + } + + err = n.Pinning.Pin(dagnode, recursive) + if err != nil { + return fmt.Errorf("pin: %v", err) + } + } + return n.Pinning.Flush() +} + +func Unpin(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { + + // set recursive flag + recursive, _ := opts["r"].(bool) // false if cast fails. + + for _, fn := range args { + dagnode, err := n.Resolver.ResolvePath(fn) + if err != nil { + return fmt.Errorf("pin error: %v", err) + } + + k, _ := dagnode.Key() + err = n.Pinning.Unpin(k, recursive) + if err != nil { + return fmt.Errorf("pin: %v", err) + } + } + return n.Pinning.Flush() +} diff --git a/core/commands/publish.go b/core/commands/publish.go new file mode 100644 index 000000000..042f3f0c5 --- /dev/null +++ b/core/commands/publish.go @@ -0,0 +1,53 @@ +package commands + +import ( + "errors" + "fmt" + "io" + + "github.com/jbenet/go-ipfs/core" + u "github.com/jbenet/go-ipfs/util" + + nsys "github.com/jbenet/go-ipfs/namesys" +) + +func Publish(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { + log.Debug("Begin Publish") + + if n.Identity == nil { + return errors.New("Identity not loaded!") + } + + // name := "" + ref := "" + + switch len(args) { + case 2: + // name = args[0] + ref = args[1] + return errors.New("keychains not yet implemented") + case 1: + // name = n.Identity.ID.String() + ref = args[0] + + default: + return fmt.Errorf("Publish expects 1 or 2 args; got %d.", len(args)) + } + + // later, n.Keychain.Get(name).PrivKey + k := n.Identity.PrivKey() + + pub := nsys.NewRoutingPublisher(n.Routing) + err := pub.Publish(k, ref) + if err != nil { + return err + } + + hash, err := k.GetPublic().Hash() + if err != nil { + return err + } + fmt.Fprintf(out, "published name %s to %s\n", u.Key(hash), ref) + + return nil +} diff --git a/core/commands/refs.go b/core/commands/refs.go new file mode 100644 index 000000000..98fe94aca --- /dev/null +++ b/core/commands/refs.go @@ -0,0 +1,65 @@ +package commands + +import ( + "io" + + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + "github.com/jbenet/go-ipfs/core" + mdag "github.com/jbenet/go-ipfs/merkledag" + u "github.com/jbenet/go-ipfs/util" +) + +func Refs(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { + unique, ok := opts["u"].(bool) + if !ok { + unique = false + } + + recursive, ok := opts["r"].(bool) + if !ok { + recursive = false + } + + var refsSeen map[u.Key]bool + if unique { + refsSeen = make(map[u.Key]bool) + } + + for _, fn := range args { + nd, err := n.Resolver.ResolvePath(fn) + if err != nil { + return err + } + + printRefs(n, nd, refsSeen, recursive) + } + return nil +} + +func printRefs(n *core.IpfsNode, nd *mdag.Node, refSeen map[u.Key]bool, recursive bool) { + for _, link := range nd.Links { + printRef(link.Hash, refSeen) + + if recursive { + nd, err := n.DAG.Get(u.Key(link.Hash)) + if err != nil { + log.Errorf("error: cannot retrieve %s (%s)", link.Hash.B58String(), err) + return + } + + printRefs(n, nd, refSeen, recursive) + } + } +} + +func printRef(h mh.Multihash, refsSeen map[u.Key]bool) { + if refsSeen != nil { + _, found := refsSeen[u.Key(h)] + if found { + return + } + refsSeen[u.Key(h)] = true + } + + u.POut("%s\n", h.B58String()) +} diff --git a/core/commands/resolve.go b/core/commands/resolve.go new file mode 100644 index 000000000..f6b462c8e --- /dev/null +++ b/core/commands/resolve.go @@ -0,0 +1,35 @@ +package commands + +import ( + "errors" + "fmt" + "io" + + "github.com/jbenet/go-ipfs/core" +) + +func Resolve(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { + + name := "" + + switch len(args) { + case 1: + name = args[0] + case 0: + if n.Identity == nil { + return errors.New("Identity not loaded!") + } + name = n.Identity.ID().String() + + default: + return fmt.Errorf("Publish expects 1 or 2 args; got %d.", len(args)) + } + + res, err := n.Namesys.Resolve(name) + if err != nil { + return err + } + + fmt.Fprintf(out, "%s\n", res) + return nil +} diff --git a/core/commands/update.go b/core/commands/update.go new file mode 100644 index 000000000..68f9456dd --- /dev/null +++ b/core/commands/update.go @@ -0,0 +1,76 @@ +package commands + +import ( + "errors" + "fmt" + "io" + "os" + + "github.com/jbenet/go-ipfs/core" + "github.com/jbenet/go-ipfs/updates" +) + +// UpdateApply applys an update of the ipfs binary and shuts down the node if successful +func UpdateApply(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { + fmt.Fprintln(out, "Current Version:", updates.Version) + u, err := updates.CheckForUpdate() + if err != nil { + return err + } + + if u == nil { + fmt.Fprintln(out, "No update available") + return nil + } + fmt.Fprintln(out, "New Version:", u.Version) + + _, onDaemon := opts["onDaemon"] + force := opts["force"].(bool) + if onDaemon && !force { + return fmt.Errorf(`Error: update must stop running ipfs service. +You may want to abort the update, or shut the service down manually. +To shut it down automatically, run: + + ipfs update --force +`) + } + + if err = updates.Apply(u); err != nil { + fmt.Fprint(out, err.Error()) + return fmt.Errorf("Couldn't apply update: %v", err) + } + + fmt.Fprintln(out, "Updated applied!") + if onDaemon { + if force { + fmt.Fprintln(out, "Shutting down ipfs service.") + os.Exit(1) // is there a cleaner shutdown routine? + } else { + fmt.Fprintln(out, "You can now restart the ipfs service.") + } + } + + return nil +} + +// UpdateCheck checks wether there is an update available +func UpdateCheck(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { + fmt.Fprintln(out, "Current Version:", updates.Version) + u, err := updates.CheckForUpdate() + if err != nil { + return err + } + + if u == nil { + fmt.Fprintln(out, "No update available") + return nil + } + + fmt.Fprintln(out, "New Version:", u.Version) + return nil +} + +// UpdateLog lists the version available online +func UpdateLog(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { + return errors.New("Not yet implemented") +} From 78b5fbffc74281c733050aaf0d5a403fe6c8cd7a Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 29 Oct 2014 00:06:04 -0700 Subject: [PATCH 049/383] new commands to commands2 --- core/{commands3 => commands2}/add.go | 0 core/{commands3 => commands2}/block.go | 0 core/{commands3 => commands2}/cat.go | 0 core/{commands3 => commands2}/commands.go | 0 core/{commands3 => commands2}/diag.go | 0 core/{commands3 => commands2}/log.go | 0 core/{commands3 => commands2}/ls.go | 0 core/{commands3 => commands2}/object.go | 0 core/{commands3 => commands2}/pin.go | 0 core/{commands3 => commands2}/publish.go | 0 core/{commands3 => commands2}/refs.go | 0 core/{commands3 => commands2}/resolve.go | 0 core/{commands3 => commands2}/root.go | 0 core/{commands3 => commands2}/update.go | 0 14 files changed, 0 insertions(+), 0 deletions(-) rename core/{commands3 => commands2}/add.go (100%) rename core/{commands3 => commands2}/block.go (100%) rename core/{commands3 => commands2}/cat.go (100%) rename core/{commands3 => commands2}/commands.go (100%) rename core/{commands3 => commands2}/diag.go (100%) rename core/{commands3 => commands2}/log.go (100%) rename core/{commands3 => commands2}/ls.go (100%) rename core/{commands3 => commands2}/object.go (100%) rename core/{commands3 => commands2}/pin.go (100%) rename core/{commands3 => commands2}/publish.go (100%) rename core/{commands3 => commands2}/refs.go (100%) rename core/{commands3 => commands2}/resolve.go (100%) rename core/{commands3 => commands2}/root.go (100%) rename core/{commands3 => commands2}/update.go (100%) diff --git a/core/commands3/add.go b/core/commands2/add.go similarity index 100% rename from core/commands3/add.go rename to core/commands2/add.go diff --git a/core/commands3/block.go b/core/commands2/block.go similarity index 100% rename from core/commands3/block.go rename to core/commands2/block.go diff --git a/core/commands3/cat.go b/core/commands2/cat.go similarity index 100% rename from core/commands3/cat.go rename to core/commands2/cat.go diff --git a/core/commands3/commands.go b/core/commands2/commands.go similarity index 100% rename from core/commands3/commands.go rename to core/commands2/commands.go diff --git a/core/commands3/diag.go b/core/commands2/diag.go similarity index 100% rename from core/commands3/diag.go rename to core/commands2/diag.go diff --git a/core/commands3/log.go b/core/commands2/log.go similarity index 100% rename from core/commands3/log.go rename to core/commands2/log.go diff --git a/core/commands3/ls.go b/core/commands2/ls.go similarity index 100% rename from core/commands3/ls.go rename to core/commands2/ls.go diff --git a/core/commands3/object.go b/core/commands2/object.go similarity index 100% rename from core/commands3/object.go rename to core/commands2/object.go diff --git a/core/commands3/pin.go b/core/commands2/pin.go similarity index 100% rename from core/commands3/pin.go rename to core/commands2/pin.go diff --git a/core/commands3/publish.go b/core/commands2/publish.go similarity index 100% rename from core/commands3/publish.go rename to core/commands2/publish.go diff --git a/core/commands3/refs.go b/core/commands2/refs.go similarity index 100% rename from core/commands3/refs.go rename to core/commands2/refs.go diff --git a/core/commands3/resolve.go b/core/commands2/resolve.go similarity index 100% rename from core/commands3/resolve.go rename to core/commands2/resolve.go diff --git a/core/commands3/root.go b/core/commands2/root.go similarity index 100% rename from core/commands3/root.go rename to core/commands2/root.go diff --git a/core/commands3/update.go b/core/commands2/update.go similarity index 100% rename from core/commands3/update.go rename to core/commands2/update.go From 465cce9950a8406208919466a9256aede5b563c5 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 29 Oct 2014 00:19:32 -0700 Subject: [PATCH 050/383] remove duplicate files these weren't actually modified when commands2 was introduced. they're exact replicas of the ones that presently exist in core/commands. --- core/commands2/commands.go | 12 ------ core/commands2/diag.go | 49 ------------------------ core/commands2/log.go | 18 --------- core/commands2/ls.go | 22 ----------- core/commands2/pin.go | 58 ----------------------------- core/commands2/publish.go | 53 -------------------------- core/commands2/refs.go | 65 -------------------------------- core/commands2/resolve.go | 35 ------------------ core/commands2/update.go | 76 -------------------------------------- 9 files changed, 388 deletions(-) delete mode 100644 core/commands2/commands.go delete mode 100644 core/commands2/diag.go delete mode 100644 core/commands2/log.go delete mode 100644 core/commands2/ls.go delete mode 100644 core/commands2/pin.go delete mode 100644 core/commands2/publish.go delete mode 100644 core/commands2/refs.go delete mode 100644 core/commands2/resolve.go delete mode 100644 core/commands2/update.go diff --git a/core/commands2/commands.go b/core/commands2/commands.go deleted file mode 100644 index 0c2541146..000000000 --- a/core/commands2/commands.go +++ /dev/null @@ -1,12 +0,0 @@ -package commands - -import ( - "io" - - "github.com/jbenet/go-ipfs/core" - u "github.com/jbenet/go-ipfs/util" -) - -var log = u.Logger("commands") - -type CmdFunc func(*core.IpfsNode, []string, map[string]interface{}, io.Writer) error diff --git a/core/commands2/diag.go b/core/commands2/diag.go deleted file mode 100644 index fdb84ecf4..000000000 --- a/core/commands2/diag.go +++ /dev/null @@ -1,49 +0,0 @@ -package commands - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "time" - - "github.com/jbenet/go-ipfs/core" - diagn "github.com/jbenet/go-ipfs/diagnostics" -) - -func PrintDiagnostics(info []*diagn.DiagInfo, out io.Writer) { - for _, i := range info { - fmt.Fprintf(out, "Peer: %s\n", i.ID) - fmt.Fprintf(out, "\tUp for: %s\n", i.LifeSpan.String()) - fmt.Fprintf(out, "\tConnected To:\n") - for _, c := range i.Connections { - fmt.Fprintf(out, "\t%s\n\t\tLatency = %s\n", c.ID, c.Latency.String()) - } - fmt.Fprintln(out) - } - -} - -func Diag(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { - if n.Diagnostics == nil { - return errors.New("Cannot run diagnostic in offline mode!") - } - info, err := n.Diagnostics.GetDiagnostic(time.Second * 20) - if err != nil { - return err - } - raw, ok := opts["raw"].(bool) - if !ok { - return errors.New("incorrect value to parameter 'raw'") - } - if raw { - enc := json.NewEncoder(out) - err = enc.Encode(info) - if err != nil { - return err - } - } else { - PrintDiagnostics(info, out) - } - return nil -} diff --git a/core/commands2/log.go b/core/commands2/log.go deleted file mode 100644 index 1dc3793b6..000000000 --- a/core/commands2/log.go +++ /dev/null @@ -1,18 +0,0 @@ -package commands - -import ( - "io" - - "github.com/jbenet/go-ipfs/core" - u "github.com/jbenet/go-ipfs/util" -) - -// Log changes the log level of a subsystem -func Log(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { - if err := u.SetLogLevel(args[0], args[1]); err != nil { - return err - } - - log.Info("Changed LogLevel of %q to %q", args[0], args[1]) - return nil -} diff --git a/core/commands2/ls.go b/core/commands2/ls.go deleted file mode 100644 index d87cf57b0..000000000 --- a/core/commands2/ls.go +++ /dev/null @@ -1,22 +0,0 @@ -package commands - -import ( - "fmt" - "io" - - "github.com/jbenet/go-ipfs/core" -) - -func Ls(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { - for _, fn := range args { - dagnode, err := n.Resolver.ResolvePath(fn) - if err != nil { - return fmt.Errorf("ls error: %v", err) - } - - for _, link := range dagnode.Links { - fmt.Fprintf(out, "%s %d %s\n", link.Hash.B58String(), link.Size, link.Name) - } - } - return nil -} diff --git a/core/commands2/pin.go b/core/commands2/pin.go deleted file mode 100644 index cbca3bf92..000000000 --- a/core/commands2/pin.go +++ /dev/null @@ -1,58 +0,0 @@ -package commands - -import ( - "fmt" - "io" - - "github.com/jbenet/go-ipfs/core" -) - -func Pin(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { - - // set recursive flag - recursive, _ := opts["r"].(bool) // false if cast fails. - - // if recursive, set depth flag - depth := 1 // default (non recursive) - if d, ok := opts["d"].(int); recursive && ok { - depth = d - } - if depth < -1 { - return fmt.Errorf("ipfs pin: called with invalid depth: %v", depth) - } - - fmt.Printf("recursive, depth: %v, %v\n", recursive, depth) - - for _, fn := range args { - dagnode, err := n.Resolver.ResolvePath(fn) - if err != nil { - return fmt.Errorf("pin error: %v", err) - } - - err = n.Pinning.Pin(dagnode, recursive) - if err != nil { - return fmt.Errorf("pin: %v", err) - } - } - return n.Pinning.Flush() -} - -func Unpin(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { - - // set recursive flag - recursive, _ := opts["r"].(bool) // false if cast fails. - - for _, fn := range args { - dagnode, err := n.Resolver.ResolvePath(fn) - if err != nil { - return fmt.Errorf("pin error: %v", err) - } - - k, _ := dagnode.Key() - err = n.Pinning.Unpin(k, recursive) - if err != nil { - return fmt.Errorf("pin: %v", err) - } - } - return n.Pinning.Flush() -} diff --git a/core/commands2/publish.go b/core/commands2/publish.go deleted file mode 100644 index 042f3f0c5..000000000 --- a/core/commands2/publish.go +++ /dev/null @@ -1,53 +0,0 @@ -package commands - -import ( - "errors" - "fmt" - "io" - - "github.com/jbenet/go-ipfs/core" - u "github.com/jbenet/go-ipfs/util" - - nsys "github.com/jbenet/go-ipfs/namesys" -) - -func Publish(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { - log.Debug("Begin Publish") - - if n.Identity == nil { - return errors.New("Identity not loaded!") - } - - // name := "" - ref := "" - - switch len(args) { - case 2: - // name = args[0] - ref = args[1] - return errors.New("keychains not yet implemented") - case 1: - // name = n.Identity.ID.String() - ref = args[0] - - default: - return fmt.Errorf("Publish expects 1 or 2 args; got %d.", len(args)) - } - - // later, n.Keychain.Get(name).PrivKey - k := n.Identity.PrivKey() - - pub := nsys.NewRoutingPublisher(n.Routing) - err := pub.Publish(k, ref) - if err != nil { - return err - } - - hash, err := k.GetPublic().Hash() - if err != nil { - return err - } - fmt.Fprintf(out, "published name %s to %s\n", u.Key(hash), ref) - - return nil -} diff --git a/core/commands2/refs.go b/core/commands2/refs.go deleted file mode 100644 index 98fe94aca..000000000 --- a/core/commands2/refs.go +++ /dev/null @@ -1,65 +0,0 @@ -package commands - -import ( - "io" - - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - "github.com/jbenet/go-ipfs/core" - mdag "github.com/jbenet/go-ipfs/merkledag" - u "github.com/jbenet/go-ipfs/util" -) - -func Refs(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { - unique, ok := opts["u"].(bool) - if !ok { - unique = false - } - - recursive, ok := opts["r"].(bool) - if !ok { - recursive = false - } - - var refsSeen map[u.Key]bool - if unique { - refsSeen = make(map[u.Key]bool) - } - - for _, fn := range args { - nd, err := n.Resolver.ResolvePath(fn) - if err != nil { - return err - } - - printRefs(n, nd, refsSeen, recursive) - } - return nil -} - -func printRefs(n *core.IpfsNode, nd *mdag.Node, refSeen map[u.Key]bool, recursive bool) { - for _, link := range nd.Links { - printRef(link.Hash, refSeen) - - if recursive { - nd, err := n.DAG.Get(u.Key(link.Hash)) - if err != nil { - log.Errorf("error: cannot retrieve %s (%s)", link.Hash.B58String(), err) - return - } - - printRefs(n, nd, refSeen, recursive) - } - } -} - -func printRef(h mh.Multihash, refsSeen map[u.Key]bool) { - if refsSeen != nil { - _, found := refsSeen[u.Key(h)] - if found { - return - } - refsSeen[u.Key(h)] = true - } - - u.POut("%s\n", h.B58String()) -} diff --git a/core/commands2/resolve.go b/core/commands2/resolve.go deleted file mode 100644 index f6b462c8e..000000000 --- a/core/commands2/resolve.go +++ /dev/null @@ -1,35 +0,0 @@ -package commands - -import ( - "errors" - "fmt" - "io" - - "github.com/jbenet/go-ipfs/core" -) - -func Resolve(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { - - name := "" - - switch len(args) { - case 1: - name = args[0] - case 0: - if n.Identity == nil { - return errors.New("Identity not loaded!") - } - name = n.Identity.ID().String() - - default: - return fmt.Errorf("Publish expects 1 or 2 args; got %d.", len(args)) - } - - res, err := n.Namesys.Resolve(name) - if err != nil { - return err - } - - fmt.Fprintf(out, "%s\n", res) - return nil -} diff --git a/core/commands2/update.go b/core/commands2/update.go deleted file mode 100644 index 68f9456dd..000000000 --- a/core/commands2/update.go +++ /dev/null @@ -1,76 +0,0 @@ -package commands - -import ( - "errors" - "fmt" - "io" - "os" - - "github.com/jbenet/go-ipfs/core" - "github.com/jbenet/go-ipfs/updates" -) - -// UpdateApply applys an update of the ipfs binary and shuts down the node if successful -func UpdateApply(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { - fmt.Fprintln(out, "Current Version:", updates.Version) - u, err := updates.CheckForUpdate() - if err != nil { - return err - } - - if u == nil { - fmt.Fprintln(out, "No update available") - return nil - } - fmt.Fprintln(out, "New Version:", u.Version) - - _, onDaemon := opts["onDaemon"] - force := opts["force"].(bool) - if onDaemon && !force { - return fmt.Errorf(`Error: update must stop running ipfs service. -You may want to abort the update, or shut the service down manually. -To shut it down automatically, run: - - ipfs update --force -`) - } - - if err = updates.Apply(u); err != nil { - fmt.Fprint(out, err.Error()) - return fmt.Errorf("Couldn't apply update: %v", err) - } - - fmt.Fprintln(out, "Updated applied!") - if onDaemon { - if force { - fmt.Fprintln(out, "Shutting down ipfs service.") - os.Exit(1) // is there a cleaner shutdown routine? - } else { - fmt.Fprintln(out, "You can now restart the ipfs service.") - } - } - - return nil -} - -// UpdateCheck checks wether there is an update available -func UpdateCheck(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { - fmt.Fprintln(out, "Current Version:", updates.Version) - u, err := updates.CheckForUpdate() - if err != nil { - return err - } - - if u == nil { - fmt.Fprintln(out, "No update available") - return nil - } - - fmt.Fprintln(out, "New Version:", u.Version) - return nil -} - -// UpdateLog lists the version available online -func UpdateLog(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { - return errors.New("Not yet implemented") -} From 93e959a4596c26b1eab7cd385102b1117f6c54d5 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 29 Oct 2014 00:23:19 -0700 Subject: [PATCH 051/383] fix(cmd/ipfs2, commands) imports --- cmd/ipfs2/ipfs.go | 2 +- cmd/ipfs2/root.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go index 6fae87cdb..8d99435f5 100644 --- a/cmd/ipfs2/ipfs.go +++ b/cmd/ipfs2/ipfs.go @@ -11,7 +11,7 @@ import ( cmdsHttp "github.com/jbenet/go-ipfs/commands/http" "github.com/jbenet/go-ipfs/config" "github.com/jbenet/go-ipfs/core" - "github.com/jbenet/go-ipfs/core/commands" + commands "github.com/jbenet/go-ipfs/core/commands2" "github.com/jbenet/go-ipfs/daemon" u "github.com/jbenet/go-ipfs/util" ) diff --git a/cmd/ipfs2/root.go b/cmd/ipfs2/root.go index bd84f43a8..62fa2473b 100644 --- a/cmd/ipfs2/root.go +++ b/cmd/ipfs2/root.go @@ -2,7 +2,7 @@ package main import ( cmds "github.com/jbenet/go-ipfs/commands" - "github.com/jbenet/go-ipfs/core/commands" + commands "github.com/jbenet/go-ipfs/core/commands2" ) var Root = &cmds.Command{ From 525126c7e83209b1cb4cb0f4a44702cf1120de3f Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 29 Oct 2014 00:28:30 -0700 Subject: [PATCH 052/383] move new daemon to daemon2 --- {daemon => daemon2}/daemon.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {daemon => daemon2}/daemon.go (100%) diff --git a/daemon/daemon.go b/daemon2/daemon.go similarity index 100% rename from daemon/daemon.go rename to daemon2/daemon.go From 8c56280cb907d09b1c37aabd5b73aab0c8b68e0e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 29 Oct 2014 00:28:41 -0700 Subject: [PATCH 053/383] put original daemon back in place --- {daemon-original => daemon}/daemon.go | 0 {daemon-original => daemon}/daemon_client.go | 0 {daemon-original => daemon}/daemon_test.go | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {daemon-original => daemon}/daemon.go (100%) rename {daemon-original => daemon}/daemon_client.go (100%) rename {daemon-original => daemon}/daemon_test.go (100%) diff --git a/daemon-original/daemon.go b/daemon/daemon.go similarity index 100% rename from daemon-original/daemon.go rename to daemon/daemon.go diff --git a/daemon-original/daemon_client.go b/daemon/daemon_client.go similarity index 100% rename from daemon-original/daemon_client.go rename to daemon/daemon_client.go diff --git a/daemon-original/daemon_test.go b/daemon/daemon_test.go similarity index 100% rename from daemon-original/daemon_test.go rename to daemon/daemon_test.go From 026c30fa71d7568a6f86f5e920f1abee372861e8 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 29 Oct 2014 00:29:36 -0700 Subject: [PATCH 054/383] fix(cmd/ipfs2/daemon) imports --- cmd/ipfs2/daemon.go | 2 +- cmd/ipfs2/ipfs.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/ipfs2/daemon.go b/cmd/ipfs2/daemon.go index d39ef3c48..49907aa50 100644 --- a/cmd/ipfs2/daemon.go +++ b/cmd/ipfs2/daemon.go @@ -10,7 +10,7 @@ import ( cmds "github.com/jbenet/go-ipfs/commands" cmdsHttp "github.com/jbenet/go-ipfs/commands/http" "github.com/jbenet/go-ipfs/core" - "github.com/jbenet/go-ipfs/daemon" + daemon "github.com/jbenet/go-ipfs/daemon2" ) var Daemon = &cmds.Command{ diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go index 8d99435f5..f3b1a39bf 100644 --- a/cmd/ipfs2/ipfs.go +++ b/cmd/ipfs2/ipfs.go @@ -12,7 +12,7 @@ import ( "github.com/jbenet/go-ipfs/config" "github.com/jbenet/go-ipfs/core" commands "github.com/jbenet/go-ipfs/core/commands2" - "github.com/jbenet/go-ipfs/daemon" + daemon "github.com/jbenet/go-ipfs/daemon2" u "github.com/jbenet/go-ipfs/util" ) From 0aa205f36e3decfecd85cb38b74c34f3913067f9 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 29 Oct 2014 01:11:57 -0700 Subject: [PATCH 055/383] fix(core/commands2) ipfs help message --- core/commands2/root.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/core/commands2/root.go b/core/commands2/root.go index 673d874d9..fb05f87b5 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -2,8 +2,9 @@ package commands import ( "fmt" - cmds "github.com/jbenet/go-ipfs/commands" "strings" + + cmds "github.com/jbenet/go-ipfs/commands" ) type TestOutput struct { @@ -31,6 +32,7 @@ Basic commands: Tool commands: config Manage configuration. + update Download and apply go-ipfs updates. version Show ipfs version information. commands List all available commands. @@ -38,7 +40,13 @@ Advanced Commands: mount Mount an ipfs read-only mountpoint. serve Serve an interface to ipfs. - net-diag Print network diagnostic. + net-diag Print network diagnostic + +Plumbing commands: + + block Interact with raw blocks in the datastore + object Interact with raw dag nodes + Use "ipfs help " for more information about a command. `, From eb1a5618a09599576bd3de684c4a9c18dc761531 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 29 Oct 2014 01:22:11 -0700 Subject: [PATCH 056/383] chore(Makefile) provide install task --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index dcb369941..7c7cb176c 100644 --- a/Makefile +++ b/Makefile @@ -10,9 +10,10 @@ godep: vendor: godep godep save -r ./... - +# TODO remove ipfs2 once new command refactoring is complete install: cd cmd/ipfs && go install + cd cmd/ipfs2 && go install test: test_go test_sharness From 1dd0ade3ab65e8ca45e14e6ac8005d2e140a5030 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 29 Oct 2014 01:34:17 -0700 Subject: [PATCH 057/383] fix(commands2/cat) rm extraneous print in output --- core/commands2/cat.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/commands2/cat.go b/core/commands2/cat.go index a81ff4eec..465de09db 100644 --- a/core/commands2/cat.go +++ b/core/commands2/cat.go @@ -1,7 +1,6 @@ package commands import ( - "fmt" "io" cmds "github.com/jbenet/go-ipfs/commands" @@ -12,7 +11,6 @@ var cat = &cmds.Command{ Help: "TODO", Run: func(req cmds.Request, res cmds.Response) { node := req.Context().Node - fmt.Println(node.Resolver) readers := make([]io.Reader, 0, len(req.Arguments())) for _, path := range req.Arguments() { From b3e7cbb9a55845c87a432fe39141ab0761f4ad81 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 29 Oct 2014 01:42:52 -0700 Subject: [PATCH 058/383] refactor(commands) swap argument order to match Http(w, r) idiom --- cmd/ipfs2/daemon.go | 2 +- core/commands2/cat.go | 2 +- core/commands2/root.go | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/ipfs2/daemon.go b/cmd/ipfs2/daemon.go index 49907aa50..c99a8533c 100644 --- a/cmd/ipfs2/daemon.go +++ b/cmd/ipfs2/daemon.go @@ -20,7 +20,7 @@ var Daemon = &cmds.Command{ Run: daemonFunc, } -func daemonFunc(req cmds.Request, res cmds.Response) { +func daemonFunc(res cmds.Response, req cmds.Request) { ctx := req.Context() node, err := core.NewIpfsNode(ctx.Config, true) diff --git a/core/commands2/cat.go b/core/commands2/cat.go index 465de09db..6957c1109 100644 --- a/core/commands2/cat.go +++ b/core/commands2/cat.go @@ -9,7 +9,7 @@ import ( var cat = &cmds.Command{ Help: "TODO", - Run: func(req cmds.Request, res cmds.Response) { + Run: func(res cmds.Response, req cmds.Request) { node := req.Context().Node readers := make([]io.Reader, 0, len(req.Arguments())) diff --git a/core/commands2/root.go b/core/commands2/root.go index fb05f87b5..2440156ec 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -56,7 +56,7 @@ Use "ipfs help " for more information about a command. // test subcommands // TODO: remove these when we don't need them anymore "beep": &cmds.Command{ - Run: func(req cmds.Request, res cmds.Response) { + Run: func(res cmds.Response, req cmds.Request) { v := &TestOutput{"hello, world", 1337} res.SetValue(v) }, @@ -69,7 +69,7 @@ Use "ipfs help " for more information about a command. Type: &TestOutput{}, }, "boop": &cmds.Command{ - Run: func(req cmds.Request, res cmds.Response) { + Run: func(res cmds.Response, req cmds.Request) { v := strings.NewReader("hello, world") res.SetValue(v) }, @@ -78,7 +78,7 @@ Use "ipfs help " for more information about a command. Options: []cmds.Option{ cmds.Option{[]string{"power", "p"}, cmds.Float}, }, - Run: func(req cmds.Request, res cmds.Response) { + Run: func(res cmds.Response, req cmds.Request) { threshold := 1.21 if power, found := req.Option("power"); found && power.(float64) >= threshold { @@ -94,12 +94,12 @@ Use "ipfs help " for more information about a command. }, }, "args": &cmds.Command{ - Run: func(req cmds.Request, res cmds.Response) { + Run: func(res cmds.Response, req cmds.Request) { res.SetValue(req.Arguments()) }, }, "echo": &cmds.Command{ - Run: func(req cmds.Request, res cmds.Response) { + Run: func(res cmds.Response, req cmds.Request) { res.SetValue(req.Stream()) }, }, From db7a42e73c87272ef50683f19511f0f75f78ea33 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 29 Oct 2014 01:44:42 -0700 Subject: [PATCH 059/383] refactor(cmd/ipfs2) rename to match cmd/ipfs1 --- cmd/ipfs2/ipfs.go | 175 ++------------------------------------------- cmd/ipfs2/main.go | 177 ++++++++++++++++++++++++++++++++++++++++++++++ cmd/ipfs2/root.go | 14 ---- 3 files changed, 183 insertions(+), 183 deletions(-) create mode 100644 cmd/ipfs2/main.go delete mode 100644 cmd/ipfs2/root.go diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go index f3b1a39bf..62fa2473b 100644 --- a/cmd/ipfs2/ipfs.go +++ b/cmd/ipfs2/ipfs.go @@ -1,177 +1,14 @@ package main import ( - "fmt" - "io" - "os" - "runtime/pprof" - cmds "github.com/jbenet/go-ipfs/commands" - cmdsCli "github.com/jbenet/go-ipfs/commands/cli" - cmdsHttp "github.com/jbenet/go-ipfs/commands/http" - "github.com/jbenet/go-ipfs/config" - "github.com/jbenet/go-ipfs/core" commands "github.com/jbenet/go-ipfs/core/commands2" - daemon "github.com/jbenet/go-ipfs/daemon2" - u "github.com/jbenet/go-ipfs/util" ) -// log is the command logger -var log = u.Logger("cmd/ipfs") - -func main() { - args := os.Args[1:] - root := Root - - req, err := cmdsCli.Parse(args, root) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - // if the CLI-specific root doesn't contain the command, use the general root - if len(req.Path()) == 0 { - root = commands.Root - req, err = cmdsCli.Parse(args, root) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - } - - cmd, err := root.Get(req.Path()) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - options, err := getOptions(req, root) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - if help, found := options.Option("help"); found && help.(bool) { - fmt.Println(cmd.Help) - os.Exit(0) - } - - if debug, found := options.Option("debug"); found && debug.(bool) { - u.Debug = true - - // if debugging, setup profiling. - if u.Debug { - ofi, err := os.Create("cpu.prof") - if err != nil { - fmt.Println(err) - return - } - pprof.StartCPUProfile(ofi) - defer ofi.Close() - defer pprof.StopCPUProfile() - } - } - - configPath, err := getConfigRoot(options) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - conf, err := getConfig(configPath) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - ctx := req.Context() - ctx.ConfigRoot = configPath - ctx.Config = conf - - if _, found := options.Option("encoding"); !found { - if req.Command().Format != nil { - req.SetOption("encoding", cmds.Text) - } else { - req.SetOption("encoding", cmds.JSON) - } - } - - var res cmds.Response - if root == Root { - res = root.Call(req) - - } else { - local, found := options.Option("local") - - if (!found || !local.(bool)) && daemon.Locked(configPath) { - res, err = cmdsHttp.Send(req) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - } else { - node, err := core.NewIpfsNode(conf, false) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - ctx.Node = node - - res = root.Call(req) - } - } - - if res.Error() != nil { - fmt.Println(res.Error().Error()) - - if cmd.Help != "" && res.Error().Code == cmds.ErrClient { - // TODO: convert from markdown to ANSI terminal format? - fmt.Println(cmd.Help) - } - - os.Exit(1) - } - - _, err = io.Copy(os.Stdout, res) - if err != nil { - fmt.Println(err.Error()) - } -} - -func getOptions(req cmds.Request, root *cmds.Command) (cmds.Request, error) { - tempReq := cmds.NewRequest(req.Path(), req.Options(), nil, nil, nil) - - options, err := root.GetOptions(tempReq.Path()) - if err != nil { - return nil, err - } - - err = tempReq.ConvertOptions(options) - if err != nil { - return nil, err - } - - return tempReq, nil -} - -func getConfigRoot(req cmds.Request) (string, error) { - if opt, found := req.Option("config"); found { - return opt.(string), nil - } - - configPath, err := config.PathRoot() - if err != nil { - return "", err - } - return configPath, nil -} - -func getConfig(path string) (*config.Config, error) { - configFile, err := config.Filename(path) - if err != nil { - return nil, err - } - - return config.Load(configFile) +var Root = &cmds.Command{ + Options: commands.Root.Options, + Help: commands.Root.Help, + Subcommands: map[string]*cmds.Command{ + "daemon": Daemon, + }, } diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go new file mode 100644 index 000000000..f3b1a39bf --- /dev/null +++ b/cmd/ipfs2/main.go @@ -0,0 +1,177 @@ +package main + +import ( + "fmt" + "io" + "os" + "runtime/pprof" + + cmds "github.com/jbenet/go-ipfs/commands" + cmdsCli "github.com/jbenet/go-ipfs/commands/cli" + cmdsHttp "github.com/jbenet/go-ipfs/commands/http" + "github.com/jbenet/go-ipfs/config" + "github.com/jbenet/go-ipfs/core" + commands "github.com/jbenet/go-ipfs/core/commands2" + daemon "github.com/jbenet/go-ipfs/daemon2" + u "github.com/jbenet/go-ipfs/util" +) + +// log is the command logger +var log = u.Logger("cmd/ipfs") + +func main() { + args := os.Args[1:] + root := Root + + req, err := cmdsCli.Parse(args, root) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + // if the CLI-specific root doesn't contain the command, use the general root + if len(req.Path()) == 0 { + root = commands.Root + req, err = cmdsCli.Parse(args, root) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + } + + cmd, err := root.Get(req.Path()) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + options, err := getOptions(req, root) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + if help, found := options.Option("help"); found && help.(bool) { + fmt.Println(cmd.Help) + os.Exit(0) + } + + if debug, found := options.Option("debug"); found && debug.(bool) { + u.Debug = true + + // if debugging, setup profiling. + if u.Debug { + ofi, err := os.Create("cpu.prof") + if err != nil { + fmt.Println(err) + return + } + pprof.StartCPUProfile(ofi) + defer ofi.Close() + defer pprof.StopCPUProfile() + } + } + + configPath, err := getConfigRoot(options) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + conf, err := getConfig(configPath) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + ctx := req.Context() + ctx.ConfigRoot = configPath + ctx.Config = conf + + if _, found := options.Option("encoding"); !found { + if req.Command().Format != nil { + req.SetOption("encoding", cmds.Text) + } else { + req.SetOption("encoding", cmds.JSON) + } + } + + var res cmds.Response + if root == Root { + res = root.Call(req) + + } else { + local, found := options.Option("local") + + if (!found || !local.(bool)) && daemon.Locked(configPath) { + res, err = cmdsHttp.Send(req) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + } else { + node, err := core.NewIpfsNode(conf, false) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + ctx.Node = node + + res = root.Call(req) + } + } + + if res.Error() != nil { + fmt.Println(res.Error().Error()) + + if cmd.Help != "" && res.Error().Code == cmds.ErrClient { + // TODO: convert from markdown to ANSI terminal format? + fmt.Println(cmd.Help) + } + + os.Exit(1) + } + + _, err = io.Copy(os.Stdout, res) + if err != nil { + fmt.Println(err.Error()) + } +} + +func getOptions(req cmds.Request, root *cmds.Command) (cmds.Request, error) { + tempReq := cmds.NewRequest(req.Path(), req.Options(), nil, nil, nil) + + options, err := root.GetOptions(tempReq.Path()) + if err != nil { + return nil, err + } + + err = tempReq.ConvertOptions(options) + if err != nil { + return nil, err + } + + return tempReq, nil +} + +func getConfigRoot(req cmds.Request) (string, error) { + if opt, found := req.Option("config"); found { + return opt.(string), nil + } + + configPath, err := config.PathRoot() + if err != nil { + return "", err + } + return configPath, nil +} + +func getConfig(path string) (*config.Config, error) { + configFile, err := config.Filename(path) + if err != nil { + return nil, err + } + + return config.Load(configFile) +} diff --git a/cmd/ipfs2/root.go b/cmd/ipfs2/root.go deleted file mode 100644 index 62fa2473b..000000000 --- a/cmd/ipfs2/root.go +++ /dev/null @@ -1,14 +0,0 @@ -package main - -import ( - cmds "github.com/jbenet/go-ipfs/commands" - commands "github.com/jbenet/go-ipfs/core/commands2" -) - -var Root = &cmds.Command{ - Options: commands.Root.Options, - Help: commands.Root.Help, - Subcommands: map[string]*cmds.Command{ - "daemon": Daemon, - }, -} From 9fb20dabb3fb47f1c20a64a3c7cda9fe2705d822 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 29 Oct 2014 02:19:56 -0700 Subject: [PATCH 060/383] cmd/ipfs2: Added init command --- cmd/ipfs2/init.go | 157 ++++++++++++++++++++++++++++++++++++++++++++++ cmd/ipfs2/ipfs.go | 1 + 2 files changed, 158 insertions(+) create mode 100644 cmd/ipfs2/init.go diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go new file mode 100644 index 000000000..4111481c7 --- /dev/null +++ b/cmd/ipfs2/init.go @@ -0,0 +1,157 @@ +package main + +import ( + "encoding/base64" + "errors" + "os" + "path/filepath" + + cmds "github.com/jbenet/go-ipfs/commands" + config "github.com/jbenet/go-ipfs/config" + ci "github.com/jbenet/go-ipfs/crypto" + peer "github.com/jbenet/go-ipfs/peer" + updates "github.com/jbenet/go-ipfs/updates" + u "github.com/jbenet/go-ipfs/util" +) + +var initCmd = &cmds.Command{ + Options: []cmds.Option{ + cmds.Option{[]string{"bits", "b"}, cmds.Int}, + cmds.Option{[]string{"passphrase", "p"}, cmds.String}, + cmds.Option{[]string{"force", "f"}, cmds.Bool}, + cmds.Option{[]string{"datastore", "d"}, cmds.String}, + }, + Help: `ipfs init + + Initializes ipfs configuration files and generates a + new keypair. +`, + Run: func(res cmds.Response, req cmds.Request) { + ctx := req.Context() + + u.POut("initializing ipfs node at %s\n", ctx.ConfigRoot) + filename, err := config.Filename(ctx.ConfigRoot) + if err != nil { + res.SetError(errors.New("Couldn't get home directory path"), cmds.ErrNormal) + return + } + + arg, found := req.Option("d") + dspath, ok := arg.(string) + if found && !ok { + res.SetError(errors.New("failed to parse datastore flag"), cmds.ErrNormal) + return + } + + fi, err := os.Lstat(filename) + arg, found = req.Option("f") + force, ok := arg.(bool) + if found && !ok { + res.SetError(errors.New("failed to parse force flag"), cmds.ErrNormal) + return + } + if fi != nil || (err != nil && !os.IsNotExist(err)) { + if !force { + res.SetError(errors.New("ipfs configuration file already exists!\nReinitializing would overwrite your keys.\n(use -f to force overwrite)"), cmds.ErrNormal) + return + } + } + cfg := new(config.Config) + + cfg.Datastore = config.Datastore{} + if len(dspath) == 0 { + dspath, err = config.DataStorePath("") + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + } + cfg.Datastore.Path = dspath + cfg.Datastore.Type = "leveldb" + + // Construct the data store if missing + if err := os.MkdirAll(dspath, os.ModePerm); err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + // Check the directory is writeable + if f, err := os.Create(filepath.Join(dspath, "._check_writeable")); err == nil { + os.Remove(f.Name()) + } else { + res.SetError(errors.New("Datastore '"+dspath+"' is not writeable"), cmds.ErrNormal) + return + } + + cfg.Identity = config.Identity{} + + // setup the node addresses. + cfg.Addresses = config.Addresses{ + Swarm: "/ip4/0.0.0.0/tcp/4001", + API: "/ip4/127.0.0.1/tcp/5001", + } + + // setup the node mount points. + cfg.Mounts = config.Mounts{ + IPFS: "/ipfs", + IPNS: "/ipns", + } + + arg, found = req.Option("b") + nbits, ok := arg.(int) + if found && !ok { + res.SetError(errors.New("failed to get bits flag"), cmds.ErrNormal) + return + } else if !found { + nbits = 4096 + } + if nbits < 1024 { + res.SetError(errors.New("Bitsize less than 1024 is considered unsafe."), cmds.ErrNormal) + return + } + + u.POut("generating key pair\n") + sk, pk, err := ci.GenerateKeyPair(ci.RSA, nbits) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + // currently storing key unencrypted. in the future we need to encrypt it. + // TODO(security) + skbytes, err := sk.Bytes() + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + cfg.Identity.PrivKey = base64.StdEncoding.EncodeToString(skbytes) + + id, err := peer.IDFromPubKey(pk) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + cfg.Identity.PeerID = id.Pretty() + + // Use these hardcoded bootstrap peers for now. + cfg.Bootstrap = []*config.BootstrapPeer{ + &config.BootstrapPeer{ + // mars.i.ipfs.io + PeerID: "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + Address: "/ip4/104.131.131.82/tcp/4001", + }, + } + + // tracking ipfs version used to generate the init folder and adding update checker default setting. + cfg.Version = config.Version{ + Check: "error", + Current: updates.Version, + } + + err = config.WriteConfigFile(filename, cfg) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + }, +} diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go index 62fa2473b..edcd5a83a 100644 --- a/cmd/ipfs2/ipfs.go +++ b/cmd/ipfs2/ipfs.go @@ -10,5 +10,6 @@ var Root = &cmds.Command{ Help: commands.Root.Help, Subcommands: map[string]*cmds.Command{ "daemon": Daemon, + "init": initCmd, }, } From decda218899bd6c826e196a0db2122fe254c5814 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 29 Oct 2014 21:12:34 -0700 Subject: [PATCH 061/383] cmd/ipfs2: Changed command variable naming convention --- cmd/ipfs2/daemon.go | 2 +- cmd/ipfs2/ipfs.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/ipfs2/daemon.go b/cmd/ipfs2/daemon.go index c99a8533c..1418030fb 100644 --- a/cmd/ipfs2/daemon.go +++ b/cmd/ipfs2/daemon.go @@ -13,7 +13,7 @@ import ( daemon "github.com/jbenet/go-ipfs/daemon2" ) -var Daemon = &cmds.Command{ +var daemonCmd = &cmds.Command{ Options: []cmds.Option{}, Help: "TODO", Subcommands: map[string]*cmds.Command{}, diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go index edcd5a83a..bb328085a 100644 --- a/cmd/ipfs2/ipfs.go +++ b/cmd/ipfs2/ipfs.go @@ -9,7 +9,7 @@ var Root = &cmds.Command{ Options: commands.Root.Options, Help: commands.Root.Help, Subcommands: map[string]*cmds.Command{ - "daemon": Daemon, + "daemon": daemonCmd, "init": initCmd, }, } From e01f1ba8a17a968ec77916c92bdc816b2ae47fd5 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 29 Oct 2014 22:11:17 -0700 Subject: [PATCH 062/383] cmd/ipfs2: daemon command: Handle lock before initializing node --- cmd/ipfs2/daemon.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/ipfs2/daemon.go b/cmd/ipfs2/daemon.go index 1418030fb..c177fc71e 100644 --- a/cmd/ipfs2/daemon.go +++ b/cmd/ipfs2/daemon.go @@ -23,13 +23,6 @@ var daemonCmd = &cmds.Command{ func daemonFunc(res cmds.Response, req cmds.Request) { ctx := req.Context() - node, err := core.NewIpfsNode(ctx.Config, true) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - ctx.Node = node - lk, err := daemon.Lock(ctx.ConfigRoot) if err != nil { res.SetError(fmt.Errorf("Couldn't obtain lock. Is another daemon already running?"), cmds.ErrNormal) @@ -37,6 +30,13 @@ func daemonFunc(res cmds.Response, req cmds.Request) { } defer lk.Close() + node, err := core.NewIpfsNode(ctx.Config, true) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + ctx.Node = node + addr, err := ma.NewMultiaddr(ctx.Config.Addresses.API) if err != nil { res.SetError(err, cmds.ErrNormal) From f2271356f5c879270535303f9587350496329679 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 30 Oct 2014 18:29:40 -0700 Subject: [PATCH 063/383] core/commands2: Added 'ls' command --- core/commands2/ls.go | 60 ++++++++++++++++++++++++++++++++++++++++++ core/commands2/root.go | 1 + 2 files changed, 61 insertions(+) create mode 100644 core/commands2/ls.go diff --git a/core/commands2/ls.go b/core/commands2/ls.go new file mode 100644 index 000000000..6fc06893b --- /dev/null +++ b/core/commands2/ls.go @@ -0,0 +1,60 @@ +package commands + +import ( + "fmt" + + cmds "github.com/jbenet/go-ipfs/commands" +) + +type Link struct { + Name, Hash string + Size uint64 +} + +var ls = &cmds.Command{ + Help: "TODO", + Run: func(res cmds.Response, req cmds.Request) { + node := req.Context().Node + output := make(map[string][]Link, len(req.Arguments())) + + for _, path := range req.Arguments() { + dagnode, err := node.Resolver.ResolvePath(path) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + output[path] = make([]Link, len(dagnode.Links)) + for i, link := range dagnode.Links { + output[path][i] = Link{ + Name: link.Name, + Hash: link.Hash.B58String(), + Size: link.Size, + } + } + } + + res.SetValue(output) + }, + Format: func(res cmds.Response) (string, error) { + s := "" + output := res.Value().(*map[string][]Link) + + for path, links := range *output { + if len(*output) > 1 { + s += fmt.Sprintf("%s:\n", path) + } + + for _, link := range links { + s += fmt.Sprintf("-> %s %s (%v bytes)\n", link.Name, link.Hash, link.Size) + } + + if len(*output) > 1 { + s += "\n" + } + } + + return s, nil + }, + Type: &map[string][]Link{}, +} diff --git a/core/commands2/root.go b/core/commands2/root.go index 2440156ec..4182410b9 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -52,6 +52,7 @@ Use "ipfs help " for more information about a command. `, Subcommands: map[string]*cmds.Command{ "cat": cat, + "ls": ls, // test subcommands // TODO: remove these when we don't need them anymore From 17c6907e1f6adca30ba482c3beecbba84f750c5a Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 30 Oct 2014 18:50:29 -0700 Subject: [PATCH 064/383] core/command2: Fixed ls output for XML marshalling --- core/commands2/ls.go | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/core/commands2/ls.go b/core/commands2/ls.go index 6fc06893b..006bb97e0 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -11,22 +11,34 @@ type Link struct { Size uint64 } +type Object struct { + Hash string + Links []Link +} + +type LsOutput struct { + Objects []Object +} + var ls = &cmds.Command{ Help: "TODO", Run: func(res cmds.Response, req cmds.Request) { node := req.Context().Node - output := make(map[string][]Link, len(req.Arguments())) + output := make([]Object, len(req.Arguments())) - for _, path := range req.Arguments() { + for i, path := range req.Arguments() { dagnode, err := node.Resolver.ResolvePath(path) if err != nil { res.SetError(err, cmds.ErrNormal) return } - output[path] = make([]Link, len(dagnode.Links)) - for i, link := range dagnode.Links { - output[path][i] = Link{ + output[i] = Object{ + Hash: path, + Links: make([]Link, len(dagnode.Links)), + } + for j, link := range dagnode.Links { + output[i].Links[j] = Link{ Name: link.Name, Hash: link.Hash.B58String(), Size: link.Size, @@ -34,27 +46,27 @@ var ls = &cmds.Command{ } } - res.SetValue(output) + res.SetValue(&LsOutput{output}) }, Format: func(res cmds.Response) (string, error) { s := "" - output := res.Value().(*map[string][]Link) + output := res.Value().(*LsOutput).Objects - for path, links := range *output { - if len(*output) > 1 { - s += fmt.Sprintf("%s:\n", path) + for _, object := range output { + if len(output) > 1 { + s += fmt.Sprintf("%s:\n", object.Hash) } - for _, link := range links { + for _, link := range object.Links { s += fmt.Sprintf("-> %s %s (%v bytes)\n", link.Name, link.Hash, link.Size) } - if len(*output) > 1 { + if len(output) > 1 { s += "\n" } } return s, nil }, - Type: &map[string][]Link{}, + Type: &LsOutput{}, } From fb12530822708ccb4c4d4bc4426ddb0b8baf2bcb Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 30 Oct 2014 20:39:45 -0700 Subject: [PATCH 065/383] cmd/ipfs2: Got rid of second root command in favor of Command#Private --- cmd/ipfs2/main.go | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index f3b1a39bf..67a19a601 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -21,38 +21,21 @@ var log = u.Logger("cmd/ipfs") func main() { args := os.Args[1:] - root := Root - req, err := cmdsCli.Parse(args, root) + req, err := cmdsCli.Parse(args, commands.Root) if err != nil { fmt.Println(err) os.Exit(1) } - // if the CLI-specific root doesn't contain the command, use the general root - if len(req.Path()) == 0 { - root = commands.Root - req, err = cmdsCli.Parse(args, root) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - } - - cmd, err := root.Get(req.Path()) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - options, err := getOptions(req, root) + options, err := getOptions(req, commands.Root) if err != nil { fmt.Println(err) os.Exit(1) } if help, found := options.Option("help"); found && help.(bool) { - fmt.Println(cmd.Help) + fmt.Println(req.Command().Help) os.Exit(0) } @@ -97,8 +80,8 @@ func main() { } var res cmds.Response - if root == Root { - res = root.Call(req) + if req.Command().Private { + res = commands.Root.Call(req) } else { local, found := options.Option("local") @@ -118,16 +101,16 @@ func main() { } ctx.Node = node - res = root.Call(req) + res = commands.Root.Call(req) } } if res.Error() != nil { fmt.Println(res.Error().Error()) - if cmd.Help != "" && res.Error().Code == cmds.ErrClient { + if req.Command().Help != "" && res.Error().Code == cmds.ErrClient { // TODO: convert from markdown to ANSI terminal format? - fmt.Println(cmd.Help) + fmt.Println(req.Command().Help) } os.Exit(1) From 9356e1a0a1c8e8626b08e172f25e27cc1ae59787 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 30 Oct 2014 20:42:31 -0700 Subject: [PATCH 066/383] cmd/ipfs: Moved private commands to core/commands2 --- cmd/ipfs2/ipfs.go | 15 --------------- core/commands2/cat.go | 2 +- {cmd/ipfs2 => core/commands2}/daemon.go | 3 ++- {cmd/ipfs2 => core/commands2}/init.go | 3 ++- core/commands2/ls.go | 2 +- core/commands2/root.go | 9 +++++++-- 6 files changed, 13 insertions(+), 21 deletions(-) delete mode 100644 cmd/ipfs2/ipfs.go rename {cmd/ipfs2 => core/commands2}/daemon.go (97%) rename {cmd/ipfs2 => core/commands2}/init.go (99%) diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go deleted file mode 100644 index bb328085a..000000000 --- a/cmd/ipfs2/ipfs.go +++ /dev/null @@ -1,15 +0,0 @@ -package main - -import ( - cmds "github.com/jbenet/go-ipfs/commands" - commands "github.com/jbenet/go-ipfs/core/commands2" -) - -var Root = &cmds.Command{ - Options: commands.Root.Options, - Help: commands.Root.Help, - Subcommands: map[string]*cmds.Command{ - "daemon": daemonCmd, - "init": initCmd, - }, -} diff --git a/core/commands2/cat.go b/core/commands2/cat.go index 6957c1109..363050cd6 100644 --- a/core/commands2/cat.go +++ b/core/commands2/cat.go @@ -7,7 +7,7 @@ import ( uio "github.com/jbenet/go-ipfs/unixfs/io" ) -var cat = &cmds.Command{ +var catCmd = &cmds.Command{ Help: "TODO", Run: func(res cmds.Response, req cmds.Request) { node := req.Context().Node diff --git a/cmd/ipfs2/daemon.go b/core/commands2/daemon.go similarity index 97% rename from cmd/ipfs2/daemon.go rename to core/commands2/daemon.go index c177fc71e..5dd5e8c89 100644 --- a/cmd/ipfs2/daemon.go +++ b/core/commands2/daemon.go @@ -1,4 +1,4 @@ -package main +package commands import ( "fmt" @@ -14,6 +14,7 @@ import ( ) var daemonCmd = &cmds.Command{ + Private: true, Options: []cmds.Option{}, Help: "TODO", Subcommands: map[string]*cmds.Command{}, diff --git a/cmd/ipfs2/init.go b/core/commands2/init.go similarity index 99% rename from cmd/ipfs2/init.go rename to core/commands2/init.go index 4111481c7..25b535009 100644 --- a/cmd/ipfs2/init.go +++ b/core/commands2/init.go @@ -1,4 +1,4 @@ -package main +package commands import ( "encoding/base64" @@ -15,6 +15,7 @@ import ( ) var initCmd = &cmds.Command{ + Private: true, Options: []cmds.Option{ cmds.Option{[]string{"bits", "b"}, cmds.Int}, cmds.Option{[]string{"passphrase", "p"}, cmds.String}, diff --git a/core/commands2/ls.go b/core/commands2/ls.go index 006bb97e0..ae68c91b0 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -20,7 +20,7 @@ type LsOutput struct { Objects []Object } -var ls = &cmds.Command{ +var lsCmd = &cmds.Command{ Help: "TODO", Run: func(res cmds.Response, req cmds.Request) { node := req.Context().Node diff --git a/core/commands2/root.go b/core/commands2/root.go index 4182410b9..5809dd786 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -51,8 +51,9 @@ Plumbing commands: Use "ipfs help " for more information about a command. `, Subcommands: map[string]*cmds.Command{ - "cat": cat, - "ls": ls, + "cat": catCmd, + "ls": lsCmd, + "init": initCmd, // test subcommands // TODO: remove these when we don't need them anymore @@ -106,3 +107,7 @@ Use "ipfs help " for more information about a command. }, }, } + +func init() { + Root.Subcommands["daemon"] = daemonCmd +} From 921d3a51ab3ed10489df2721ce8450d672074e7f Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 30 Oct 2014 20:44:31 -0700 Subject: [PATCH 067/383] commands/http: Pass root command in as field instead of statically depending on core/commands --- core/commands2/daemon.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/commands2/daemon.go b/core/commands2/daemon.go index 5dd5e8c89..9260b8727 100644 --- a/core/commands2/daemon.go +++ b/core/commands2/daemon.go @@ -50,7 +50,7 @@ func daemonFunc(res cmds.Response, req cmds.Request) { return } - handler := cmdsHttp.Handler{*ctx} + handler := cmdsHttp.Handler{*ctx, Root} http.Handle(cmdsHttp.ApiPath+"/", handler) fmt.Printf("API server listening on '%s'\n", host) From 0c205f56cb4a254ab1eb00ea13ed8fd209f9e206 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 30 Oct 2014 21:19:19 -0700 Subject: [PATCH 068/383] core/commands2: Added 'commands' command --- core/commands2/commands.go | 56 ++++++++++++++++++++++++++++++++++++++ core/commands2/root.go | 1 + 2 files changed, 57 insertions(+) create mode 100644 core/commands2/commands.go diff --git a/core/commands2/commands.go b/core/commands2/commands.go new file mode 100644 index 000000000..999fac5c7 --- /dev/null +++ b/core/commands2/commands.go @@ -0,0 +1,56 @@ +package commands + +import ( + "fmt" + "strings" + + cmds "github.com/jbenet/go-ipfs/commands" +) + +type Command struct { + Name string + Subcommands []Command +} + +var commandsCmd = &cmds.Command{ + Help: "TODO", + Run: func(res cmds.Response, req cmds.Request) { + root := outputCommand("ipfs", Root) + res.SetValue(&root) + }, + Format: func(res cmds.Response) (string, error) { + v := res.Value().(*Command) + return formatCommand(v, 0), nil + }, + Type: &Command{}, +} + +func outputCommand(name string, cmd *cmds.Command) Command { + output := Command{ + Name: name, + Subcommands: make([]Command, len(cmd.Subcommands)), + } + + i := 0 + for name, sub := range cmd.Subcommands { + output.Subcommands[i] = outputCommand(name, sub) + i++ + } + + return output +} + +func formatCommand(cmd *Command, depth int) string { + var s string + + if depth > 0 { + indent := strings.Repeat(" ", depth-1) + s = fmt.Sprintf("%s%s\n", indent, cmd.Name) + } + + for _, sub := range cmd.Subcommands { + s += formatCommand(&sub, depth+1) + } + + return s +} diff --git a/core/commands2/root.go b/core/commands2/root.go index 5809dd786..59967c742 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -110,4 +110,5 @@ Use "ipfs help " for more information about a command. func init() { Root.Subcommands["daemon"] = daemonCmd + Root.Subcommands["commands"] = commandsCmd } From 6ae09b167d0870ad408089506278a549a1f562ee Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 30 Oct 2014 22:28:02 -0700 Subject: [PATCH 069/383] core/commands: Moved commands that were rebased into the wrong directory --- core/commands/add.go | 22 ++++++- core/commands/block.go | 6 +- core/commands/object.go | 6 +- core/commands2/add.go | 136 --------------------------------------- core/commands2/block.go | 60 ----------------- core/commands2/object.go | 135 -------------------------------------- 6 files changed, 26 insertions(+), 339 deletions(-) delete mode 100644 core/commands2/add.go delete mode 100644 core/commands2/block.go delete mode 100644 core/commands2/object.go diff --git a/core/commands/add.go b/core/commands/add.go index b6d1708f8..7bcc5119f 100644 --- a/core/commands/add.go +++ b/core/commands/add.go @@ -10,8 +10,11 @@ import ( "github.com/jbenet/go-ipfs/core" "github.com/jbenet/go-ipfs/importer" + "github.com/jbenet/go-ipfs/importer/chunk" dag "github.com/jbenet/go-ipfs/merkledag" + "github.com/jbenet/go-ipfs/pin" ft "github.com/jbenet/go-ipfs/unixfs" + uio "github.com/jbenet/go-ipfs/unixfs/io" ) // Error indicating the max depth has been exceded. @@ -87,7 +90,14 @@ func addDir(n *core.IpfsNode, fpath string, depth int, out io.Writer) (*dag.Node } func addFile(n *core.IpfsNode, fpath string, depth int, out io.Writer) (*dag.Node, error) { - root, err := importer.NewDagFromFile(fpath) + dw := uio.NewDagWriter(n.DAG, chunk.DefaultSplitter) + mp, ok := n.Pinning.(pin.ManualPinner) + if !ok { + return nil, errors.New("invalid pinner type! expected manual pinner") + } + dw.Pinner = mp + + root, err := importer.ImportFileDag(fpath, dw) if err != nil { return nil, err } @@ -98,7 +108,15 @@ func addFile(n *core.IpfsNode, fpath string, depth int, out io.Writer) (*dag.Nod log.Info("adding subblock: %s %s", l.Name, l.Hash.B58String()) } - return root, addNode(n, root, fpath, out) + k, err := root.Key() + if err != nil { + return nil, err + } + + // output that we've added this node + fmt.Fprintf(out, "added %s %s\n", k, fpath) + + return root, nil } // addNode adds the node to the graph + local storage diff --git a/core/commands/block.go b/core/commands/block.go index 089ed3734..2448d6977 100644 --- a/core/commands/block.go +++ b/core/commands/block.go @@ -7,7 +7,7 @@ import ( "os" "time" - "code.google.com/p/go.net/context" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" "github.com/jbenet/go-ipfs/blocks" @@ -28,7 +28,7 @@ func BlockGet(n *core.IpfsNode, args []string, opts map[string]interface{}, out } k := u.Key(h) - log.Debug("BlockGet key: '%q'", k) + log.Debugf("BlockGet key: '%q'", k) ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) b, err := n.Blocks.GetBlock(ctx, k) if err != nil { @@ -48,7 +48,7 @@ func BlockPut(n *core.IpfsNode, args []string, opts map[string]interface{}, out } b := blocks.NewBlock(data) - log.Debug("BlockPut key: '%q'", b.Key()) + log.Debugf("BlockPut key: '%q'", b.Key()) k, err := n.Blocks.AddBlock(b) if err != nil { diff --git a/core/commands/object.go b/core/commands/object.go index 5d0ccddab..24386a173 100644 --- a/core/commands/object.go +++ b/core/commands/object.go @@ -19,7 +19,7 @@ func ObjectData(n *core.IpfsNode, args []string, opts map[string]interface{}, ou 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)) + log.Debugf("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 @@ -31,7 +31,7 @@ func ObjectLinks(n *core.IpfsNode, args []string, opts map[string]interface{}, o 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)) + log.Debugf("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) @@ -70,7 +70,7 @@ func ObjectGet(n *core.IpfsNode, args []string, opts map[string]interface{}, out 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)) + log.Debugf("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 diff --git a/core/commands2/add.go b/core/commands2/add.go deleted file mode 100644 index 0208354cd..000000000 --- a/core/commands2/add.go +++ /dev/null @@ -1,136 +0,0 @@ -package commands - -import ( - "errors" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - - "github.com/jbenet/go-ipfs/core" - "github.com/jbenet/go-ipfs/importer" - dag "github.com/jbenet/go-ipfs/merkledag" - "github.com/jbenet/go-ipfs/pin" - ft "github.com/jbenet/go-ipfs/unixfs" -) - -// Error indicating the max depth has been exceded. -var ErrDepthLimitExceeded = fmt.Errorf("depth limit exceeded") - -// Add is a command that imports files and directories -- given as arguments -- into ipfs. -func Add(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { - depth := 1 - - // if recursive, set depth to reflect so - if r, ok := opts["r"].(bool); r && ok { - depth = -1 - } - - // add every path in args - for _, path := range args { - - // Add the file - _, err := AddPath(n, path, depth, out) - if err != nil { - if err == ErrDepthLimitExceeded && depth == 1 { - err = errors.New("use -r to recursively add directories") - } - return fmt.Errorf("addFile error: %v", err) - } - - } - return nil -} - -// AddPath adds a particular path to ipfs. -func AddPath(n *core.IpfsNode, fpath string, depth int, out io.Writer) (*dag.Node, error) { - if depth == 0 { - return nil, ErrDepthLimitExceeded - } - - fi, err := os.Stat(fpath) - if err != nil { - return nil, err - } - - if fi.IsDir() { - return addDir(n, fpath, depth, out) - } - - return addFile(n, fpath, depth, out) -} - -func addDir(n *core.IpfsNode, fpath string, depth int, out io.Writer) (*dag.Node, error) { - tree := &dag.Node{Data: ft.FolderPBData()} - - files, err := ioutil.ReadDir(fpath) - if err != nil { - return nil, err - } - - // construct nodes for containing files. - for _, f := range files { - fp := filepath.Join(fpath, f.Name()) - nd, err := AddPath(n, fp, depth-1, out) - if err != nil { - return nil, err - } - - if err = tree.AddNodeLink(f.Name(), nd); err != nil { - return nil, err - } - } - - log.Infof("adding dir: %s", fpath) - - return tree, addNode(n, tree, fpath, out) -} - -func addFile(n *core.IpfsNode, fpath string, depth int, out io.Writer) (*dag.Node, error) { - mp, ok := n.Pinning.(pin.ManualPinner) - if !ok { - return nil, errors.New("invalid pinner type! expected manual pinner") - } - - root, err := importer.BuildDagFromFile(fpath, n.DAG, mp) - if err != nil { - return nil, err - } - - log.Infof("adding file: %s", fpath) - - for _, l := range root.Links { - log.Infof("adding subblock: '%s' %s", l.Name, l.Hash.B58String()) - } - - k, err := root.Key() - if err != nil { - return nil, err - } - - // output that we've added this node - fmt.Fprintf(out, "added %s %s\n", k, fpath) - - return root, nil -} - -// addNode adds the node to the graph + local storage -func addNode(n *core.IpfsNode, nd *dag.Node, fpath string, out io.Writer) error { - // add the file to the graph + local storage - err := n.DAG.AddRecursive(nd) - if err != nil { - return err - } - - k, err := nd.Key() - if err != nil { - return err - } - - // output that we've added this node - fmt.Fprintf(out, "added %s %s\n", k, fpath) - - // ensure we keep it - return n.Pinning.Pin(nd, true) -} diff --git a/core/commands2/block.go b/core/commands2/block.go deleted file mode 100644 index 2448d6977..000000000 --- a/core/commands2/block.go +++ /dev/null @@ -1,60 +0,0 @@ -package commands - -import ( - "fmt" - "io" - "io/ioutil" - "os" - "time" - - "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" - - mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - "github.com/jbenet/go-ipfs/blocks" - "github.com/jbenet/go-ipfs/core" - u "github.com/jbenet/go-ipfs/util" -) - -// BlockGet retrives a raw ipfs block from the node's BlockService -func BlockGet(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { - - if !u.IsValidHash(args[0]) { - return fmt.Errorf("block get: not a valid hash") - } - - h, err := mh.FromB58String(args[0]) - if err != nil { - return fmt.Errorf("block get: %v", err) - } - - k := u.Key(h) - log.Debugf("BlockGet key: '%q'", k) - ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) - b, err := n.Blocks.GetBlock(ctx, k) - if err != nil { - return fmt.Errorf("block get: %v", err) - } - - _, err = out.Write(b.Data) - return err -} - -// BlockPut reads everything from conn and saves the data to the nodes BlockService -func BlockPut(n *core.IpfsNode, args []string, opts map[string]interface{}, out io.Writer) error { - // TODO: this should read from an io.Reader arg - data, err := ioutil.ReadAll(os.Stdin) - if err != nil { - return err - } - - b := blocks.NewBlock(data) - log.Debugf("BlockPut key: '%q'", b.Key()) - - k, err := n.Blocks.AddBlock(b) - if err != nil { - return err - } - fmt.Fprintf(out, "added as '%s'\n", k) - - return nil -} diff --git a/core/commands2/object.go b/core/commands2/object.go deleted file mode 100644 index 24386a173..000000000 --- a/core/commands2/object.go +++ /dev/null @@ -1,135 +0,0 @@ -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.Debugf("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.Debugf("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.Debugf("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) -} From 24814a411f294f25c8c6447135adf32997f64d1d Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 31 Oct 2014 13:47:09 -0700 Subject: [PATCH 070/383] core/commands2: Set root subcommands in init to prevent initialization loops --- core/commands2/root.go | 104 +++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 51 deletions(-) diff --git a/core/commands2/root.go b/core/commands2/root.go index 59967c742..9f679f11d 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -50,65 +50,67 @@ Plumbing commands: Use "ipfs help " for more information about a command. `, - Subcommands: map[string]*cmds.Command{ - "cat": catCmd, - "ls": lsCmd, - "init": initCmd, +} - // test subcommands - // TODO: remove these when we don't need them anymore - "beep": &cmds.Command{ - Run: func(res cmds.Response, req cmds.Request) { - v := &TestOutput{"hello, world", 1337} - res.SetValue(v) - }, - Format: func(res cmds.Response) (string, error) { - v := res.Value().(*TestOutput) - s := fmt.Sprintf("Foo: %s\n", v.Foo) - s += fmt.Sprintf("Bar: %v\n", v.Bar) - return s, nil - }, - Type: &TestOutput{}, - }, - "boop": &cmds.Command{ - Run: func(res cmds.Response, req cmds.Request) { - v := strings.NewReader("hello, world") - res.SetValue(v) - }, - }, - "warp": &cmds.Command{ - Options: []cmds.Option{ - cmds.Option{[]string{"power", "p"}, cmds.Float}, - }, - Run: func(res cmds.Response, req cmds.Request) { - threshold := 1.21 +var rootSubcommands = map[string]*cmds.Command{ + "cat": catCmd, + "ls": lsCmd, + "commands": commandsCmd, + "init": initCmd, + "daemon": daemonCmd, - if power, found := req.Option("power"); found && power.(float64) >= threshold { - res.SetValue(struct { - Status string - Power float64 - }{"Flux capacitor activated!", power.(float64)}) + // test subcommands + // TODO: remove these when we don't need them anymore + "beep": &cmds.Command{ + Run: func(res cmds.Response, req cmds.Request) { + v := &TestOutput{"hello, world", 1337} + res.SetValue(v) + }, + Format: func(res cmds.Response) (string, error) { + v := res.Value().(*TestOutput) + s := fmt.Sprintf("Foo: %s\n", v.Foo) + s += fmt.Sprintf("Bar: %v\n", v.Bar) + return s, nil + }, + Type: &TestOutput{}, + }, + "boop": &cmds.Command{ + Run: func(res cmds.Response, req cmds.Request) { + v := strings.NewReader("hello, world") + res.SetValue(v) + }, + }, + "warp": &cmds.Command{ + Options: []cmds.Option{ + cmds.Option{[]string{"power", "p"}, cmds.Float}, + }, + Run: func(res cmds.Response, req cmds.Request) { + threshold := 1.21 - } else { - err := fmt.Errorf("Insufficient power (%v jiggawatts required)", threshold) - res.SetError(err, cmds.ErrClient) - } - }, + if power, found := req.Option("power"); found && power.(float64) >= threshold { + res.SetValue(struct { + Status string + Power float64 + }{"Flux capacitor activated!", power.(float64)}) + + } else { + err := fmt.Errorf("Insufficient power (%v jiggawatts required)", threshold) + res.SetError(err, cmds.ErrClient) + } }, - "args": &cmds.Command{ - Run: func(res cmds.Response, req cmds.Request) { - res.SetValue(req.Arguments()) - }, + }, + "args": &cmds.Command{ + Run: func(res cmds.Response, req cmds.Request) { + res.SetValue(req.Arguments()) }, - "echo": &cmds.Command{ - Run: func(res cmds.Response, req cmds.Request) { - res.SetValue(req.Stream()) - }, + }, + "echo": &cmds.Command{ + Run: func(res cmds.Response, req cmds.Request) { + res.SetValue(req.Stream()) }, }, } func init() { - Root.Subcommands["daemon"] = daemonCmd - Root.Subcommands["commands"] = commandsCmd + Root.Subcommands = rootSubcommands } From cac8844b8d88e1ab6eca68501546a1b309a6f0f6 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 31 Oct 2014 13:55:52 -0700 Subject: [PATCH 071/383] cmd/ipfs: Reverted back to secondary CLI root command --- {core/commands2 => cmd/ipfs2}/daemon.go | 6 +++--- {core/commands2 => cmd/ipfs2}/init.go | 3 +-- cmd/ipfs2/ipfs.go | 15 +++++++++++++++ cmd/ipfs2/main.go | 10 +++++----- core/commands2/root.go | 2 -- 5 files changed, 24 insertions(+), 12 deletions(-) rename {core/commands2 => cmd/ipfs2}/daemon.go (92%) rename {core/commands2 => cmd/ipfs2}/init.go (99%) create mode 100644 cmd/ipfs2/ipfs.go diff --git a/core/commands2/daemon.go b/cmd/ipfs2/daemon.go similarity index 92% rename from core/commands2/daemon.go rename to cmd/ipfs2/daemon.go index 9260b8727..d7469f01a 100644 --- a/core/commands2/daemon.go +++ b/cmd/ipfs2/daemon.go @@ -1,4 +1,4 @@ -package commands +package main import ( "fmt" @@ -10,11 +10,11 @@ import ( cmds "github.com/jbenet/go-ipfs/commands" cmdsHttp "github.com/jbenet/go-ipfs/commands/http" "github.com/jbenet/go-ipfs/core" + commands "github.com/jbenet/go-ipfs/core/commands2" daemon "github.com/jbenet/go-ipfs/daemon2" ) var daemonCmd = &cmds.Command{ - Private: true, Options: []cmds.Option{}, Help: "TODO", Subcommands: map[string]*cmds.Command{}, @@ -50,7 +50,7 @@ func daemonFunc(res cmds.Response, req cmds.Request) { return } - handler := cmdsHttp.Handler{*ctx, Root} + handler := cmdsHttp.Handler{*ctx, commands.Root} http.Handle(cmdsHttp.ApiPath+"/", handler) fmt.Printf("API server listening on '%s'\n", host) diff --git a/core/commands2/init.go b/cmd/ipfs2/init.go similarity index 99% rename from core/commands2/init.go rename to cmd/ipfs2/init.go index 25b535009..4111481c7 100644 --- a/core/commands2/init.go +++ b/cmd/ipfs2/init.go @@ -1,4 +1,4 @@ -package commands +package main import ( "encoding/base64" @@ -15,7 +15,6 @@ import ( ) var initCmd = &cmds.Command{ - Private: true, Options: []cmds.Option{ cmds.Option{[]string{"bits", "b"}, cmds.Int}, cmds.Option{[]string{"passphrase", "p"}, cmds.String}, diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go new file mode 100644 index 000000000..bb328085a --- /dev/null +++ b/cmd/ipfs2/ipfs.go @@ -0,0 +1,15 @@ +package main + +import ( + cmds "github.com/jbenet/go-ipfs/commands" + commands "github.com/jbenet/go-ipfs/core/commands2" +) + +var Root = &cmds.Command{ + Options: commands.Root.Options, + Help: commands.Root.Help, + Subcommands: map[string]*cmds.Command{ + "daemon": daemonCmd, + "init": initCmd, + }, +} diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 67a19a601..f4a923ff7 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -22,13 +22,13 @@ var log = u.Logger("cmd/ipfs") func main() { args := os.Args[1:] - req, err := cmdsCli.Parse(args, commands.Root) + req, root, err := cmdsCli.Parse(args, Root, commands.Root) if err != nil { fmt.Println(err) os.Exit(1) } - options, err := getOptions(req, commands.Root) + options, err := getOptions(req, root) if err != nil { fmt.Println(err) os.Exit(1) @@ -80,8 +80,8 @@ func main() { } var res cmds.Response - if req.Command().Private { - res = commands.Root.Call(req) + if root == Root { + res = root.Call(req) } else { local, found := options.Option("local") @@ -101,7 +101,7 @@ func main() { } ctx.Node = node - res = commands.Root.Call(req) + res = root.Call(req) } } diff --git a/core/commands2/root.go b/core/commands2/root.go index 9f679f11d..73823e0db 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -56,8 +56,6 @@ var rootSubcommands = map[string]*cmds.Command{ "cat": catCmd, "ls": lsCmd, "commands": commandsCmd, - "init": initCmd, - "daemon": daemonCmd, // test subcommands // TODO: remove these when we don't need them anymore From d8afd9a9bb11cb1e60e18d740b38613543dd5308 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 31 Oct 2014 14:11:02 -0700 Subject: [PATCH 072/383] cmd/ipfs: Show usage text if input fails to parse --- cmd/ipfs2/main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index f4a923ff7..630e2e37a 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -25,6 +25,7 @@ func main() { req, root, err := cmdsCli.Parse(args, Root, commands.Root) if err != nil { fmt.Println(err) + fmt.Println(Root.Help) os.Exit(1) } From c70ca4dd51f10c059d03ac0708c6610324263d52 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 31 Oct 2014 16:08:06 -0700 Subject: [PATCH 073/383] core/commands2: Added 'publish' command --- core/commands2/publish.go | 69 +++++++++++++++++++++++++++++++++++++++ core/commands2/root.go | 1 + 2 files changed, 70 insertions(+) create mode 100644 core/commands2/publish.go diff --git a/core/commands2/publish.go b/core/commands2/publish.go new file mode 100644 index 000000000..feb8dfcf9 --- /dev/null +++ b/core/commands2/publish.go @@ -0,0 +1,69 @@ +package commands + +import ( + "errors" + "fmt" + + cmds "github.com/jbenet/go-ipfs/commands" + nsys "github.com/jbenet/go-ipfs/namesys" + u "github.com/jbenet/go-ipfs/util" +) + +type PublishOutput struct { + Name, Value string +} + +var publishCmd = &cmds.Command{ + Help: "TODO", + Run: func(res cmds.Response, req cmds.Request) { + n := req.Context().Node + args := req.Arguments() + + if n.Identity == nil { + res.SetError(errors.New("Identity not loaded!"), cmds.ErrNormal) + return + } + + // name := "" + ref := "" + + switch len(args) { + case 2: + // name = args[0] + ref = args[1] + res.SetError(errors.New("keychains not yet implemented"), cmds.ErrNormal) + return + case 1: + // name = n.Identity.ID.String() + ref = args[0] + + default: + res.SetError(fmt.Errorf("Publish expects 1 or 2 args; got %d.", len(args)), cmds.ErrClient) + } + // later, n.Keychain.Get(name).PrivKey + k := n.Identity.PrivKey() + + pub := nsys.NewRoutingPublisher(n.Routing) + err := pub.Publish(k, ref) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + hash, err := k.GetPublic().Hash() + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetValue(&PublishOutput{ + Name: u.Key(hash).String(), + Value: ref, + }) + }, + Format: func(res cmds.Response) (string, error) { + v := res.Value().(*PublishOutput) + return fmt.Sprintf("Published name %s to %s\n", v.Name, v.Value), nil + }, + Type: &PublishOutput{}, +} diff --git a/core/commands2/root.go b/core/commands2/root.go index 73823e0db..5f0c3f2ae 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -56,6 +56,7 @@ var rootSubcommands = map[string]*cmds.Command{ "cat": catCmd, "ls": lsCmd, "commands": commandsCmd, + "publish": publishCmd, // test subcommands // TODO: remove these when we don't need them anymore From 2915007c4fad7a8c185ca30710bf7235b9f9c5eb Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sun, 2 Nov 2014 16:55:13 -0800 Subject: [PATCH 074/383] commands: Changed Request arguments to a []interface{} --- core/commands2/cat.go | 3 ++- core/commands2/ls.go | 3 ++- core/commands2/publish.go | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/core/commands2/cat.go b/core/commands2/cat.go index 363050cd6..d1619eabe 100644 --- a/core/commands2/cat.go +++ b/core/commands2/cat.go @@ -13,7 +13,8 @@ var catCmd = &cmds.Command{ node := req.Context().Node readers := make([]io.Reader, 0, len(req.Arguments())) - for _, path := range req.Arguments() { + for _, arg := range req.Arguments() { + path := arg.(string) dagnode, err := node.Resolver.ResolvePath(path) if err != nil { res.SetError(err, cmds.ErrNormal) diff --git a/core/commands2/ls.go b/core/commands2/ls.go index ae68c91b0..2ffd8be65 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -26,7 +26,8 @@ var lsCmd = &cmds.Command{ node := req.Context().Node output := make([]Object, len(req.Arguments())) - for i, path := range req.Arguments() { + for i, arg := range req.Arguments() { + path := arg.(string) dagnode, err := node.Resolver.ResolvePath(path) if err != nil { res.SetError(err, cmds.ErrNormal) diff --git a/core/commands2/publish.go b/core/commands2/publish.go index feb8dfcf9..4afee9ea7 100644 --- a/core/commands2/publish.go +++ b/core/commands2/publish.go @@ -30,12 +30,12 @@ var publishCmd = &cmds.Command{ switch len(args) { case 2: // name = args[0] - ref = args[1] + ref = args[1].(string) res.SetError(errors.New("keychains not yet implemented"), cmds.ErrNormal) return case 1: // name = n.Identity.ID.String() - ref = args[0] + ref = args[0].(string) default: res.SetError(fmt.Errorf("Publish expects 1 or 2 args; got %d.", len(args)), cmds.ErrClient) From 26c2164c35fbf58165ef3bb162466a5705a78f86 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sun, 2 Nov 2014 16:55:55 -0800 Subject: [PATCH 075/383] core/commands2: Removed 'echo' test command --- core/commands2/root.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/core/commands2/root.go b/core/commands2/root.go index 5f0c3f2ae..1f970f281 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -103,11 +103,6 @@ var rootSubcommands = map[string]*cmds.Command{ res.SetValue(req.Arguments()) }, }, - "echo": &cmds.Command{ - Run: func(res cmds.Response, req cmds.Request) { - res.SetValue(req.Stream()) - }, - }, } func init() { From 6bab1f47fb6c81de59f840ff916dc625da2f5553 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sun, 2 Nov 2014 16:59:52 -0800 Subject: [PATCH 076/383] commands: Removed inpout stream from Request --- cmd/ipfs2/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 630e2e37a..b59c28f52 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -124,7 +124,7 @@ func main() { } func getOptions(req cmds.Request, root *cmds.Command) (cmds.Request, error) { - tempReq := cmds.NewRequest(req.Path(), req.Options(), nil, nil, nil) + tempReq := cmds.NewRequest(req.Path(), req.Options(), nil, nil) options, err := root.GetOptions(tempReq.Path()) if err != nil { From b420ba1e49f1207597a747087a7a28a354e2cf2d Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sun, 2 Nov 2014 18:49:35 -0800 Subject: [PATCH 077/383] core/commands2: Added preliminary 'add' command --- core/commands2/add.go | 58 ++++++++++++++++++++++++++++++++++++++++++ core/commands2/root.go | 1 + 2 files changed, 59 insertions(+) create mode 100644 core/commands2/add.go diff --git a/core/commands2/add.go b/core/commands2/add.go new file mode 100644 index 000000000..b32a397b4 --- /dev/null +++ b/core/commands2/add.go @@ -0,0 +1,58 @@ +package commands + +import ( + "fmt" + "io" + + cmds "github.com/jbenet/go-ipfs/commands" + "github.com/jbenet/go-ipfs/core" + "github.com/jbenet/go-ipfs/importer" + dag "github.com/jbenet/go-ipfs/merkledag" +) + +// Error indicating the max depth has been exceded. +var ErrDepthLimitExceeded = fmt.Errorf("depth limit exceeded") + +var addCmd = &cmds.Command{ + Options: []cmds.Option{ + cmds.Option{[]string{"recursive", "r"}, cmds.Bool}, + }, + Arguments: []cmds.Argument{ + cmds.Argument{"file", cmds.ArgFile, false, true}, + }, + Help: "TODO", + Run: func(res cmds.Response, req cmds.Request) { + n := req.Context().Node + + // if recursive, set depth to reflect so + //opt, found := req.Option("r") + //if r, _ := opt.(bool); found && r { + //} + + // add every path in args + for _, arg := range req.Arguments() { + // Add the file + node, err := add(n, arg.(io.Reader)) + if err != nil { + res.SetError(err, cmds.ErrNormal) + } + fmt.Println(node.Key()) + } + }, +} + +func add(n *core.IpfsNode, in io.Reader) (*dag.Node, error) { + node, err := importer.NewDagFromReader(in) + if err != nil { + return nil, err + } + + // add the file to the graph + local storage + err = n.DAG.AddRecursive(node) + if err != nil { + return nil, err + } + + // ensure we keep it + return node, n.Pinning.Pin(node, true) +} diff --git a/core/commands2/root.go b/core/commands2/root.go index 1f970f281..df5ad9e3f 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -57,6 +57,7 @@ var rootSubcommands = map[string]*cmds.Command{ "ls": lsCmd, "commands": commandsCmd, "publish": publishCmd, + "add": addCmd, // test subcommands // TODO: remove these when we don't need them anymore From 77fea8928aa731c7d2dac391ab531008c90a15b9 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sun, 2 Nov 2014 18:50:06 -0800 Subject: [PATCH 078/383] Added argument definition to 'cat' command --- core/commands2/cat.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/commands2/cat.go b/core/commands2/cat.go index d1619eabe..9e7b7e547 100644 --- a/core/commands2/cat.go +++ b/core/commands2/cat.go @@ -8,6 +8,9 @@ import ( ) var catCmd = &cmds.Command{ + Arguments: []cmds.Argument{ + cmds.Argument{"object", cmds.ArgString, false, true}, + }, Help: "TODO", Run: func(res cmds.Response, req cmds.Request) { node := req.Context().Node From 38318a6c28f637271848cc83e07b8fc29aeb72d9 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sun, 2 Nov 2014 19:15:19 -0800 Subject: [PATCH 079/383] core/commands2: Added output for 'add' command --- core/commands2/add.go | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/core/commands2/add.go b/core/commands2/add.go index b32a397b4..9c50db1f4 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -13,6 +13,10 @@ import ( // Error indicating the max depth has been exceded. var ErrDepthLimitExceeded = fmt.Errorf("depth limit exceeded") +type AddOutput struct { + Added []Object +} + var addCmd = &cmds.Command{ Options: []cmds.Option{ cmds.Option{[]string{"recursive", "r"}, cmds.Bool}, @@ -29,16 +33,41 @@ var addCmd = &cmds.Command{ //if r, _ := opt.(bool); found && r { //} + added := make([]Object, len(req.Arguments())) + // add every path in args - for _, arg := range req.Arguments() { + for i, arg := range req.Arguments() { // Add the file node, err := add(n, arg.(io.Reader)) if err != nil { res.SetError(err, cmds.ErrNormal) + return } - fmt.Println(node.Key()) + + k, err := node.Key() + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + added[i] = Object{k.String(), nil} } + + res.SetValue(&AddOutput{added}) }, + Format: func(res cmds.Response) (string, error) { + v := res.Value().(*AddOutput).Added + if len(v) == 1 { + return fmt.Sprintf("Added object: %s\n", v[0].Hash), nil + } + + s := fmt.Sprintf("Added %v objects:\n", len(v)) + for _, obj := range v { + s += fmt.Sprintf("- %s\n", obj.Hash) + } + return s, nil + }, + Type: &AddOutput{}, } func add(n *core.IpfsNode, in io.Reader) (*dag.Node, error) { From 92e308f32b3cfff4db357e25213a4018671e33ab Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sun, 2 Nov 2014 23:35:55 -0800 Subject: [PATCH 080/383] core/commands2: Added logging --- core/commands2/root.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/commands2/root.go b/core/commands2/root.go index df5ad9e3f..45c51dcca 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -5,8 +5,11 @@ import ( "strings" cmds "github.com/jbenet/go-ipfs/commands" + u "github.com/jbenet/go-ipfs/util" ) +var log = u.Logger("core/commands") + type TestOutput struct { Foo string Bar int @@ -64,6 +67,7 @@ var rootSubcommands = map[string]*cmds.Command{ "beep": &cmds.Command{ Run: func(res cmds.Response, req cmds.Request) { v := &TestOutput{"hello, world", 1337} + log.Info("beep") res.SetValue(v) }, Format: func(res cmds.Response) (string, error) { @@ -108,4 +112,5 @@ var rootSubcommands = map[string]*cmds.Command{ func init() { Root.Subcommands = rootSubcommands + u.SetLogLevel("core/commands", "info") } From 3c4e7bf9a10f616879ff25b9984e3fa94f73c690 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 3 Nov 2014 00:07:53 -0800 Subject: [PATCH 081/383] core/commands2: Added a simple 'message' output/formatter --- core/commands2/root.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/commands2/root.go b/core/commands2/root.go index 45c51dcca..df8fd366c 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -114,3 +114,11 @@ func init() { Root.Subcommands = rootSubcommands u.SetLogLevel("core/commands", "info") } + +type MessageOutput struct { + Message string +} + +func MessageFormatter(res cmds.Response) (string, error) { + return res.Value().(*MessageOutput).Message, nil +} From ff987cffae0a1abce373e8269b4e5d8a63f1cf9c Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 3 Nov 2014 00:08:19 -0800 Subject: [PATCH 082/383] core/commands2: Added 'log' command --- core/commands2/log.go | 28 ++++++++++++++++++++++++++++ core/commands2/root.go | 1 + 2 files changed, 29 insertions(+) create mode 100644 core/commands2/log.go diff --git a/core/commands2/log.go b/core/commands2/log.go new file mode 100644 index 000000000..f5e840988 --- /dev/null +++ b/core/commands2/log.go @@ -0,0 +1,28 @@ +package commands + +import ( + "fmt" + + cmds "github.com/jbenet/go-ipfs/commands" + u "github.com/jbenet/go-ipfs/util" +) + +var logCmd = &cmds.Command{ + Arguments: []cmds.Argument{ + cmds.Argument{"subsystem", cmds.ArgString, true, false}, + cmds.Argument{"level", cmds.ArgString, true, false}, + }, + Help: "TODO", + Run: func(res cmds.Response, req cmds.Request) { + args := req.Arguments() + if err := u.SetLogLevel(args[0].(string), args[1].(string)); err != nil { + res.SetError(err, cmds.ErrClient) + return + } + + s := fmt.Sprintf("Changed log level of '%s' to '%s'", args[0], args[1]) + res.SetValue(&MessageOutput{s}) + }, + Format: MessageFormatter, + Type: &MessageOutput{}, +} diff --git a/core/commands2/root.go b/core/commands2/root.go index df8fd366c..de89ad5c1 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -61,6 +61,7 @@ var rootSubcommands = map[string]*cmds.Command{ "commands": commandsCmd, "publish": publishCmd, "add": addCmd, + "log": logCmd, // test subcommands // TODO: remove these when we don't need them anymore From f1209d091294bca9f3868cce6997cc1a4c1f6b12 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 3 Nov 2014 14:37:22 -0800 Subject: [PATCH 083/383] core/commands2: Added argument definition for 'ls' --- core/commands2/ls.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/commands2/ls.go b/core/commands2/ls.go index 2ffd8be65..e3cad9e2b 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -21,6 +21,9 @@ type LsOutput struct { } var lsCmd = &cmds.Command{ + Arguments: []cmds.Argument{ + cmds.Argument{"object", cmds.ArgString, false, true}, + }, Help: "TODO", Run: func(res cmds.Response, req cmds.Request) { node := req.Context().Node From 15cd24a6d19dceaf911aa128c706da6c6b691158 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 3 Nov 2014 15:47:00 -0800 Subject: [PATCH 084/383] commands: Renamed Response#Value to Response#Output --- core/commands2/add.go | 4 ++-- core/commands2/cat.go | 2 +- core/commands2/commands.go | 4 ++-- core/commands2/log.go | 2 +- core/commands2/ls.go | 4 ++-- core/commands2/publish.go | 4 ++-- core/commands2/root.go | 12 ++++++------ 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/core/commands2/add.go b/core/commands2/add.go index 9c50db1f4..056c904d2 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -53,10 +53,10 @@ var addCmd = &cmds.Command{ added[i] = Object{k.String(), nil} } - res.SetValue(&AddOutput{added}) + res.SetOutput(&AddOutput{added}) }, Format: func(res cmds.Response) (string, error) { - v := res.Value().(*AddOutput).Added + v := res.Output().(*AddOutput).Added if len(v) == 1 { return fmt.Sprintf("Added object: %s\n", v[0].Hash), nil } diff --git a/core/commands2/cat.go b/core/commands2/cat.go index 9e7b7e547..c190ddd6d 100644 --- a/core/commands2/cat.go +++ b/core/commands2/cat.go @@ -34,6 +34,6 @@ var catCmd = &cmds.Command{ } reader := io.MultiReader(readers...) - res.SetValue(reader) + res.SetOutput(reader) }, } diff --git a/core/commands2/commands.go b/core/commands2/commands.go index 999fac5c7..365ebd04d 100644 --- a/core/commands2/commands.go +++ b/core/commands2/commands.go @@ -16,10 +16,10 @@ var commandsCmd = &cmds.Command{ Help: "TODO", Run: func(res cmds.Response, req cmds.Request) { root := outputCommand("ipfs", Root) - res.SetValue(&root) + res.SetOutput(&root) }, Format: func(res cmds.Response) (string, error) { - v := res.Value().(*Command) + v := res.Output().(*Command) return formatCommand(v, 0), nil }, Type: &Command{}, diff --git a/core/commands2/log.go b/core/commands2/log.go index f5e840988..d5a042b9f 100644 --- a/core/commands2/log.go +++ b/core/commands2/log.go @@ -21,7 +21,7 @@ var logCmd = &cmds.Command{ } s := fmt.Sprintf("Changed log level of '%s' to '%s'", args[0], args[1]) - res.SetValue(&MessageOutput{s}) + res.SetOutput(&MessageOutput{s}) }, Format: MessageFormatter, Type: &MessageOutput{}, diff --git a/core/commands2/ls.go b/core/commands2/ls.go index e3cad9e2b..ec8840c87 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -50,11 +50,11 @@ var lsCmd = &cmds.Command{ } } - res.SetValue(&LsOutput{output}) + res.SetOutput(&LsOutput{output}) }, Format: func(res cmds.Response) (string, error) { s := "" - output := res.Value().(*LsOutput).Objects + output := res.Output().(*LsOutput).Objects for _, object := range output { if len(output) > 1 { diff --git a/core/commands2/publish.go b/core/commands2/publish.go index 4afee9ea7..06f43dab2 100644 --- a/core/commands2/publish.go +++ b/core/commands2/publish.go @@ -56,13 +56,13 @@ var publishCmd = &cmds.Command{ return } - res.SetValue(&PublishOutput{ + res.SetOutput(&PublishOutput{ Name: u.Key(hash).String(), Value: ref, }) }, Format: func(res cmds.Response) (string, error) { - v := res.Value().(*PublishOutput) + v := res.Output().(*PublishOutput) return fmt.Sprintf("Published name %s to %s\n", v.Name, v.Value), nil }, Type: &PublishOutput{}, diff --git a/core/commands2/root.go b/core/commands2/root.go index de89ad5c1..1a971aae9 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -69,10 +69,10 @@ var rootSubcommands = map[string]*cmds.Command{ Run: func(res cmds.Response, req cmds.Request) { v := &TestOutput{"hello, world", 1337} log.Info("beep") - res.SetValue(v) + res.SetOutput(v) }, Format: func(res cmds.Response) (string, error) { - v := res.Value().(*TestOutput) + v := res.Output().(*TestOutput) s := fmt.Sprintf("Foo: %s\n", v.Foo) s += fmt.Sprintf("Bar: %v\n", v.Bar) return s, nil @@ -82,7 +82,7 @@ var rootSubcommands = map[string]*cmds.Command{ "boop": &cmds.Command{ Run: func(res cmds.Response, req cmds.Request) { v := strings.NewReader("hello, world") - res.SetValue(v) + res.SetOutput(v) }, }, "warp": &cmds.Command{ @@ -93,7 +93,7 @@ var rootSubcommands = map[string]*cmds.Command{ threshold := 1.21 if power, found := req.Option("power"); found && power.(float64) >= threshold { - res.SetValue(struct { + res.SetOutput(struct { Status string Power float64 }{"Flux capacitor activated!", power.(float64)}) @@ -106,7 +106,7 @@ var rootSubcommands = map[string]*cmds.Command{ }, "args": &cmds.Command{ Run: func(res cmds.Response, req cmds.Request) { - res.SetValue(req.Arguments()) + res.SetOutput(req.Arguments()) }, }, } @@ -121,5 +121,5 @@ type MessageOutput struct { } func MessageFormatter(res cmds.Response) (string, error) { - return res.Value().(*MessageOutput).Message, nil + return res.Output().(*MessageOutput).Message, nil } From 39a60e7f1de19c71a03af09abf69048a71e76a4c Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 3 Nov 2014 16:30:39 -0800 Subject: [PATCH 085/383] commands: Return a reader in a Response#Reader method, instead of making Response implementing io.Reader --- cmd/ipfs2/main.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index b59c28f52..36dfa49cd 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -117,10 +117,13 @@ func main() { os.Exit(1) } - _, err = io.Copy(os.Stdout, res) + out, err := res.Reader() if err != nil { fmt.Println(err.Error()) + return } + + io.Copy(os.Stdout, out) } func getOptions(req cmds.Request, root *cmds.Command) (cmds.Request, error) { From 7d4d55a84d03f8449d3c984d30b99bfa3f7133d8 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 3 Nov 2014 20:08:08 -0800 Subject: [PATCH 086/383] commands/http: Unexported Handler fields and created constructor --- cmd/ipfs2/daemon.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ipfs2/daemon.go b/cmd/ipfs2/daemon.go index d7469f01a..e3f3b0043 100644 --- a/cmd/ipfs2/daemon.go +++ b/cmd/ipfs2/daemon.go @@ -50,7 +50,7 @@ func daemonFunc(res cmds.Response, req cmds.Request) { return } - handler := cmdsHttp.Handler{*ctx, commands.Root} + handler := cmdsHttp.NewHandler(*ctx, commands.Root) http.Handle(cmdsHttp.ApiPath+"/", handler) fmt.Printf("API server listening on '%s'\n", host) From 7c1e45786a1fc36fc8bc2e595310c4ee4e62fec1 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 3 Nov 2014 22:09:19 -0800 Subject: [PATCH 087/383] cmd/ipfs2: Stricter option type coercion in main --- cmd/ipfs2/main.go | 57 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 36dfa49cd..aa057dfda 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -35,24 +35,34 @@ func main() { os.Exit(1) } - if help, found := options.Option("help"); found && help.(bool) { - fmt.Println(req.Command().Help) - os.Exit(0) + if help, found := options.Option("help"); found { + if helpBool, ok := help.(bool); helpBool && ok { + fmt.Println(req.Command().Help) + os.Exit(0) + } else if !ok { + fmt.Println("error: expected 'help' option to be a bool") + os.Exit(1) + } } - if debug, found := options.Option("debug"); found && debug.(bool) { - u.Debug = true + if debug, found := options.Option("debug"); found { + if debugBool, ok := debug.(bool); debugBool && ok { + u.Debug = true - // if debugging, setup profiling. - if u.Debug { - ofi, err := os.Create("cpu.prof") - if err != nil { - fmt.Println(err) - return + // if debugging, setup profiling. + if u.Debug { + ofi, err := os.Create("cpu.prof") + if err != nil { + fmt.Println(err) + return + } + pprof.StartCPUProfile(ofi) + defer ofi.Close() + defer pprof.StopCPUProfile() } - pprof.StartCPUProfile(ofi) - defer ofi.Close() - defer pprof.StopCPUProfile() + } else if !ok { + fmt.Println("error: expected 'debug' option to be a bool") + os.Exit(1) } } @@ -85,9 +95,18 @@ func main() { res = root.Call(req) } else { - local, found := options.Option("local") + var found bool + localBool := false + if local, found := options.Option("local"); found { + var ok bool + localBool, ok = local.(bool) + if !ok { + fmt.Println("error: expected 'local' option to be a bool") + os.Exit(1) + } + } - if (!found || !local.(bool)) && daemon.Locked(configPath) { + if (!found || !localBool) && daemon.Locked(configPath) { res, err = cmdsHttp.Send(req) if err != nil { fmt.Println(err) @@ -144,7 +163,11 @@ func getOptions(req cmds.Request, root *cmds.Command) (cmds.Request, error) { func getConfigRoot(req cmds.Request) (string, error) { if opt, found := req.Option("config"); found { - return opt.(string), nil + if optStr, ok := opt.(string); ok { + return optStr, nil + } else { + return "", fmt.Errorf("Expected 'config' option to be a string") + } } configPath, err := config.PathRoot() From 3dd7a9a5c8d261e19565c925e20178f0ddffefaa Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 3 Nov 2014 22:34:24 -0800 Subject: [PATCH 088/383] cmd/ipfs2: Broke up main into subfunctions --- cmd/ipfs2/main.go | 90 +++++++++++++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 30 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index aa057dfda..5d30a265a 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -21,7 +21,13 @@ var log = u.Logger("cmd/ipfs") func main() { args := os.Args[1:] + req, root := createRequest(args) + handleOptions(req, root) + res := callCommand(req, root) + outputResponse(res) +} +func createRequest(args []string) (cmds.Request, *cmds.Command) { req, root, err := cmdsCli.Parse(args, Root, commands.Root) if err != nil { fmt.Println(err) @@ -35,6 +41,40 @@ func main() { os.Exit(1) } + configPath, err := getConfigRoot(options) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + conf, err := getConfig(configPath) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + ctx := req.Context() + ctx.ConfigRoot = configPath + ctx.Config = conf + + if _, found := options.Option("encoding"); !found { + if req.Command().Format != nil { + req.SetOption("encoding", cmds.Text) + } else { + req.SetOption("encoding", cmds.JSON) + } + } + + return req, root +} + +func handleOptions(req cmds.Request, root *cmds.Command) { + options, err := getOptions(req, root) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + if help, found := options.Option("help"); found { if helpBool, ok := help.(bool); helpBool && ok { fmt.Println(req.Command().Help) @@ -65,39 +105,25 @@ func main() { os.Exit(1) } } +} - configPath, err := getConfigRoot(options) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - conf, err := getConfig(configPath) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - ctx := req.Context() - ctx.ConfigRoot = configPath - ctx.Config = conf - - if _, found := options.Option("encoding"); !found { - if req.Command().Format != nil { - req.SetOption("encoding", cmds.Text) - } else { - req.SetOption("encoding", cmds.JSON) - } - } - +func callCommand(req cmds.Request, root *cmds.Command) cmds.Response { var res cmds.Response + if root == Root { res = root.Call(req) } else { + options, err := getOptions(req, root) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + var found bool + var local interface{} localBool := false - if local, found := options.Option("local"); found { + if local, found = options.Option("local"); found { var ok bool localBool, ok = local.(bool) if !ok { @@ -106,7 +132,7 @@ func main() { } } - if (!found || !localBool) && daemon.Locked(configPath) { + if (!found || !localBool) && daemon.Locked(req.Context().ConfigRoot) { res, err = cmdsHttp.Send(req) if err != nil { fmt.Println(err) @@ -114,23 +140,27 @@ func main() { } } else { - node, err := core.NewIpfsNode(conf, false) + node, err := core.NewIpfsNode(req.Context().Config, false) if err != nil { fmt.Println(err) os.Exit(1) } - ctx.Node = node + req.Context().Node = node res = root.Call(req) } } + return res +} + +func outputResponse(res cmds.Response) { if res.Error() != nil { fmt.Println(res.Error().Error()) - if req.Command().Help != "" && res.Error().Code == cmds.ErrClient { + if res.Request().Command().Help != "" && res.Error().Code == cmds.ErrClient { // TODO: convert from markdown to ANSI terminal format? - fmt.Println(req.Command().Help) + fmt.Println(res.Request().Command().Help) } os.Exit(1) From 0afd3391a8e630bcb4b0cb80fc157053dbfa0779 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 3 Nov 2014 23:02:50 -0800 Subject: [PATCH 089/383] commands: Replaced 'Formatter' with 'Marshaller' --- core/commands2/add.go | 7 ++++--- core/commands2/commands.go | 5 +++-- core/commands2/log.go | 2 +- core/commands2/ls.go | 4 ++-- core/commands2/publish.go | 5 +++-- core/commands2/root.go | 8 ++++---- 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/core/commands2/add.go b/core/commands2/add.go index 056c904d2..aacb5c9e1 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -55,17 +55,18 @@ var addCmd = &cmds.Command{ res.SetOutput(&AddOutput{added}) }, - Format: func(res cmds.Response) (string, error) { + Format: func(res cmds.Response) ([]byte, error) { v := res.Output().(*AddOutput).Added if len(v) == 1 { - return fmt.Sprintf("Added object: %s\n", v[0].Hash), nil + s := fmt.Sprintf("Added object: %s\n", v[0].Hash) + return []byte(s), nil } s := fmt.Sprintf("Added %v objects:\n", len(v)) for _, obj := range v { s += fmt.Sprintf("- %s\n", obj.Hash) } - return s, nil + return []byte(s), nil }, Type: &AddOutput{}, } diff --git a/core/commands2/commands.go b/core/commands2/commands.go index 365ebd04d..bc40faabe 100644 --- a/core/commands2/commands.go +++ b/core/commands2/commands.go @@ -18,9 +18,10 @@ var commandsCmd = &cmds.Command{ root := outputCommand("ipfs", Root) res.SetOutput(&root) }, - Format: func(res cmds.Response) (string, error) { + Format: func(res cmds.Response) ([]byte, error) { v := res.Output().(*Command) - return formatCommand(v, 0), nil + s := formatCommand(v, 0) + return []byte(s), nil }, Type: &Command{}, } diff --git a/core/commands2/log.go b/core/commands2/log.go index d5a042b9f..534c8a07f 100644 --- a/core/commands2/log.go +++ b/core/commands2/log.go @@ -23,6 +23,6 @@ var logCmd = &cmds.Command{ s := fmt.Sprintf("Changed log level of '%s' to '%s'", args[0], args[1]) res.SetOutput(&MessageOutput{s}) }, - Format: MessageFormatter, + Format: MessageMarshaller, Type: &MessageOutput{}, } diff --git a/core/commands2/ls.go b/core/commands2/ls.go index ec8840c87..a100f6884 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -52,7 +52,7 @@ var lsCmd = &cmds.Command{ res.SetOutput(&LsOutput{output}) }, - Format: func(res cmds.Response) (string, error) { + Format: func(res cmds.Response) ([]byte, error) { s := "" output := res.Output().(*LsOutput).Objects @@ -70,7 +70,7 @@ var lsCmd = &cmds.Command{ } } - return s, nil + return []byte(s), nil }, Type: &LsOutput{}, } diff --git a/core/commands2/publish.go b/core/commands2/publish.go index 06f43dab2..08c75c0c1 100644 --- a/core/commands2/publish.go +++ b/core/commands2/publish.go @@ -61,9 +61,10 @@ var publishCmd = &cmds.Command{ Value: ref, }) }, - Format: func(res cmds.Response) (string, error) { + Format: func(res cmds.Response) ([]byte, error) { v := res.Output().(*PublishOutput) - return fmt.Sprintf("Published name %s to %s\n", v.Name, v.Value), nil + s := fmt.Sprintf("Published name %s to %s\n", v.Name, v.Value) + return []byte(s), nil }, Type: &PublishOutput{}, } diff --git a/core/commands2/root.go b/core/commands2/root.go index 1a971aae9..37df2c899 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -71,11 +71,11 @@ var rootSubcommands = map[string]*cmds.Command{ log.Info("beep") res.SetOutput(v) }, - Format: func(res cmds.Response) (string, error) { + Format: func(res cmds.Response) ([]byte, error) { v := res.Output().(*TestOutput) s := fmt.Sprintf("Foo: %s\n", v.Foo) s += fmt.Sprintf("Bar: %v\n", v.Bar) - return s, nil + return []byte(s), nil }, Type: &TestOutput{}, }, @@ -120,6 +120,6 @@ type MessageOutput struct { Message string } -func MessageFormatter(res cmds.Response) (string, error) { - return res.Output().(*MessageOutput).Message, nil +func MessageMarshaller(res cmds.Response) ([]byte, error) { + return []byte(res.Output().(*MessageOutput).Message), nil } From f95476c19aa6bcfe240c1c3bf6fc6fec3a54135b Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 3 Nov 2014 23:17:39 -0800 Subject: [PATCH 090/383] commands: Allow overriding marshaller for any encoding type --- cmd/ipfs2/main.go | 2 +- core/commands2/add.go | 24 +++++++++++++----------- core/commands2/commands.go | 10 ++++++---- core/commands2/log.go | 6 ++++-- core/commands2/ls.go | 34 ++++++++++++++++++---------------- core/commands2/publish.go | 10 ++++++---- core/commands2/root.go | 14 ++++++++------ 7 files changed, 56 insertions(+), 44 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 5d30a265a..64e6a51b6 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -58,7 +58,7 @@ func createRequest(args []string) (cmds.Request, *cmds.Command) { ctx.Config = conf if _, found := options.Option("encoding"); !found { - if req.Command().Format != nil { + if req.Command().Marshallers != nil && req.Command().Marshallers[cmds.Text] != nil { req.SetOption("encoding", cmds.Text) } else { req.SetOption("encoding", cmds.JSON) diff --git a/core/commands2/add.go b/core/commands2/add.go index aacb5c9e1..3dbce4430 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -55,18 +55,20 @@ var addCmd = &cmds.Command{ res.SetOutput(&AddOutput{added}) }, - Format: func(res cmds.Response) ([]byte, error) { - v := res.Output().(*AddOutput).Added - if len(v) == 1 { - s := fmt.Sprintf("Added object: %s\n", v[0].Hash) - return []byte(s), nil - } + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + v := res.Output().(*AddOutput).Added + if len(v) == 1 { + s := fmt.Sprintf("Added object: %s\n", v[0].Hash) + return []byte(s), nil + } - s := fmt.Sprintf("Added %v objects:\n", len(v)) - for _, obj := range v { - s += fmt.Sprintf("- %s\n", obj.Hash) - } - return []byte(s), nil + s := fmt.Sprintf("Added %v objects:\n", len(v)) + for _, obj := range v { + s += fmt.Sprintf("- %s\n", obj.Hash) + } + return []byte(s), nil + }, }, Type: &AddOutput{}, } diff --git a/core/commands2/commands.go b/core/commands2/commands.go index bc40faabe..0f102b2c4 100644 --- a/core/commands2/commands.go +++ b/core/commands2/commands.go @@ -18,10 +18,12 @@ var commandsCmd = &cmds.Command{ root := outputCommand("ipfs", Root) res.SetOutput(&root) }, - Format: func(res cmds.Response) ([]byte, error) { - v := res.Output().(*Command) - s := formatCommand(v, 0) - return []byte(s), nil + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + v := res.Output().(*Command) + s := formatCommand(v, 0) + return []byte(s), nil + }, }, Type: &Command{}, } diff --git a/core/commands2/log.go b/core/commands2/log.go index 534c8a07f..461107b1d 100644 --- a/core/commands2/log.go +++ b/core/commands2/log.go @@ -23,6 +23,8 @@ var logCmd = &cmds.Command{ s := fmt.Sprintf("Changed log level of '%s' to '%s'", args[0], args[1]) res.SetOutput(&MessageOutput{s}) }, - Format: MessageMarshaller, - Type: &MessageOutput{}, + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: MessageTextMarshaller, + }, + Type: &MessageOutput{}, } diff --git a/core/commands2/ls.go b/core/commands2/ls.go index a100f6884..1f67510cb 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -52,25 +52,27 @@ var lsCmd = &cmds.Command{ res.SetOutput(&LsOutput{output}) }, - Format: func(res cmds.Response) ([]byte, error) { - s := "" - output := res.Output().(*LsOutput).Objects + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + s := "" + output := res.Output().(*LsOutput).Objects - for _, object := range output { - if len(output) > 1 { - s += fmt.Sprintf("%s:\n", object.Hash) + for _, object := range output { + if len(output) > 1 { + s += fmt.Sprintf("%s:\n", object.Hash) + } + + for _, link := range object.Links { + s += fmt.Sprintf("-> %s %s (%v bytes)\n", link.Name, link.Hash, link.Size) + } + + if len(output) > 1 { + s += "\n" + } } - for _, link := range object.Links { - s += fmt.Sprintf("-> %s %s (%v bytes)\n", link.Name, link.Hash, link.Size) - } - - if len(output) > 1 { - s += "\n" - } - } - - return []byte(s), nil + return []byte(s), nil + }, }, Type: &LsOutput{}, } diff --git a/core/commands2/publish.go b/core/commands2/publish.go index 08c75c0c1..af12c1006 100644 --- a/core/commands2/publish.go +++ b/core/commands2/publish.go @@ -61,10 +61,12 @@ var publishCmd = &cmds.Command{ Value: ref, }) }, - Format: func(res cmds.Response) ([]byte, error) { - v := res.Output().(*PublishOutput) - s := fmt.Sprintf("Published name %s to %s\n", v.Name, v.Value) - return []byte(s), nil + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + v := res.Output().(*PublishOutput) + s := fmt.Sprintf("Published name %s to %s\n", v.Name, v.Value) + return []byte(s), nil + }, }, Type: &PublishOutput{}, } diff --git a/core/commands2/root.go b/core/commands2/root.go index 37df2c899..ee99e2ceb 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -71,11 +71,13 @@ var rootSubcommands = map[string]*cmds.Command{ log.Info("beep") res.SetOutput(v) }, - Format: func(res cmds.Response) ([]byte, error) { - v := res.Output().(*TestOutput) - s := fmt.Sprintf("Foo: %s\n", v.Foo) - s += fmt.Sprintf("Bar: %v\n", v.Bar) - return []byte(s), nil + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + v := res.Output().(*TestOutput) + s := fmt.Sprintf("Foo: %s\n", v.Foo) + s += fmt.Sprintf("Bar: %v\n", v.Bar) + return []byte(s), nil + }, }, Type: &TestOutput{}, }, @@ -120,6 +122,6 @@ type MessageOutput struct { Message string } -func MessageMarshaller(res cmds.Response) ([]byte, error) { +func MessageTextMarshaller(res cmds.Response) ([]byte, error) { return []byte(res.Output().(*MessageOutput).Message), nil } From 283c175fc4a66ea39d314d457622ba654b2d4f87 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 3 Nov 2014 23:42:05 -0800 Subject: [PATCH 091/383] commands/http: Refactored API to a Client object that takes a string address --- cmd/ipfs2/main.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 64e6a51b6..e7ea3fa36 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -6,6 +6,9 @@ import ( "os" "runtime/pprof" + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" + cmds "github.com/jbenet/go-ipfs/commands" cmdsCli "github.com/jbenet/go-ipfs/commands/cli" cmdsHttp "github.com/jbenet/go-ipfs/commands/http" @@ -133,7 +136,21 @@ func callCommand(req cmds.Request, root *cmds.Command) cmds.Response { } if (!found || !localBool) && daemon.Locked(req.Context().ConfigRoot) { - res, err = cmdsHttp.Send(req) + addr, err := ma.NewMultiaddr(req.Context().Config.Addresses.API) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + _, host, err := manet.DialArgs(addr) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + client := cmdsHttp.NewClient(host) + + res, err = client.Send(req) if err != nil { fmt.Println(err) os.Exit(1) From fd1cd995562c8b8aef96928ac343352ed155da5b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 4 Nov 2014 02:25:21 -0800 Subject: [PATCH 092/383] restore add.go from master --- core/commands/add.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/core/commands/add.go b/core/commands/add.go index 7bcc5119f..0208354cd 100644 --- a/core/commands/add.go +++ b/core/commands/add.go @@ -10,11 +10,9 @@ import ( "github.com/jbenet/go-ipfs/core" "github.com/jbenet/go-ipfs/importer" - "github.com/jbenet/go-ipfs/importer/chunk" dag "github.com/jbenet/go-ipfs/merkledag" "github.com/jbenet/go-ipfs/pin" ft "github.com/jbenet/go-ipfs/unixfs" - uio "github.com/jbenet/go-ipfs/unixfs/io" ) // Error indicating the max depth has been exceded. @@ -84,28 +82,26 @@ func addDir(n *core.IpfsNode, fpath string, depth int, out io.Writer) (*dag.Node } } - log.Info("adding dir: %s", fpath) + log.Infof("adding dir: %s", fpath) return tree, addNode(n, tree, fpath, out) } func addFile(n *core.IpfsNode, fpath string, depth int, out io.Writer) (*dag.Node, error) { - dw := uio.NewDagWriter(n.DAG, chunk.DefaultSplitter) mp, ok := n.Pinning.(pin.ManualPinner) if !ok { return nil, errors.New("invalid pinner type! expected manual pinner") } - dw.Pinner = mp - root, err := importer.ImportFileDag(fpath, dw) + root, err := importer.BuildDagFromFile(fpath, n.DAG, mp) if err != nil { return nil, err } - log.Info("adding file: %s", fpath) + log.Infof("adding file: %s", fpath) for _, l := range root.Links { - log.Info("adding subblock: %s %s", l.Name, l.Hash.B58String()) + log.Infof("adding subblock: '%s' %s", l.Name, l.Hash.B58String()) } k, err := root.Key() From c67e37716228f8511fd08f3e8fe4a89035ab7fe3 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 02:32:02 -0800 Subject: [PATCH 093/383] docs(ipfs2/daemon) add some copy can replace if desired, but at least it's not an end-user TODO --- cmd/ipfs2/daemon.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ipfs2/daemon.go b/cmd/ipfs2/daemon.go index e3f3b0043..b97d53815 100644 --- a/cmd/ipfs2/daemon.go +++ b/cmd/ipfs2/daemon.go @@ -16,7 +16,7 @@ import ( var daemonCmd = &cmds.Command{ Options: []cmds.Option{}, - Help: "TODO", + Help: "run a network-connected ipfs node", // TODO adjust copy Subcommands: map[string]*cmds.Command{}, Run: daemonFunc, } From 2a15fe8e1eba236ef398d768661a0fb82fb1c41e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 02:42:45 -0800 Subject: [PATCH 094/383] refactor(commands2/cat) readability --- core/commands2/cat.go | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/core/commands2/cat.go b/core/commands2/cat.go index c190ddd6d..f7378ed89 100644 --- a/core/commands2/cat.go +++ b/core/commands2/cat.go @@ -1,9 +1,11 @@ package commands import ( + "errors" "io" cmds "github.com/jbenet/go-ipfs/commands" + core "github.com/jbenet/go-ipfs/core" uio "github.com/jbenet/go-ipfs/unixfs/io" ) @@ -14,26 +16,41 @@ var catCmd = &cmds.Command{ Help: "TODO", Run: func(res cmds.Response, req cmds.Request) { node := req.Context().Node + paths := make([]string, 0, len(req.Arguments())) readers := make([]io.Reader, 0, len(req.Arguments())) for _, arg := range req.Arguments() { - path := arg.(string) - dagnode, err := node.Resolver.ResolvePath(path) - if err != nil { - res.SetError(err, cmds.ErrNormal) + path, ok := arg.(string) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) return } + paths = append(paths, path) + } - read, err := uio.NewDagReader(dagnode, node.DAG) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - readers = append(readers, read) + readers, err := cat(node, paths) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return } reader := io.MultiReader(readers...) res.SetOutput(reader) }, } + +func cat(node *core.IpfsNode, paths []string) ([]io.Reader, error) { + readers := make([]io.Reader, 0, len(paths)) + for _, path := range paths { + dagnode, err := node.Resolver.ResolvePath(path) + if err != nil { + return nil, err + } + read, err := uio.NewDagReader(dagnode, node.DAG) + if err != nil { + return nil, err + } + readers = append(readers, read) + } + return readers, nil +} From d2d29a80dccc7087e057022fe045a70bb04ac360 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 02:57:34 -0800 Subject: [PATCH 095/383] docs(commands2/cat) help text --- core/commands2/cat.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/commands2/cat.go b/core/commands2/cat.go index f7378ed89..d3b0fdf0f 100644 --- a/core/commands2/cat.go +++ b/core/commands2/cat.go @@ -13,7 +13,11 @@ var catCmd = &cmds.Command{ Arguments: []cmds.Argument{ cmds.Argument{"object", cmds.ArgString, false, true}, }, - Help: "TODO", + Help: `ipfs cat - Show ipfs object data. + + Retrieves the object named by and displays the Data + it contains. + `, Run: func(res cmds.Response, req cmds.Request) { node := req.Context().Node paths := make([]string, 0, len(req.Arguments())) From fee3b0dd3171f619bf5daac32ea94f59fe3b117f Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 03:05:53 -0800 Subject: [PATCH 096/383] +2 characters for readability --- cmd/ipfs2/daemon.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/ipfs2/daemon.go b/cmd/ipfs2/daemon.go index b97d53815..9ffe96d9d 100644 --- a/cmd/ipfs2/daemon.go +++ b/cmd/ipfs2/daemon.go @@ -24,12 +24,12 @@ var daemonCmd = &cmds.Command{ func daemonFunc(res cmds.Response, req cmds.Request) { ctx := req.Context() - lk, err := daemon.Lock(ctx.ConfigRoot) + lock, err := daemon.Lock(ctx.ConfigRoot) if err != nil { res.SetError(fmt.Errorf("Couldn't obtain lock. Is another daemon already running?"), cmds.ErrNormal) return } - defer lk.Close() + defer lock.Close() node, err := core.NewIpfsNode(ctx.Config, true) if err != nil { From faf6454df6691b81156486727e3f8818ef8c9884 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 03:33:06 -0800 Subject: [PATCH 097/383] refactor(init) extract method --- cmd/ipfs2/init.go | 262 +++++++++++++++++++++++----------------------- 1 file changed, 133 insertions(+), 129 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 4111481c7..abdec5959 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -23,135 +23,139 @@ var initCmd = &cmds.Command{ }, Help: `ipfs init - Initializes ipfs configuration files and generates a - new keypair. -`, + Initializes ipfs configuration files and generates a + new keypair. + `, Run: func(res cmds.Response, req cmds.Request) { - ctx := req.Context() - - u.POut("initializing ipfs node at %s\n", ctx.ConfigRoot) - filename, err := config.Filename(ctx.ConfigRoot) - if err != nil { - res.SetError(errors.New("Couldn't get home directory path"), cmds.ErrNormal) - return - } - - arg, found := req.Option("d") - dspath, ok := arg.(string) - if found && !ok { - res.SetError(errors.New("failed to parse datastore flag"), cmds.ErrNormal) - return - } - - fi, err := os.Lstat(filename) - arg, found = req.Option("f") - force, ok := arg.(bool) - if found && !ok { - res.SetError(errors.New("failed to parse force flag"), cmds.ErrNormal) - return - } - if fi != nil || (err != nil && !os.IsNotExist(err)) { - if !force { - res.SetError(errors.New("ipfs configuration file already exists!\nReinitializing would overwrite your keys.\n(use -f to force overwrite)"), cmds.ErrNormal) - return - } - } - cfg := new(config.Config) - - cfg.Datastore = config.Datastore{} - if len(dspath) == 0 { - dspath, err = config.DataStorePath("") - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - } - cfg.Datastore.Path = dspath - cfg.Datastore.Type = "leveldb" - - // Construct the data store if missing - if err := os.MkdirAll(dspath, os.ModePerm); err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - // Check the directory is writeable - if f, err := os.Create(filepath.Join(dspath, "._check_writeable")); err == nil { - os.Remove(f.Name()) - } else { - res.SetError(errors.New("Datastore '"+dspath+"' is not writeable"), cmds.ErrNormal) - return - } - - cfg.Identity = config.Identity{} - - // setup the node addresses. - cfg.Addresses = config.Addresses{ - Swarm: "/ip4/0.0.0.0/tcp/4001", - API: "/ip4/127.0.0.1/tcp/5001", - } - - // setup the node mount points. - cfg.Mounts = config.Mounts{ - IPFS: "/ipfs", - IPNS: "/ipns", - } - - arg, found = req.Option("b") - nbits, ok := arg.(int) - if found && !ok { - res.SetError(errors.New("failed to get bits flag"), cmds.ErrNormal) - return - } else if !found { - nbits = 4096 - } - if nbits < 1024 { - res.SetError(errors.New("Bitsize less than 1024 is considered unsafe."), cmds.ErrNormal) - return - } - - u.POut("generating key pair\n") - sk, pk, err := ci.GenerateKeyPair(ci.RSA, nbits) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - // currently storing key unencrypted. in the future we need to encrypt it. - // TODO(security) - skbytes, err := sk.Bytes() - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - cfg.Identity.PrivKey = base64.StdEncoding.EncodeToString(skbytes) - - id, err := peer.IDFromPubKey(pk) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - cfg.Identity.PeerID = id.Pretty() - - // Use these hardcoded bootstrap peers for now. - cfg.Bootstrap = []*config.BootstrapPeer{ - &config.BootstrapPeer{ - // mars.i.ipfs.io - PeerID: "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", - Address: "/ip4/104.131.131.82/tcp/4001", - }, - } - - // tracking ipfs version used to generate the init folder and adding update checker default setting. - cfg.Version = config.Version{ - Check: "error", - Current: updates.Version, - } - - err = config.WriteConfigFile(filename, cfg) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } + foo(res, req) }, } + +func foo(res cmds.Response, req cmds.Request) { + ctx := req.Context() + + u.POut("initializing ipfs node at %s\n", ctx.ConfigRoot) + filename, err := config.Filename(ctx.ConfigRoot) + if err != nil { + res.SetError(errors.New("Couldn't get home directory path"), cmds.ErrNormal) + return + } + + arg, found := req.Option("d") + dspath, ok := arg.(string) + if found && !ok { + res.SetError(errors.New("failed to parse datastore flag"), cmds.ErrNormal) + return + } + + fi, err := os.Lstat(filename) + arg, found = req.Option("f") + force, ok := arg.(bool) + if found && !ok { + res.SetError(errors.New("failed to parse force flag"), cmds.ErrNormal) + return + } + if fi != nil || (err != nil && !os.IsNotExist(err)) { + if !force { + res.SetError(errors.New("ipfs configuration file already exists!\nReinitializing would overwrite your keys.\n(use -f to force overwrite)"), cmds.ErrNormal) + return + } + } + cfg := new(config.Config) + + cfg.Datastore = config.Datastore{} + if len(dspath) == 0 { + dspath, err = config.DataStorePath("") + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + } + cfg.Datastore.Path = dspath + cfg.Datastore.Type = "leveldb" + + // Construct the data store if missing + if err := os.MkdirAll(dspath, os.ModePerm); err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + // Check the directory is writeable + if f, err := os.Create(filepath.Join(dspath, "._check_writeable")); err == nil { + os.Remove(f.Name()) + } else { + res.SetError(errors.New("Datastore '"+dspath+"' is not writeable"), cmds.ErrNormal) + return + } + + cfg.Identity = config.Identity{} + + // setup the node addresses. + cfg.Addresses = config.Addresses{ + Swarm: "/ip4/0.0.0.0/tcp/4001", + API: "/ip4/127.0.0.1/tcp/5001", + } + + // setup the node mount points. + cfg.Mounts = config.Mounts{ + IPFS: "/ipfs", + IPNS: "/ipns", + } + + arg, found = req.Option("b") + nbits, ok := arg.(int) + if found && !ok { + res.SetError(errors.New("failed to get bits flag"), cmds.ErrNormal) + return + } else if !found { + nbits = 4096 + } + if nbits < 1024 { + res.SetError(errors.New("Bitsize less than 1024 is considered unsafe."), cmds.ErrNormal) + return + } + + u.POut("generating key pair\n") + sk, pk, err := ci.GenerateKeyPair(ci.RSA, nbits) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + // currently storing key unencrypted. in the future we need to encrypt it. + // TODO(security) + skbytes, err := sk.Bytes() + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + cfg.Identity.PrivKey = base64.StdEncoding.EncodeToString(skbytes) + + id, err := peer.IDFromPubKey(pk) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + cfg.Identity.PeerID = id.Pretty() + + // Use these hardcoded bootstrap peers for now. + cfg.Bootstrap = []*config.BootstrapPeer{ + &config.BootstrapPeer{ + // mars.i.ipfs.io + PeerID: "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + Address: "/ip4/104.131.131.82/tcp/4001", + }, + } + + // tracking ipfs version used to generate the init folder and adding update checker default setting. + cfg.Version = config.Version{ + Check: "error", + Current: updates.Version, + } + + err = config.WriteConfigFile(filename, cfg) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } +} From d894924152104b13ba69ba279a45df6f17be5d2b Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 03:36:13 -0800 Subject: [PATCH 098/383] refactor(init) return an error --- cmd/ipfs2/init.go | 49 ++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index abdec5959..16fc8d771 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -27,38 +27,39 @@ var initCmd = &cmds.Command{ new keypair. `, Run: func(res cmds.Response, req cmds.Request) { - foo(res, req) + err := foo(res, req) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } }, } -func foo(res cmds.Response, req cmds.Request) { +func foo(res cmds.Response, req cmds.Request) error { ctx := req.Context() u.POut("initializing ipfs node at %s\n", ctx.ConfigRoot) filename, err := config.Filename(ctx.ConfigRoot) if err != nil { - res.SetError(errors.New("Couldn't get home directory path"), cmds.ErrNormal) - return + return errors.New("Couldn't get home directory path") } arg, found := req.Option("d") dspath, ok := arg.(string) if found && !ok { - res.SetError(errors.New("failed to parse datastore flag"), cmds.ErrNormal) - return + return errors.New("failed to parse datastore flag") } fi, err := os.Lstat(filename) arg, found = req.Option("f") force, ok := arg.(bool) if found && !ok { - res.SetError(errors.New("failed to parse force flag"), cmds.ErrNormal) - return + return errors.New("failed to parse force flag") } if fi != nil || (err != nil && !os.IsNotExist(err)) { if !force { - res.SetError(errors.New("ipfs configuration file already exists!\nReinitializing would overwrite your keys.\n(use -f to force overwrite)"), cmds.ErrNormal) - return + // TODO multi-line string + return errors.New("ipfs configuration file already exists!\nReinitializing would overwrite your keys.\n(use -f to force overwrite)") } } cfg := new(config.Config) @@ -67,8 +68,7 @@ func foo(res cmds.Response, req cmds.Request) { if len(dspath) == 0 { dspath, err = config.DataStorePath("") if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return err } } cfg.Datastore.Path = dspath @@ -76,16 +76,14 @@ func foo(res cmds.Response, req cmds.Request) { // Construct the data store if missing if err := os.MkdirAll(dspath, os.ModePerm); err != nil { - res.SetError(err, cmds.ErrNormal) - return + return err } // Check the directory is writeable if f, err := os.Create(filepath.Join(dspath, "._check_writeable")); err == nil { os.Remove(f.Name()) } else { - res.SetError(errors.New("Datastore '"+dspath+"' is not writeable"), cmds.ErrNormal) - return + return errors.New("Datastore '" + dspath + "' is not writeable") } cfg.Identity = config.Identity{} @@ -105,36 +103,31 @@ func foo(res cmds.Response, req cmds.Request) { arg, found = req.Option("b") nbits, ok := arg.(int) if found && !ok { - res.SetError(errors.New("failed to get bits flag"), cmds.ErrNormal) - return + return errors.New("failed to get bits flag") } else if !found { nbits = 4096 } if nbits < 1024 { - res.SetError(errors.New("Bitsize less than 1024 is considered unsafe."), cmds.ErrNormal) - return + return errors.New("Bitsize less than 1024 is considered unsafe.") } u.POut("generating key pair\n") sk, pk, err := ci.GenerateKeyPair(ci.RSA, nbits) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return err } // currently storing key unencrypted. in the future we need to encrypt it. // TODO(security) skbytes, err := sk.Bytes() if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return err } cfg.Identity.PrivKey = base64.StdEncoding.EncodeToString(skbytes) id, err := peer.IDFromPubKey(pk) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return err } cfg.Identity.PeerID = id.Pretty() @@ -155,7 +148,7 @@ func foo(res cmds.Response, req cmds.Request) { err = config.WriteConfigFile(filename, cfg) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return err } + return nil } From 3c8657503c0448221a095141703d29f8de6af4dd Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 03:38:01 -0800 Subject: [PATCH 099/383] refactor(init) check file after --- cmd/ipfs2/init.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 16fc8d771..c4caf4f5a 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -50,12 +50,12 @@ func foo(res cmds.Response, req cmds.Request) error { return errors.New("failed to parse datastore flag") } - fi, err := os.Lstat(filename) arg, found = req.Option("f") force, ok := arg.(bool) if found && !ok { return errors.New("failed to parse force flag") } + fi, err := os.Lstat(filename) if fi != nil || (err != nil && !os.IsNotExist(err)) { if !force { // TODO multi-line string From b987c99eeac0050ac7e4a3343ce2118e0ab3bc61 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 03:43:43 -0800 Subject: [PATCH 100/383] refactor(init) extract dspath --- cmd/ipfs2/init.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index c4caf4f5a..2ea048301 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -27,7 +27,15 @@ var initCmd = &cmds.Command{ new keypair. `, Run: func(res cmds.Response, req cmds.Request) { - err := foo(res, req) + + arg, found := req.Option("d") + dspath, ok := arg.(string) // TODO param + if found && !ok { + res.SetError(errors.New("failed to parse datastore flag"), cmds.ErrNormal) + return + } + + err := foo(res, req, dspath) if err != nil { res.SetError(err, cmds.ErrNormal) return @@ -35,7 +43,7 @@ var initCmd = &cmds.Command{ }, } -func foo(res cmds.Response, req cmds.Request) error { +func foo(res cmds.Response, req cmds.Request, dspath string) error { ctx := req.Context() u.POut("initializing ipfs node at %s\n", ctx.ConfigRoot) @@ -44,17 +52,12 @@ func foo(res cmds.Response, req cmds.Request) error { return errors.New("Couldn't get home directory path") } - arg, found := req.Option("d") - dspath, ok := arg.(string) - if found && !ok { - return errors.New("failed to parse datastore flag") - } - - arg, found = req.Option("f") - force, ok := arg.(bool) + arg, found := req.Option("f") + force, ok := arg.(bool) // TODO param if found && !ok { return errors.New("failed to parse force flag") } + fi, err := os.Lstat(filename) if fi != nil || (err != nil && !os.IsNotExist(err)) { if !force { @@ -101,7 +104,7 @@ func foo(res cmds.Response, req cmds.Request) error { } arg, found = req.Option("b") - nbits, ok := arg.(int) + nbits, ok := arg.(int) // TODO param if found && !ok { return errors.New("failed to get bits flag") } else if !found { From 630b88d227fbbf3f0af9faa18af86b3666e6f833 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 03:45:29 -0800 Subject: [PATCH 101/383] refactor(init) extract force flag --- cmd/ipfs2/init.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 2ea048301..5bb24fb16 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -29,13 +29,20 @@ var initCmd = &cmds.Command{ Run: func(res cmds.Response, req cmds.Request) { arg, found := req.Option("d") - dspath, ok := arg.(string) // TODO param + dspath, ok := arg.(string) if found && !ok { res.SetError(errors.New("failed to parse datastore flag"), cmds.ErrNormal) return } - err := foo(res, req, dspath) + arg, found = req.Option("f") + force, ok := arg.(bool) // TODO param + if found && !ok { + res.SetError(errors.New("failed to parse force flag"), cmds.ErrNormal) + return + } + + err := foo(res, req, dspath, force) if err != nil { res.SetError(err, cmds.ErrNormal) return @@ -43,7 +50,7 @@ var initCmd = &cmds.Command{ }, } -func foo(res cmds.Response, req cmds.Request, dspath string) error { +func foo(res cmds.Response, req cmds.Request, dspath string, force bool) error { ctx := req.Context() u.POut("initializing ipfs node at %s\n", ctx.ConfigRoot) @@ -52,12 +59,6 @@ func foo(res cmds.Response, req cmds.Request, dspath string) error { return errors.New("Couldn't get home directory path") } - arg, found := req.Option("f") - force, ok := arg.(bool) // TODO param - if found && !ok { - return errors.New("failed to parse force flag") - } - fi, err := os.Lstat(filename) if fi != nil || (err != nil && !os.IsNotExist(err)) { if !force { From f3048ab712e335b07169d14dce91fe1ca24a20bf Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 03:54:56 -0800 Subject: [PATCH 102/383] refactor(init) extract bits flag --- cmd/ipfs2/init.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 5bb24fb16..eacaf6bcf 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -42,7 +42,16 @@ var initCmd = &cmds.Command{ return } - err := foo(res, req, dspath, force) + arg, found = req.Option("b") + nBitsForKeypair, ok := arg.(int) // TODO param + if found && !ok { + res.SetError(errors.New("failed to get bits flag"), cmds.ErrNormal) + return + } else if !found { + nBitsForKeypair = 4096 + } + + err := foo(res, req, dspath, force, nBitsForKeypair) if err != nil { res.SetError(err, cmds.ErrNormal) return @@ -50,7 +59,7 @@ var initCmd = &cmds.Command{ }, } -func foo(res cmds.Response, req cmds.Request, dspath string, force bool) error { +func foo(res cmds.Response, req cmds.Request, dspath string, force bool, nBitsForKeypair int) error { ctx := req.Context() u.POut("initializing ipfs node at %s\n", ctx.ConfigRoot) @@ -104,19 +113,13 @@ func foo(res cmds.Response, req cmds.Request, dspath string, force bool) error { IPNS: "/ipns", } - arg, found = req.Option("b") - nbits, ok := arg.(int) // TODO param - if found && !ok { - return errors.New("failed to get bits flag") - } else if !found { - nbits = 4096 - } - if nbits < 1024 { + // TODO guard + if nBitsForKeypair < 1024 { return errors.New("Bitsize less than 1024 is considered unsafe.") } u.POut("generating key pair\n") - sk, pk, err := ci.GenerateKeyPair(ci.RSA, nbits) + sk, pk, err := ci.GenerateKeyPair(ci.RSA, nBitsForKeypair) if err != nil { return err } From 1b5e4032ff88c60c1bef097cdf4ec1728cfd2f3b Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 03:56:51 -0800 Subject: [PATCH 103/383] refactor(init) extract context --- cmd/ipfs2/init.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index eacaf6bcf..898b5411e 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -51,7 +51,7 @@ var initCmd = &cmds.Command{ nBitsForKeypair = 4096 } - err := foo(res, req, dspath, force, nBitsForKeypair) + err := foo(res, req.Context().ConfigRoot, dspath, force, nBitsForKeypair) if err != nil { res.SetError(err, cmds.ErrNormal) return @@ -59,11 +59,10 @@ var initCmd = &cmds.Command{ }, } -func foo(res cmds.Response, req cmds.Request, dspath string, force bool, nBitsForKeypair int) error { - ctx := req.Context() +func foo(res cmds.Response, configRoot string, dspath string, force bool, nBitsForKeypair int) error { - u.POut("initializing ipfs node at %s\n", ctx.ConfigRoot) - filename, err := config.Filename(ctx.ConfigRoot) + u.POut("initializing ipfs node at %s\n", configRoot) + filename, err := config.Filename(configRoot) if err != nil { return errors.New("Couldn't get home directory path") } From 3b7983e028f8035261df3553dd419b0174ecfb44 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 03:57:38 -0800 Subject: [PATCH 104/383] refactor(init) extract response --- cmd/ipfs2/init.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 898b5411e..83524cde6 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -51,7 +51,7 @@ var initCmd = &cmds.Command{ nBitsForKeypair = 4096 } - err := foo(res, req.Context().ConfigRoot, dspath, force, nBitsForKeypair) + err := foo(req.Context().ConfigRoot, dspath, force, nBitsForKeypair) if err != nil { res.SetError(err, cmds.ErrNormal) return @@ -59,7 +59,7 @@ var initCmd = &cmds.Command{ }, } -func foo(res cmds.Response, configRoot string, dspath string, force bool, nBitsForKeypair int) error { +func foo(configRoot string, dspath string, force bool, nBitsForKeypair int) error { u.POut("initializing ipfs node at %s\n", configRoot) filename, err := config.Filename(configRoot) From 8d7ebcb582cb5a2bdbc62eb54c522530422c0499 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 03:59:39 -0800 Subject: [PATCH 105/383] refactor(init) rename extracted method --- cmd/ipfs2/init.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 83524cde6..ff9d1c643 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -51,7 +51,7 @@ var initCmd = &cmds.Command{ nBitsForKeypair = 4096 } - err := foo(req.Context().ConfigRoot, dspath, force, nBitsForKeypair) + err := doInit(req.Context().ConfigRoot, dspath, force, nBitsForKeypair) if err != nil { res.SetError(err, cmds.ErrNormal) return @@ -59,7 +59,7 @@ var initCmd = &cmds.Command{ }, } -func foo(configRoot string, dspath string, force bool, nBitsForKeypair int) error { +func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) error { u.POut("initializing ipfs node at %s\n", configRoot) filename, err := config.Filename(configRoot) From a1e738e297e2f477b53d94aa5c8ca6e9e2ea954b Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 04:03:17 -0800 Subject: [PATCH 106/383] docs(init) rename filename var --- cmd/ipfs2/init.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index ff9d1c643..4b612c872 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -62,12 +62,13 @@ var initCmd = &cmds.Command{ func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) error { u.POut("initializing ipfs node at %s\n", configRoot) - filename, err := config.Filename(configRoot) + + configFilename, err := config.Filename(configRoot) if err != nil { return errors.New("Couldn't get home directory path") } - fi, err := os.Lstat(filename) + fi, err := os.Lstat(configFilename) if fi != nil || (err != nil && !os.IsNotExist(err)) { if !force { // TODO multi-line string @@ -112,7 +113,7 @@ func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) e IPNS: "/ipns", } - // TODO guard + // TODO guard higher up if nBitsForKeypair < 1024 { return errors.New("Bitsize less than 1024 is considered unsafe.") } @@ -152,7 +153,7 @@ func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) e Current: updates.Version, } - err = config.WriteConfigFile(filename, cfg) + err = config.WriteConfigFile(configFilename, cfg) if err != nil { return err } From 46f1afbe083ec0f18490c3352b3d2aa379cec7ea Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 04:06:47 -0800 Subject: [PATCH 107/383] refactor(init) re-order --- cmd/ipfs2/init.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 4b612c872..706046b37 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -77,15 +77,16 @@ func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) e } cfg := new(config.Config) - cfg.Datastore = config.Datastore{} if len(dspath) == 0 { dspath, err = config.DataStorePath("") if err != nil { return err } } - cfg.Datastore.Path = dspath - cfg.Datastore.Type = "leveldb" + cfg.Datastore = config.Datastore{ + Path: dspath, + Type: "leveldb", + } // Construct the data store if missing if err := os.MkdirAll(dspath, os.ModePerm); err != nil { From 96fd88e91634e02fd1f0cbb400368b2424aa925a Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 04:16:34 -0800 Subject: [PATCH 108/383] feat(ipfs2/main) port mem-profiling from previous main was added 5 days ago in... https://github.com/jbenet/go-ipfs/commit/7510ef2081e3787e97b815e69a676bd6fc75a9cd --- cmd/ipfs2/main.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index e7ea3fa36..d156a3296 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -22,12 +22,21 @@ import ( // log is the command logger var log = u.Logger("cmd/ipfs") +const heapProfile = "ipfs.mprof" + func main() { args := os.Args[1:] req, root := createRequest(args) handleOptions(req, root) res := callCommand(req, root) outputResponse(res) + + if u.Debug { + err := writeHeapProfileToFile() + if err != nil { + log.Critical(err) + } + } } func createRequest(args []string) (cmds.Request, *cmds.Command) { @@ -232,3 +241,12 @@ func getConfig(path string) (*config.Config, error) { return config.Load(configFile) } + +func writeHeapProfileToFile() error { + mprof, err := os.Create(heapProfile) + if err != nil { + log.Fatal(err) + } + defer mprof.Close() + return pprof.WriteHeapProfile(mprof) +} From e86e7d875ed82ae36d165840a279cedf2fce535e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 05:58:10 -0800 Subject: [PATCH 109/383] extract publish func --- core/commands2/publish.go | 41 +++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/core/commands2/publish.go b/core/commands2/publish.go index af12c1006..e1aba25a5 100644 --- a/core/commands2/publish.go +++ b/core/commands2/publish.go @@ -5,12 +5,15 @@ import ( "fmt" cmds "github.com/jbenet/go-ipfs/commands" + core "github.com/jbenet/go-ipfs/core" + crypto "github.com/jbenet/go-ipfs/crypto" nsys "github.com/jbenet/go-ipfs/namesys" u "github.com/jbenet/go-ipfs/util" ) type PublishOutput struct { - Name, Value string + Name string + Value string } var publishCmd = &cmds.Command{ @@ -40,26 +43,16 @@ var publishCmd = &cmds.Command{ default: res.SetError(fmt.Errorf("Publish expects 1 or 2 args; got %d.", len(args)), cmds.ErrClient) } - // later, n.Keychain.Get(name).PrivKey + + // TODO n.Keychain.Get(name).PrivKey k := n.Identity.PrivKey() + publishOutput, err := publish(n, k, ref) - pub := nsys.NewRoutingPublisher(n.Routing) - err := pub.Publish(k, ref) if err != nil { res.SetError(err, cmds.ErrNormal) return } - - hash, err := k.GetPublic().Hash() - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - res.SetOutput(&PublishOutput{ - Name: u.Key(hash).String(), - Value: ref, - }) + res.SetOutput(publishOutput) }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ cmds.Text: func(res cmds.Response) ([]byte, error) { @@ -70,3 +63,21 @@ var publishCmd = &cmds.Command{ }, Type: &PublishOutput{}, } + +func publish(n *core.IpfsNode, k crypto.PrivKey, ref string) (*PublishOutput, error) { + pub := nsys.NewRoutingPublisher(n.Routing) + err := pub.Publish(k, ref) + if err != nil { + return nil, err + } + + hash, err := k.GetPublic().Hash() + if err != nil { + return nil, err + } + + return &PublishOutput{ + Name: u.Key(hash).String(), + Value: ref, + }, nil +} From 542c2a2da3327270996151d34d55dd3d3416210a Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 19:06:12 -0800 Subject: [PATCH 110/383] fix(ipfs2/init) extract bootstrap --- cmd/ipfs2/init.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 706046b37..b1db2692f 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -59,6 +59,15 @@ var initCmd = &cmds.Command{ }, } +// Use these hardcoded bootstrap peers for now. +var defaultPeers = []*config.BootstrapPeer{ + &config.BootstrapPeer{ + // mars.i.ipfs.io + PeerID: "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + Address: "/ip4/104.131.131.82/tcp/4001", + }, +} + func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) error { u.POut("initializing ipfs node at %s\n", configRoot) @@ -139,14 +148,7 @@ func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) e } cfg.Identity.PeerID = id.Pretty() - // Use these hardcoded bootstrap peers for now. - cfg.Bootstrap = []*config.BootstrapPeer{ - &config.BootstrapPeer{ - // mars.i.ipfs.io - PeerID: "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", - Address: "/ip4/104.131.131.82/tcp/4001", - }, - } + cfg.Bootstrap = defaultPeers // tracking ipfs version used to generate the init folder and adding update checker default setting. cfg.Version = config.Version{ From 62fd9166ceb7a960f19a00dc290926c3764dd4db Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 19:09:47 -0800 Subject: [PATCH 111/383] fix(ipfs2/init) datastore --- cmd/ipfs2/init.go | 50 ++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index b1db2692f..9f3c071cc 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -86,28 +86,11 @@ func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) e } cfg := new(config.Config) - if len(dspath) == 0 { - dspath, err = config.DataStorePath("") - if err != nil { - return err - } - } - cfg.Datastore = config.Datastore{ - Path: dspath, - Type: "leveldb", - } - - // Construct the data store if missing - if err := os.MkdirAll(dspath, os.ModePerm); err != nil { + ds, err := datastoreConfig(dspath) + if err != nil { return err } - - // Check the directory is writeable - if f, err := os.Create(filepath.Join(dspath, "._check_writeable")); err == nil { - os.Remove(f.Name()) - } else { - return errors.New("Datastore '" + dspath + "' is not writeable") - } + cfg.Datastore = ds cfg.Identity = config.Identity{} @@ -162,3 +145,30 @@ func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) e } return nil } + +func datastoreConfig(dspath string) (config.Datastore, error) { + ds := config.Datastore{} + if len(dspath) == 0 { + var err error + dspath, err = config.DataStorePath("") + if err != nil { + return ds, err + } + } + ds.Path = dspath + ds.Type = "leveldb" + + // Construct the data store if missing + if err := os.MkdirAll(dspath, os.ModePerm); err != nil { + return ds, err + } + + // Check the directory is writeable + if f, err := os.Create(filepath.Join(dspath, "._check_writeable")); err == nil { + os.Remove(f.Name()) + } else { + return ds, errors.New("Datastore '" + dspath + "' is not writeable") + } + + return ds, nil +} From e305e45e8165b7877db7638e633204e40d7ed9ba Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 19:13:04 -0800 Subject: [PATCH 112/383] fix(ipfs2/init) identity --- cmd/ipfs2/init.go | 62 +++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 9f3c071cc..9b22a33e6 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -3,6 +3,7 @@ package main import ( "encoding/base64" "errors" + "fmt" "os" "path/filepath" @@ -92,7 +93,11 @@ func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) e } cfg.Datastore = ds - cfg.Identity = config.Identity{} + identity, err := identityConfig(nBitsForKeypair) + if err != nil { + return err + } + cfg.Identity = identity // setup the node addresses. cfg.Addresses = config.Addresses{ @@ -106,31 +111,6 @@ func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) e IPNS: "/ipns", } - // TODO guard higher up - if nBitsForKeypair < 1024 { - return errors.New("Bitsize less than 1024 is considered unsafe.") - } - - u.POut("generating key pair\n") - sk, pk, err := ci.GenerateKeyPair(ci.RSA, nBitsForKeypair) - if err != nil { - return err - } - - // currently storing key unencrypted. in the future we need to encrypt it. - // TODO(security) - skbytes, err := sk.Bytes() - if err != nil { - return err - } - cfg.Identity.PrivKey = base64.StdEncoding.EncodeToString(skbytes) - - id, err := peer.IDFromPubKey(pk) - if err != nil { - return err - } - cfg.Identity.PeerID = id.Pretty() - cfg.Bootstrap = defaultPeers // tracking ipfs version used to generate the init folder and adding update checker default setting. @@ -172,3 +152,33 @@ func datastoreConfig(dspath string) (config.Datastore, error) { return ds, nil } + +func identityConfig(nbits int) (config.Identity, error) { + // TODO guard higher up + ident := config.Identity{} + if nbits < 1024 { + return ident, errors.New("Bitsize less than 1024 is considered unsafe.") + } + + fmt.Println("generating key pair...") + sk, pk, err := ci.GenerateKeyPair(ci.RSA, nbits) + if err != nil { + return ident, err + } + + // currently storing key unencrypted. in the future we need to encrypt it. + // TODO(security) + skbytes, err := sk.Bytes() + if err != nil { + return ident, err + } + ident.PrivKey = base64.StdEncoding.EncodeToString(skbytes) + + id, err := peer.IDFromPubKey(pk) + if err != nil { + return ident, err + } + ident.PeerID = id.Pretty() + + return ident, nil +} From 79015e78913d7ba768eeb4d3b4bfa247a2de41ae Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 19:18:42 -0800 Subject: [PATCH 113/383] fix(ipfs2/init) declarative --- cmd/ipfs2/init.go | 49 ++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 9b22a33e6..4330cdfc8 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -85,41 +85,46 @@ func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) e return errors.New("ipfs configuration file already exists!\nReinitializing would overwrite your keys.\n(use -f to force overwrite)") } } - cfg := new(config.Config) ds, err := datastoreConfig(dspath) if err != nil { return err } - cfg.Datastore = ds identity, err := identityConfig(nBitsForKeypair) if err != nil { return err } - cfg.Identity = identity - // setup the node addresses. - cfg.Addresses = config.Addresses{ - Swarm: "/ip4/0.0.0.0/tcp/4001", - API: "/ip4/127.0.0.1/tcp/5001", + conf := config.Config{ + + // setup the node addresses. + Addresses: config.Addresses{ + Swarm: "/ip4/0.0.0.0/tcp/4001", + API: "/ip4/127.0.0.1/tcp/5001", + }, + + Bootstrap: defaultPeers, + + Datastore: ds, + + Identity: identity, + + // setup the node mount points. + Mounts: config.Mounts{ + IPFS: "/ipfs", + IPNS: "/ipns", + }, + + // tracking ipfs version used to generate the init folder and adding + // update checker default setting. + Version: config.Version{ + Check: "error", + Current: updates.Version, + }, } - // setup the node mount points. - cfg.Mounts = config.Mounts{ - IPFS: "/ipfs", - IPNS: "/ipns", - } - - cfg.Bootstrap = defaultPeers - - // tracking ipfs version used to generate the init folder and adding update checker default setting. - cfg.Version = config.Version{ - Check: "error", - Current: updates.Version, - } - - err = config.WriteConfigFile(configFilename, cfg) + err = config.WriteConfigFile(configFilename, conf) if err != nil { return err } From 8960a4ff8bef55d8b5d26cc0ab842a0cc8da9b28 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 19:39:51 -0800 Subject: [PATCH 114/383] TODO(ipfs2/init) add FIXME note as a reminder about https://github.com/jbenet/go-ipfs/commit/c6b74207bc35504cfa678d632d9d9c32f4feaf62 --- cmd/ipfs2/init.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 4330cdfc8..97222c377 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -118,6 +118,9 @@ func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) e // tracking ipfs version used to generate the init folder and adding // update checker default setting. + + // FIXME(brian): before merging into master, change this to... + // Version: config.VersionDefaultValue() Version: config.Version{ Check: "error", Current: updates.Version, From d180832f3ef1d37d5c31f45f4d9767917159162c Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 4 Nov 2014 20:34:20 -0800 Subject: [PATCH 115/383] add todo --- cmd/ipfs2/init.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 97222c377..241929f52 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -69,6 +69,7 @@ var defaultPeers = []*config.BootstrapPeer{ }, } +// TODO add default welcome hash: eaa68bedae247ed1e5bd0eb4385a3c0959b976e4 func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) error { u.POut("initializing ipfs node at %s\n", configRoot) From 116041c5ec52335bbafe1755c05f3f30bbe2a70d Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 4 Nov 2014 17:41:17 -0800 Subject: [PATCH 116/383] commands: Fixed argument value/definition mapping --- commands/cli/parse.go | 51 ++++++++++++++++++++++++++++++++++-------- commands/command.go | 24 +++++++++++++++----- commands/http/parse.go | 29 +++++++++++++++++++----- 3 files changed, 85 insertions(+), 19 deletions(-) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index 1c2b11b86..87317691a 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -117,25 +117,58 @@ func parseOptions(input []string) (map[string]interface{}, []string, error) { } func parseArgs(stringArgs []string, cmd *cmds.Command) ([]interface{}, error) { - var argDef cmds.Argument - args := make([]interface{}, len(stringArgs)) + args := make([]interface{}, 0) - for i, arg := range stringArgs { - if i < len(cmd.Arguments) { - argDef = cmd.Arguments[i] + // count required argument definitions + lenRequired := 0 + for _, argDef := range cmd.Arguments { + if argDef.Required { + lenRequired++ + } + } + + j := 0 + for _, argDef := range cmd.Arguments { + // skip optional argument definitions if there aren't sufficient remaining values + if len(stringArgs)-j <= lenRequired && !argDef.Required { + continue } - if argDef.Type == cmds.ArgString { - args[i] = arg + if j >= len(stringArgs) { + break + } + if argDef.Variadic { + for _, arg := range stringArgs[j:] { + var err error + args, err = appendArg(args, argDef, arg) + if err != nil { + return nil, err + } + } } else { - in, err := os.Open(arg) + var err error + args, err = appendArg(args, argDef, stringArgs[j]) if err != nil { return nil, err } - args[i] = in } + + j++ } return args, nil } + +func appendArg(args []interface{}, argDef cmds.Argument, value string) ([]interface{}, error) { + if argDef.Type == cmds.ArgString { + return append(args, value), nil + + } else { + in, err := os.Open(value) + if err != nil { + return nil, err + } + return append(args, in), nil + } +} diff --git a/commands/command.go b/commands/command.go index cac29918e..d90aa3f7f 100644 --- a/commands/command.go +++ b/commands/command.go @@ -149,13 +149,27 @@ func (c *Command) CheckArguments(req Request) error { return fmt.Errorf("Expected %v arguments, got %v", len(argDefs), len(args)) } + // count required argument definitions + lenRequired := 0 + for _, argDef := range c.Arguments { + if argDef.Required { + lenRequired++ + } + } + // iterate over the arg definitions - for i, argDef := range c.Arguments { + j := 0 + for _, argDef := range c.Arguments { + // skip optional argument definitions if there aren't sufficient remaining values + if len(args)-j <= lenRequired && !argDef.Required { + continue + } // the value for this argument definition. can be nil if it wasn't provided by the caller var v interface{} - if i < len(args) { - v = args[i] + if j < len(args) { + v = args[j] + j++ } err := checkArgValue(v, argDef) @@ -164,8 +178,8 @@ func (c *Command) CheckArguments(req Request) error { } // any additional values are for the variadic arg definition - if argDef.Variadic && i < len(args)-1 { - for _, val := range args[i+1:] { + if argDef.Variadic && j < len(args)-1 { + for _, val := range args[j+1:] { err := checkArgValue(val, argDef) if err != nil { return err diff --git a/commands/http/parse.go b/commands/http/parse.go index c9168f3e3..18b829227 100644 --- a/commands/http/parse.go +++ b/commands/http/parse.go @@ -39,13 +39,32 @@ func Parse(r *http.Request, root *cmds.Command) (cmds.Request, error) { opts, stringArgs2 := parseOptions(r) stringArgs = append(stringArgs, stringArgs2...) - // Note that the argument handling here is dumb, it does not do any error-checking. - // (Arguments are further processed when the request is passed to the command to run) args := make([]interface{}, 0) - for _, arg := range cmd.Arguments { - if arg.Type == cmds.ArgString { - if arg.Variadic { + // count required argument definitions + lenRequired := 0 + for _, argDef := range cmd.Arguments { + if argDef.Required { + lenRequired++ + } + } + + // count the number of provided argument values + valCount := len(stringArgs) + // TODO: add total number of parts in request body (instead of just 1 if body is present) + if r.Body != nil { + valCount += 1 + } + + for _, argDef := range cmd.Arguments { + // skip optional argument definitions if there aren't sufficient remaining values + if valCount <= lenRequired && !argDef.Required { + continue + } + valCount-- + + if argDef.Type == cmds.ArgString { + if argDef.Variadic { for _, s := range stringArgs { args = append(args, s) } From 37f05a8bf6759e3c5ce3be193ccbbc101d9193c7 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 4 Nov 2014 18:49:59 -0800 Subject: [PATCH 117/383] commands: Ensure argument parsing maintains total argument count, so that argument validation will fail if there are too many --- commands/cli/parse.go | 4 ++++ commands/http/parse.go | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index 87317691a..0f6751c20 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -157,6 +157,10 @@ func parseArgs(stringArgs []string, cmd *cmds.Command) ([]interface{}, error) { j++ } + if len(stringArgs)-j > 0 { + args = append(args, make([]interface{}, len(stringArgs)-j)) + } + return args, nil } diff --git a/commands/http/parse.go b/commands/http/parse.go index 18b829227..59e626bca 100644 --- a/commands/http/parse.go +++ b/commands/http/parse.go @@ -83,6 +83,10 @@ func Parse(r *http.Request, root *cmds.Command) (cmds.Request, error) { } } + if valCount-1 > 0 { + args = append(args, make([]interface{}, valCount-1)) + } + req := cmds.NewRequest(path, opts, args, cmd) err = cmd.CheckArguments(req) From 48c108d523f591db3224e45a8b8370870eadd00a Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 4 Nov 2014 18:55:59 -0800 Subject: [PATCH 118/383] commands/cli: Made Parse return the resolved subcommand, even on error --- commands/cli/parse.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index 0f6751c20..2a7be8341 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -11,7 +11,7 @@ import ( // Parse parses the input commandline string (cmd, flags, and args). // returns the corresponding command Request object. -func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, error) { +func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, *cmds.Command, error) { var root, cmd *cmds.Command var path, stringArgs []string var opts map[string]interface{} @@ -22,7 +22,7 @@ func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, p, i, c := parsePath(input, r) o, s, err := parseOptions(i) if err != nil { - return nil, nil, err + return nil, root, c, err } length := len(p) @@ -37,22 +37,22 @@ func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, } if maxLength == 0 { - return nil, nil, errors.New("Not a valid subcommand") + return nil, root, nil, errors.New("Not a valid subcommand") } args, err := parseArgs(stringArgs, cmd) if err != nil { - return nil, nil, err + return nil, root, cmd, err } req := cmds.NewRequest(path, opts, args, cmd) err = cmd.CheckArguments(req) if err != nil { - return nil, nil, err + return nil, root, cmd, err } - return req, root, nil + return req, root, cmd, nil } // parsePath separates the command path and the opts and args from a command string From 1cb94a1231709744f1bb1f87c119cb2729422d4f Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 4 Nov 2014 18:56:32 -0800 Subject: [PATCH 119/383] cmd/ipfs2: Display subcommand help text on parse error --- cmd/ipfs2/main.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index d156a3296..4a4516ed8 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -40,10 +40,16 @@ func main() { } func createRequest(args []string) (cmds.Request, *cmds.Command) { - req, root, err := cmdsCli.Parse(args, Root, commands.Root) + req, root, cmd, err := cmdsCli.Parse(args, Root, commands.Root) if err != nil { fmt.Println(err) - fmt.Println(Root.Help) + if cmd != nil { + if cmd.Help != "" { + fmt.Println(cmd.Help) + } + } else { + fmt.Println(Root.Help) + } os.Exit(1) } From 1ee6c7e5f256424ca954897df1204ecebf84533b Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 4 Nov 2014 23:37:51 -0800 Subject: [PATCH 120/383] commands/cli: Fixed arg parse bug --- commands/cli/parse.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index 2a7be8341..a26b4b78f 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -145,6 +145,7 @@ func parseArgs(stringArgs []string, cmd *cmds.Command) ([]interface{}, error) { if err != nil { return nil, err } + j++ } } else { var err error @@ -152,9 +153,8 @@ func parseArgs(stringArgs []string, cmd *cmds.Command) ([]interface{}, error) { if err != nil { return nil, err } + j++ } - - j++ } if len(stringArgs)-j > 0 { From fecb434ab4577695366ce3a1488ccea934c8a314 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 4 Nov 2014 23:39:12 -0800 Subject: [PATCH 121/383] commands: Fixed arg validation bug --- commands/command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/command.go b/commands/command.go index d90aa3f7f..351b249c5 100644 --- a/commands/command.go +++ b/commands/command.go @@ -179,7 +179,7 @@ func (c *Command) CheckArguments(req Request) error { // any additional values are for the variadic arg definition if argDef.Variadic && j < len(args)-1 { - for _, val := range args[j+1:] { + for _, val := range args[j:] { err := checkArgValue(val, argDef) if err != nil { return err From 83cda2e699598d65afa48ac0372e4fc9bea58944 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 4 Nov 2014 23:40:15 -0800 Subject: [PATCH 122/383] commands/http: Fixed arg parse bug --- commands/http/parse.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/commands/http/parse.go b/commands/http/parse.go index 59e626bca..b23240739 100644 --- a/commands/http/parse.go +++ b/commands/http/parse.go @@ -61,17 +61,18 @@ func Parse(r *http.Request, root *cmds.Command) (cmds.Request, error) { if valCount <= lenRequired && !argDef.Required { continue } - valCount-- if argDef.Type == cmds.ArgString { if argDef.Variadic { for _, s := range stringArgs { args = append(args, s) } + valCount -= len(stringArgs) } else if len(stringArgs) > 0 { args = append(args, stringArgs[0]) stringArgs = stringArgs[1:] + valCount-- } else { break From 4765a1d660e8e2256b78cfc70f9f7f42af192daa Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 4 Nov 2014 23:41:45 -0800 Subject: [PATCH 123/383] core/commands2: Made 'cat' require object argument --- core/commands2/cat.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/commands2/cat.go b/core/commands2/cat.go index d3b0fdf0f..36416f33c 100644 --- a/core/commands2/cat.go +++ b/core/commands2/cat.go @@ -11,11 +11,11 @@ import ( var catCmd = &cmds.Command{ Arguments: []cmds.Argument{ - cmds.Argument{"object", cmds.ArgString, false, true}, + cmds.Argument{"object", cmds.ArgString, true, true}, }, - Help: `ipfs cat - Show ipfs object data. + Help: `ipfs cat - Show ipfs object data. - Retrieves the object named by and displays the Data + Retrieves the object named by and outputs the data it contains. `, Run: func(res cmds.Response, req cmds.Request) { From 79b88ee028794436153165bf0a763d5cb240bdee Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 4 Nov 2014 23:44:25 -0800 Subject: [PATCH 124/383] core/commands2: Added argument definitions to 'publish' --- core/commands2/publish.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/core/commands2/publish.go b/core/commands2/publish.go index e1aba25a5..928ba6be9 100644 --- a/core/commands2/publish.go +++ b/core/commands2/publish.go @@ -11,12 +11,16 @@ import ( u "github.com/jbenet/go-ipfs/util" ) -type PublishOutput struct { +type IpnsEntry struct { Name string Value string } var publishCmd = &cmds.Command{ + Arguments: []cmds.Argument{ + cmds.Argument{"name", cmds.ArgString, false, false}, + cmds.Argument{"object", cmds.ArgString, true, false}, + }, Help: "TODO", Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node @@ -39,9 +43,6 @@ var publishCmd = &cmds.Command{ case 1: // name = n.Identity.ID.String() ref = args[0].(string) - - default: - res.SetError(fmt.Errorf("Publish expects 1 or 2 args; got %d.", len(args)), cmds.ErrClient) } // TODO n.Keychain.Get(name).PrivKey @@ -56,15 +57,15 @@ var publishCmd = &cmds.Command{ }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ cmds.Text: func(res cmds.Response) ([]byte, error) { - v := res.Output().(*PublishOutput) + v := res.Output().(*IpnsEntry) s := fmt.Sprintf("Published name %s to %s\n", v.Name, v.Value) return []byte(s), nil }, }, - Type: &PublishOutput{}, + Type: &IpnsEntry{}, } -func publish(n *core.IpfsNode, k crypto.PrivKey, ref string) (*PublishOutput, error) { +func publish(n *core.IpfsNode, k crypto.PrivKey, ref string) (*IpnsEntry, error) { pub := nsys.NewRoutingPublisher(n.Routing) err := pub.Publish(k, ref) if err != nil { @@ -76,7 +77,7 @@ func publish(n *core.IpfsNode, k crypto.PrivKey, ref string) (*PublishOutput, er return nil, err } - return &PublishOutput{ + return &IpnsEntry{ Name: u.Key(hash).String(), Value: ref, }, nil From 61e8ad999021a7a7d4640bf06541fca4901a9371 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 4 Nov 2014 23:45:14 -0800 Subject: [PATCH 125/383] core/commands2: Added 'diag' command --- core/commands2/diag.go | 79 ++++++++++++++++++++++++++++++++++++++++++ core/commands2/root.go | 1 + 2 files changed, 80 insertions(+) create mode 100644 core/commands2/diag.go diff --git a/core/commands2/diag.go b/core/commands2/diag.go new file mode 100644 index 000000000..197cfd130 --- /dev/null +++ b/core/commands2/diag.go @@ -0,0 +1,79 @@ +package commands + +import ( + "errors" + "fmt" + "io" + "time" + + cmds "github.com/jbenet/go-ipfs/commands" + diagn "github.com/jbenet/go-ipfs/diagnostics" +) + +type DiagnosticConnection struct { + ID string + Latency int64 +} + +type DiagnosticPeer struct { + ID string + LifeSpan float64 + BandwidthIn uint64 + BandwidthOut uint64 + Connections []DiagnosticConnection +} + +type DiagnosticOutput struct { + Peers []DiagnosticPeer +} + +var diagCmd = &cmds.Command{ + Run: func(res cmds.Response, req cmds.Request) { + n := req.Context().Node + + if n.Diagnostics == nil { + res.SetError(errors.New("Cannot run diagnostic in offline mode!"), cmds.ErrNormal) + return + } + + info, err := n.Diagnostics.GetDiagnostic(time.Second * 20) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + output := make([]DiagnosticPeer, len(info)) + for i, peer := range info { + connections := make([]DiagnosticConnection, len(peer.Connections)) + for j, conn := range peer.Connections { + connections[j] = DiagnosticConnection{ + ID: conn.ID, + Latency: conn.Latency.Nanoseconds(), + } + } + + output[i] = DiagnosticPeer{ + ID: peer.ID, + LifeSpan: peer.LifeSpan.Minutes(), + BandwidthIn: peer.BwIn, + BandwidthOut: peer.BwOut, + Connections: connections, + } + } + + res.SetOutput(&DiagnosticOutput{output}) + }, + Type: &DiagnosticOutput{}, +} + +func PrintDiagnostics(info []*diagn.DiagInfo, out io.Writer) { + for _, i := range info { + fmt.Fprintf(out, "Peer: %s\n", i.ID) + fmt.Fprintf(out, "\tUp for: %s\n", i.LifeSpan.String()) + fmt.Fprintf(out, "\tConnected To:\n") + for _, c := range i.Connections { + fmt.Fprintf(out, "\t%s\n\t\tLatency = %s\n", c.ID, c.Latency.String()) + } + fmt.Fprintln(out) + } +} diff --git a/core/commands2/root.go b/core/commands2/root.go index ee99e2ceb..fa8d65665 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -62,6 +62,7 @@ var rootSubcommands = map[string]*cmds.Command{ "publish": publishCmd, "add": addCmd, "log": logCmd, + "diag": diagCmd, // test subcommands // TODO: remove these when we don't need them anymore From 109af013966464a1a36f4adb3fd43e6e8620e217 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 4 Nov 2014 23:45:44 -0800 Subject: [PATCH 126/383] core/commands2: Added 'pin' and 'unpin' commands --- core/commands2/pin.go | 109 +++++++++++++++++++++++++++++++++++++++++ core/commands2/root.go | 2 + 2 files changed, 111 insertions(+) create mode 100644 core/commands2/pin.go diff --git a/core/commands2/pin.go b/core/commands2/pin.go new file mode 100644 index 000000000..eb362a624 --- /dev/null +++ b/core/commands2/pin.go @@ -0,0 +1,109 @@ +package commands + +import ( + "errors" + "fmt" + + cmds "github.com/jbenet/go-ipfs/commands" +) + +var pinCmd = &cmds.Command{ + Options: []cmds.Option{ + cmds.Option{[]string{"recursive", "r"}, cmds.Bool}, + cmds.Option{[]string{"depth", "d"}, cmds.Uint}, + }, + Arguments: []cmds.Argument{ + cmds.Argument{"object", cmds.ArgString, true, true}, + }, + Run: func(res cmds.Response, req cmds.Request) { + n := req.Context().Node + + // set recursive flag + opt, _ := req.Option("recursive") + recursive, _ := opt.(bool) // false if cast fails. + + /*depth := 1 // default (non recursive) + + // if recursive, set depth flag + if recursive { + opt, found := req.Option("depth") + if d, ok := opt.(int); found && ok { + depth = d + } else { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + }*/ + + for _, arg := range req.Arguments() { + path, ok := arg.(string) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + + dagnode, err := n.Resolver.ResolvePath(path) + if err != nil { + res.SetError(fmt.Errorf("pin error: %v", err), cmds.ErrNormal) + return + } + + err = n.Pinning.Pin(dagnode, recursive) + if err != nil { + res.SetError(fmt.Errorf("pin: %v", err), cmds.ErrNormal) + return + } + } + + err := n.Pinning.Flush() + if err != nil { + res.SetError(err, cmds.ErrNormal) + } + + // TODO: create some output to show what got pinned + }, +} + +var unpinCmd = &cmds.Command{ + Options: []cmds.Option{ + cmds.Option{[]string{"recursive", "r"}, cmds.Bool}, + }, + Arguments: []cmds.Argument{ + cmds.Argument{"object", cmds.ArgString, true, true}, + }, + Run: func(res cmds.Response, req cmds.Request) { + n := req.Context().Node + + // set recursive flag + opt, _ := req.Option("recursive") + recursive, _ := opt.(bool) // false if cast fails. + + for _, arg := range req.Arguments() { + path, ok := arg.(string) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + + dagnode, err := n.Resolver.ResolvePath(path) + if err != nil { + res.SetError(fmt.Errorf("pin error: %v", err), cmds.ErrNormal) + return + } + + k, _ := dagnode.Key() + err = n.Pinning.Unpin(k, recursive) + if err != nil { + res.SetError(fmt.Errorf("pin: %v", err), cmds.ErrNormal) + return + } + } + + err := n.Pinning.Flush() + if err != nil { + res.SetError(err, cmds.ErrNormal) + } + + // TODO: create some output to show what got unpinned + }, +} diff --git a/core/commands2/root.go b/core/commands2/root.go index fa8d65665..52563e708 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -63,6 +63,8 @@ var rootSubcommands = map[string]*cmds.Command{ "add": addCmd, "log": logCmd, "diag": diagCmd, + "pin": pinCmd, + "unpin": unpinCmd, // test subcommands // TODO: remove these when we don't need them anymore From bbca4452982b785933ed924b78b82c5cbb2b8a9d Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 4 Nov 2014 23:46:08 -0800 Subject: [PATCH 127/383] core/commands2: Added 'resolve' command --- core/commands2/resolve.go | 74 +++++++++++++++++++++++++++++++++++++++ core/commands2/root.go | 1 + 2 files changed, 75 insertions(+) create mode 100644 core/commands2/resolve.go diff --git a/core/commands2/resolve.go b/core/commands2/resolve.go new file mode 100644 index 000000000..ac85d583f --- /dev/null +++ b/core/commands2/resolve.go @@ -0,0 +1,74 @@ +package commands + +import ( + "errors" + + cmds "github.com/jbenet/go-ipfs/commands" + "github.com/jbenet/go-ipfs/core" +) + +type ResolveOutput struct { + Entries []IpnsEntry +} + +var resolveCmd = &cmds.Command{ + Arguments: []cmds.Argument{ + cmds.Argument{"name", cmds.ArgString, false, true}, + }, + Run: func(res cmds.Response, req cmds.Request) { + name := "" + args := req.Arguments() + n := req.Context().Node + var output []IpnsEntry + + if len(args) == 0 { + if n.Identity == nil { + res.SetError(errors.New("Identity not loaded!"), cmds.ErrNormal) + return + } + + name = n.Identity.ID().String() + entry, err := resolve(name, n) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + output = []IpnsEntry{entry} + + } else { + output = make([]IpnsEntry, len(args)) + + for i, arg := range args { + var ok bool + name, ok = arg.(string) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + + entry, err := resolve(name, n) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + output[i] = entry + } + } + + res.SetOutput(&ResolveOutput{output}) + }, + Type: &ResolveOutput{}, +} + +func resolve(name string, n *core.IpfsNode) (IpnsEntry, error) { + resolved, err := n.Namesys.Resolve(name) + if err != nil { + return IpnsEntry{}, err + } + + return IpnsEntry{ + Name: name, + Value: resolved, + }, nil +} diff --git a/core/commands2/root.go b/core/commands2/root.go index 52563e708..60da85cb7 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -65,6 +65,7 @@ var rootSubcommands = map[string]*cmds.Command{ "diag": diagCmd, "pin": pinCmd, "unpin": unpinCmd, + "resolve": resolveCmd, // test subcommands // TODO: remove these when we don't need them anymore From a7accecc8ebfaaf0cadc1893ec62c4341354f59b Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 5 Nov 2014 00:51:17 -0800 Subject: [PATCH 128/383] core/commands2: Moved 'resolve' and 'publish' into subcommands of 'name' --- core/commands2/{publish.go => name.go} | 74 ++++++++++++++++++++++++++ core/commands2/resolve.go | 74 -------------------------- core/commands2/root.go | 3 +- 3 files changed, 75 insertions(+), 76 deletions(-) rename core/commands2/{publish.go => name.go} (56%) delete mode 100644 core/commands2/resolve.go diff --git a/core/commands2/publish.go b/core/commands2/name.go similarity index 56% rename from core/commands2/publish.go rename to core/commands2/name.go index 928ba6be9..4b11e622f 100644 --- a/core/commands2/publish.go +++ b/core/commands2/name.go @@ -16,6 +16,18 @@ type IpnsEntry struct { Value string } +type ResolveOutput struct { + Entries []IpnsEntry +} + +var nameCmd = &cmds.Command{ + Help: "TODO", + Subcommands: map[string]*cmds.Command{ + "publish": publishCmd, + "resolve": resolveCmd, + }, +} + var publishCmd = &cmds.Command{ Arguments: []cmds.Argument{ cmds.Argument{"name", cmds.ArgString, false, false}, @@ -65,6 +77,56 @@ var publishCmd = &cmds.Command{ Type: &IpnsEntry{}, } +var resolveCmd = &cmds.Command{ + Arguments: []cmds.Argument{ + cmds.Argument{"name", cmds.ArgString, false, true}, + }, + Run: func(res cmds.Response, req cmds.Request) { + name := "" + args := req.Arguments() + n := req.Context().Node + var output []IpnsEntry + + if len(args) == 0 { + if n.Identity == nil { + res.SetError(errors.New("Identity not loaded!"), cmds.ErrNormal) + return + } + + name = n.Identity.ID().String() + entry, err := resolve(name, n) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + output = []IpnsEntry{entry} + + } else { + output = make([]IpnsEntry, len(args)) + + for i, arg := range args { + var ok bool + name, ok = arg.(string) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + + entry, err := resolve(name, n) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + output[i] = entry + } + } + + res.SetOutput(&ResolveOutput{output}) + }, + Type: &ResolveOutput{}, +} + func publish(n *core.IpfsNode, k crypto.PrivKey, ref string) (*IpnsEntry, error) { pub := nsys.NewRoutingPublisher(n.Routing) err := pub.Publish(k, ref) @@ -82,3 +144,15 @@ func publish(n *core.IpfsNode, k crypto.PrivKey, ref string) (*IpnsEntry, error) Value: ref, }, nil } + +func resolve(name string, n *core.IpfsNode) (IpnsEntry, error) { + resolved, err := n.Namesys.Resolve(name) + if err != nil { + return IpnsEntry{}, err + } + + return IpnsEntry{ + Name: name, + Value: resolved, + }, nil +} diff --git a/core/commands2/resolve.go b/core/commands2/resolve.go deleted file mode 100644 index ac85d583f..000000000 --- a/core/commands2/resolve.go +++ /dev/null @@ -1,74 +0,0 @@ -package commands - -import ( - "errors" - - cmds "github.com/jbenet/go-ipfs/commands" - "github.com/jbenet/go-ipfs/core" -) - -type ResolveOutput struct { - Entries []IpnsEntry -} - -var resolveCmd = &cmds.Command{ - Arguments: []cmds.Argument{ - cmds.Argument{"name", cmds.ArgString, false, true}, - }, - Run: func(res cmds.Response, req cmds.Request) { - name := "" - args := req.Arguments() - n := req.Context().Node - var output []IpnsEntry - - if len(args) == 0 { - if n.Identity == nil { - res.SetError(errors.New("Identity not loaded!"), cmds.ErrNormal) - return - } - - name = n.Identity.ID().String() - entry, err := resolve(name, n) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - output = []IpnsEntry{entry} - - } else { - output = make([]IpnsEntry, len(args)) - - for i, arg := range args { - var ok bool - name, ok = arg.(string) - if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return - } - - entry, err := resolve(name, n) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - output[i] = entry - } - } - - res.SetOutput(&ResolveOutput{output}) - }, - Type: &ResolveOutput{}, -} - -func resolve(name string, n *core.IpfsNode) (IpnsEntry, error) { - resolved, err := n.Namesys.Resolve(name) - if err != nil { - return IpnsEntry{}, err - } - - return IpnsEntry{ - Name: name, - Value: resolved, - }, nil -} diff --git a/core/commands2/root.go b/core/commands2/root.go index 60da85cb7..7414aad50 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -59,13 +59,12 @@ var rootSubcommands = map[string]*cmds.Command{ "cat": catCmd, "ls": lsCmd, "commands": commandsCmd, - "publish": publishCmd, + "name": nameCmd, "add": addCmd, "log": logCmd, "diag": diagCmd, "pin": pinCmd, "unpin": unpinCmd, - "resolve": resolveCmd, // test subcommands // TODO: remove these when we don't need them anymore From 51c6a1c5529d7ba8d625c649a20becd7e6492475 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 5 Nov 2014 01:34:01 -0800 Subject: [PATCH 129/383] refactor(resolve) separate argument marshalling from the operation @mappum see how the unpacking of arguments happens separately from the resolve loop? It's a bit more verbose, but much clearer. However, doing two different things in one loop is less clear than doing them separately. It also causes problems for further refactoring as it introduces temps that get in the way of further refactorings. Plus, there will be 50+ commands, so it's important that we stay framework agnostic as much as possible. So, this is the style we prefer. It'll keep us nimble in the long run. --- core/commands2/name.go | 64 ++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/core/commands2/name.go b/core/commands2/name.go index 4b11e622f..f6543bc86 100644 --- a/core/commands2/name.go +++ b/core/commands2/name.go @@ -82,47 +82,34 @@ var resolveCmd = &cmds.Command{ cmds.Argument{"name", cmds.ArgString, false, true}, }, Run: func(res cmds.Response, req cmds.Request) { - name := "" - args := req.Arguments() - n := req.Context().Node - var output []IpnsEntry - if len(args) == 0 { + n := req.Context().Node + var names []string + + if len(req.Arguments()) == 0 { if n.Identity == nil { res.SetError(errors.New("Identity not loaded!"), cmds.ErrNormal) return } - - name = n.Identity.ID().String() - entry, err := resolve(name, n) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - output = []IpnsEntry{entry} - + names = append(names, n.Identity.ID().String()) } else { - output = make([]IpnsEntry, len(args)) - - for i, arg := range args { - var ok bool - name, ok = arg.(string) + for _, arg := range req.Arguments() { + name, ok := arg.(string) if !ok { res.SetError(errors.New("cast error"), cmds.ErrNormal) return } - - entry, err := resolve(name, n) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - output[i] = entry + names = append(names, name) } } - res.SetOutput(&ResolveOutput{output}) + entries, err := resolve(n, names) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(&ResolveOutput{entries}) }, Type: &ResolveOutput{}, } @@ -145,14 +132,17 @@ func publish(n *core.IpfsNode, k crypto.PrivKey, ref string) (*IpnsEntry, error) }, nil } -func resolve(name string, n *core.IpfsNode) (IpnsEntry, error) { - resolved, err := n.Namesys.Resolve(name) - if err != nil { - return IpnsEntry{}, err +func resolve(n *core.IpfsNode, names []string) ([]IpnsEntry, error) { + var entries []IpnsEntry + for _, name := range names { + resolved, err := n.Namesys.Resolve(name) + if err != nil { + return nil, err + } + entries = append(entries, IpnsEntry{ + Name: name, + Value: resolved, + }) } - - return IpnsEntry{ - Name: name, - Value: resolved, - }, nil + return entries, nil } From af65eec2fc924851e1c5019c0f43f89610b64010 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 5 Nov 2014 01:58:21 -0800 Subject: [PATCH 130/383] core/commands2: Output error when trying to run 'publish' or 'resolve' in offline mode --- core/commands2/name.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/commands2/name.go b/core/commands2/name.go index f6543bc86..4a9bc7997 100644 --- a/core/commands2/name.go +++ b/core/commands2/name.go @@ -20,6 +20,8 @@ type ResolveOutput struct { Entries []IpnsEntry } +var errNotOnline = errors.New("This command must be run in online mode. Try running 'ipfs daemon' first.") + var nameCmd = &cmds.Command{ Help: "TODO", Subcommands: map[string]*cmds.Command{ @@ -38,6 +40,11 @@ var publishCmd = &cmds.Command{ n := req.Context().Node args := req.Arguments() + if n.Network == nil { + res.SetError(errNotOnline, cmds.ErrNormal) + return + } + if n.Identity == nil { res.SetError(errors.New("Identity not loaded!"), cmds.ErrNormal) return @@ -86,6 +93,11 @@ var resolveCmd = &cmds.Command{ n := req.Context().Node var names []string + if n.Network == nil { + res.SetError(errNotOnline, cmds.ErrNormal) + return + } + if len(req.Arguments()) == 0 { if n.Identity == nil { res.SetError(errors.New("Identity not loaded!"), cmds.ErrNormal) From b6aad53d86a5b1971e3816dcd5d22f9e8315df49 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 5 Nov 2014 02:17:10 -0800 Subject: [PATCH 131/383] core/commands2: Added 'version' command --- core/commands2/root.go | 1 + core/commands2/version.go | 46 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 core/commands2/version.go diff --git a/core/commands2/root.go b/core/commands2/root.go index 7414aad50..a17210d67 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -65,6 +65,7 @@ var rootSubcommands = map[string]*cmds.Command{ "diag": diagCmd, "pin": pinCmd, "unpin": unpinCmd, + "version": versionCmd, // test subcommands // TODO: remove these when we don't need them anymore diff --git a/core/commands2/version.go b/core/commands2/version.go new file mode 100644 index 000000000..019c6959b --- /dev/null +++ b/core/commands2/version.go @@ -0,0 +1,46 @@ +package commands + +import ( + "errors" + + cmds "github.com/jbenet/go-ipfs/commands" + config "github.com/jbenet/go-ipfs/config" +) + +type VersionOutput struct { + Version string +} + +var versionCmd = &cmds.Command{ + Options: []cmds.Option{ + cmds.Option{[]string{"number", "n"}, cmds.Bool}, + }, + Help: `ipfs version - Show ipfs version information. + + Returns the current version of ipfs and exits. + `, + Run: func(res cmds.Response, req cmds.Request) { + res.SetOutput(&VersionOutput{ + Version: config.CurrentVersionNumber, + }) + }, + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + v := res.Output().(*VersionOutput) + s := "" + + opt, found := res.Request().Option("number") + number, ok := opt.(bool) + if found && !ok { + return nil, errors.New("cast error") + } + + if !number { + s += "ipfs version " + } + s += v.Version + return []byte(s), nil + }, + }, + Type: &VersionOutput{}, +} From cb72868ab4775dc619717bed33cd025b2fd52862 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 5 Nov 2014 16:22:12 -0800 Subject: [PATCH 132/383] commands: Fixed parser argument bug (TODO: better test coverage for command parsers) --- commands/cli/parse.go | 2 ++ commands/http/parse.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index a26b4b78f..8972fa96d 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -132,6 +132,8 @@ func parseArgs(stringArgs []string, cmd *cmds.Command) ([]interface{}, error) { // skip optional argument definitions if there aren't sufficient remaining values if len(stringArgs)-j <= lenRequired && !argDef.Required { continue + } else if argDef.Required { + lenRequired-- } if j >= len(stringArgs) { diff --git a/commands/http/parse.go b/commands/http/parse.go index b23240739..979bacb78 100644 --- a/commands/http/parse.go +++ b/commands/http/parse.go @@ -60,6 +60,8 @@ func Parse(r *http.Request, root *cmds.Command) (cmds.Request, error) { // skip optional argument definitions if there aren't sufficient remaining values if valCount <= lenRequired && !argDef.Required { continue + } else if argDef.Required { + lenRequired-- } if argDef.Type == cmds.ArgString { From 7228c8fe486b539ab3ba7961c22d6639093ad7b6 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 5 Nov 2014 16:22:47 -0800 Subject: [PATCH 133/383] core/commands2: Added 'config' command --- core/commands2/config.go | 188 +++++++++++++++++++++++++++++++++++++++ core/commands2/root.go | 1 + 2 files changed, 189 insertions(+) create mode 100644 core/commands2/config.go diff --git a/core/commands2/config.go b/core/commands2/config.go new file mode 100644 index 000000000..1eaf2332d --- /dev/null +++ b/core/commands2/config.go @@ -0,0 +1,188 @@ +package commands + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "os" + "os/exec" + + cmds "github.com/jbenet/go-ipfs/commands" + config "github.com/jbenet/go-ipfs/config" +) + +type ConfigField struct { + Key string + Value interface{} +} + +var configCmd = &cmds.Command{ + Arguments: []cmds.Argument{ + cmds.Argument{"key", cmds.ArgString, true, false}, + cmds.Argument{"value", cmds.ArgString, false, false}, + }, + Help: `ipfs config [value] - Get/Set ipfs config values. + + ipfs config - Get value of + ipfs config - Set value of to + ipfs config show - Show config file + ipfs config edit - Edit config file in $EDITOR + +Examples: + + Get the value of the 'datastore.path' key: + + ipfs config datastore.path + + Set the value of the 'datastore.path' key: + + ipfs config datastore.path ~/.go-ipfs/datastore + +`, + Run: func(res cmds.Response, req cmds.Request) { + args := req.Arguments() + + key, ok := args[0].(string) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + + filename, err := config.Filename(req.Context().ConfigRoot) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + var value string + if len(args) == 2 { + var ok bool + value, ok = args[1].(string) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + + field, err := setConfig(filename, key, value) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(field) + return + + } else { + field, err := getConfig(filename, key) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(field) + return + } + }, + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + v := res.Output().(*ConfigField) + + s := "" + if len(res.Request().Arguments()) == 2 { + s += fmt.Sprintf("'%s' set to: ", v.Key) + } + + marshalled, err := json.Marshal(v.Value) + if err != nil { + return nil, err + } + s += fmt.Sprintf("%s\n", marshalled) + + return []byte(s), nil + }, + }, + Type: &ConfigField{}, + Subcommands: map[string]*cmds.Command{ + "show": configShowCmd, + "edit": configEditCmd, + }, +} + +var configShowCmd = &cmds.Command{ + Run: func(res cmds.Response, req cmds.Request) { + filename, err := config.Filename(req.Context().ConfigRoot) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + reader, err := showConfig(filename) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(reader) + }, +} + +var configEditCmd = &cmds.Command{ + Run: func(res cmds.Response, req cmds.Request) { + filename, err := config.Filename(req.Context().ConfigRoot) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + err = editConfig(filename) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + }, +} + +func getConfig(filename string, key string) (*ConfigField, error) { + value, err := config.ReadConfigKey(filename, key) + if err != nil { + return nil, fmt.Errorf("Failed to get config value: %s", err) + } + + return &ConfigField{ + Key: key, + Value: value, + }, nil +} + +func setConfig(filename string, key, value string) (*ConfigField, error) { + err := config.WriteConfigKey(filename, key, value) + if err != nil { + return nil, fmt.Errorf("Failed to set config value: %s", err) + } + + return getConfig(filename, key) +} + +func showConfig(filename string) (io.Reader, error) { + // MAYBE_TODO: maybe we should omit privkey so we don't accidentally leak it? + + file, err := os.Open(filename) + if err != nil { + return nil, err + } + //defer file.Close() + + return file, nil +} + +func editConfig(filename string) error { + editor := os.Getenv("EDITOR") + if editor == "" { + return errors.New("ENV variable $EDITOR not set") + } + + cmd := exec.Command("sh", "-c", editor+" "+filename) + cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr + return cmd.Run() +} diff --git a/core/commands2/root.go b/core/commands2/root.go index a17210d67..9238832d3 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -66,6 +66,7 @@ var rootSubcommands = map[string]*cmds.Command{ "pin": pinCmd, "unpin": unpinCmd, "version": versionCmd, + "config": configCmd, // test subcommands // TODO: remove these when we don't need them anymore From 16c584ce93aea721527eb310c524054cbd1c2ab5 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 5 Nov 2014 22:22:53 -0800 Subject: [PATCH 134/383] core/commands2: Added 'bootstrap' command --- core/commands2/bootstrap.go | 252 ++++++++++++++++++++++++++++++++++++ core/commands2/root.go | 23 ++-- 2 files changed, 264 insertions(+), 11 deletions(-) create mode 100644 core/commands2/bootstrap.go diff --git a/core/commands2/bootstrap.go b/core/commands2/bootstrap.go new file mode 100644 index 000000000..2a5782f14 --- /dev/null +++ b/core/commands2/bootstrap.go @@ -0,0 +1,252 @@ +package commands + +import ( + "errors" + "fmt" + "strings" + + ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + + cmds "github.com/jbenet/go-ipfs/commands" + config "github.com/jbenet/go-ipfs/config" + //peer "github.com/jbenet/go-ipfs/peer" + //u "github.com/jbenet/go-ipfs/util" +) + +type BootstrapOutput struct { + Peers []*config.BootstrapPeer +} + +var bootstrapCmd = &cmds.Command{ + Help: `ipfs bootstrap - show, or manipulate bootstrap node addresses + +Running 'ipfs bootstrap' with no arguments will run 'ipfs bootstrap list'. + +Commands: + + list Show the boostrap list. + add
Add a node's address to the bootstrap list. + remove
Remove an address from the bootstrap list. + +` + bootstrapSecurityWarning, + Run: bootstrapListCmd.Run, + Marshallers: bootstrapListCmd.Marshallers, + Subcommands: map[string]*cmds.Command{ + "list": bootstrapListCmd, + "add": bootstrapAddCmd, + "remove": bootstrapRemoveCmd, + }, +} + +var bootstrapAddCmd = &cmds.Command{ + Arguments: []cmds.Argument{ + cmds.Argument{"peer", cmds.ArgString, true, true}, + }, + Help: `ipfs bootstrap add - add addresses to the bootstrap list +` + bootstrapSecurityWarning, + Run: func(res cmds.Response, req cmds.Request) { + input, err := bootstrapInputToPeers(req.Arguments()) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + filename, err := config.Filename(req.Context().ConfigRoot) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + added, err := bootstrapAdd(filename, req.Context().Config, input) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(&BootstrapOutput{added}) + }, + Type: &BootstrapOutput{}, + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + v := res.Output().(*BootstrapOutput) + s := fmt.Sprintf("Added %v peers to the bootstrap list:\n", len(v.Peers)) + marshalled, err := bootstrapMarshaller(res) + if err != nil { + return nil, err + } + return append([]byte(s), marshalled...), nil + }, + }, +} + +var bootstrapRemoveCmd = &cmds.Command{ + Arguments: []cmds.Argument{ + cmds.Argument{"peer", cmds.ArgString, true, true}, + }, + Help: `ipfs bootstrap remove - remove addresses from the bootstrap list +` + bootstrapSecurityWarning, + Run: func(res cmds.Response, req cmds.Request) { + input, err := bootstrapInputToPeers(req.Arguments()) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + filename, err := config.Filename(req.Context().ConfigRoot) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + removed, err := bootstrapRemove(filename, req.Context().Config, input) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(&BootstrapOutput{removed}) + }, + Type: &BootstrapOutput{}, + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + v := res.Output().(*BootstrapOutput) + s := fmt.Sprintf("Removed %v peers from the bootstrap list:\n", len(v.Peers)) + marshalled, err := bootstrapMarshaller(res) + if err != nil { + return nil, err + } + return append([]byte(s), marshalled...), nil + }, + }, +} + +var bootstrapListCmd = &cmds.Command{ + Help: "ipfs bootstrap list - Show addresses in the bootstrap list", + Run: func(res cmds.Response, req cmds.Request) { + peers := req.Context().Config.Bootstrap + res.SetOutput(&BootstrapOutput{peers}) + }, + Type: &BootstrapOutput{}, + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: bootstrapMarshaller, + }, +} + +func bootstrapMarshaller(res cmds.Response) ([]byte, error) { + v := res.Output().(*BootstrapOutput) + + s := "" + for _, peer := range v.Peers { + s += fmt.Sprintf("%s/%s\n", peer.Address, peer.PeerID) + } + + return []byte(s), nil +} + +func bootstrapInputToPeers(input []interface{}) ([]*config.BootstrapPeer, error) { + split := func(addr string) (string, string) { + idx := strings.LastIndex(addr, "/") + if idx == -1 { + return "", addr + } + return addr[:idx], addr[idx+1:] + } + + peers := []*config.BootstrapPeer{} + for _, v := range input { + addr, ok := v.(string) + if !ok { + return nil, errors.New("cast error") + } + + addrS, peeridS := split(addr) + + // make sure addrS parses as a multiaddr. + if len(addrS) > 0 { + maddr, err := ma.NewMultiaddr(addrS) + if err != nil { + return nil, err + } + + addrS = maddr.String() + } + + // make sure idS parses as a peer.ID + _, err := mh.FromB58String(peeridS) + if err != nil { + return nil, err + } + + // construct config entry + peers = append(peers, &config.BootstrapPeer{ + Address: addrS, + PeerID: peeridS, + }) + } + return peers, nil +} + +func bootstrapAdd(filename string, cfg *config.Config, peers []*config.BootstrapPeer) ([]*config.BootstrapPeer, error) { + added := make([]*config.BootstrapPeer, 0, len(peers)) + + for _, peer := range peers { + duplicate := false + for _, peer2 := range cfg.Bootstrap { + if peer.Address == peer2.Address { + duplicate = true + break + } + } + + if !duplicate { + cfg.Bootstrap = append(cfg.Bootstrap, peer) + added = append(added, peer) + } + } + + err := config.WriteConfigFile(filename, cfg) + if err != nil { + return nil, err + } + + return added, nil +} + +func bootstrapRemove(filename string, cfg *config.Config, peers []*config.BootstrapPeer) ([]*config.BootstrapPeer, error) { + removed := make([]*config.BootstrapPeer, 0, len(peers)) + keep := make([]*config.BootstrapPeer, 0, len(cfg.Bootstrap)) + + for _, peer := range cfg.Bootstrap { + found := false + for _, peer2 := range peers { + if peer.Address == peer2.Address && peer.PeerID == peer2.PeerID { + found = true + removed = append(removed, peer) + break + } + } + + if !found { + keep = append(keep, peer) + } + } + cfg.Bootstrap = keep + + err := config.WriteConfigFile(filename, cfg) + if err != nil { + return nil, err + } + + return removed, nil +} + +const bootstrapSecurityWarning = ` +SECURITY WARNING: + +The bootstrap command manipulates the "bootstrap list", which contains +the addresses of bootstrap nodes. These are the *trusted peers* from +which to learn about other peers in the network. Only edit this list +if you understand the risks of adding or removing nodes from this list. + +` diff --git a/core/commands2/root.go b/core/commands2/root.go index 9238832d3..d55afe286 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -56,17 +56,18 @@ Use "ipfs help " for more information about a command. } var rootSubcommands = map[string]*cmds.Command{ - "cat": catCmd, - "ls": lsCmd, - "commands": commandsCmd, - "name": nameCmd, - "add": addCmd, - "log": logCmd, - "diag": diagCmd, - "pin": pinCmd, - "unpin": unpinCmd, - "version": versionCmd, - "config": configCmd, + "cat": catCmd, + "ls": lsCmd, + "commands": commandsCmd, + "name": nameCmd, + "add": addCmd, + "log": logCmd, + "diag": diagCmd, + "pin": pinCmd, + "unpin": unpinCmd, + "version": versionCmd, + "config": configCmd, + "bootstrap": bootstrapCmd, // test subcommands // TODO: remove these when we don't need them anymore From 1c5979e549a647a9be71f878244b6b6d6c9a44da Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 6 Nov 2014 00:36:49 -0800 Subject: [PATCH 135/383] core/commands2: Added 'mount' command --- core/commands2/mount_darwin.go | 33 +++++++++ core/commands2/mount_unix.go | 117 ++++++++++++++++++++++++++++++++ core/commands2/mount_windows.go | 14 ++++ core/commands2/root.go | 1 + 4 files changed, 165 insertions(+) create mode 100644 core/commands2/mount_darwin.go create mode 100644 core/commands2/mount_unix.go create mode 100644 core/commands2/mount_windows.go diff --git a/core/commands2/mount_darwin.go b/core/commands2/mount_darwin.go new file mode 100644 index 000000000..1b50f3173 --- /dev/null +++ b/core/commands2/mount_darwin.go @@ -0,0 +1,33 @@ +package commands + +import ( + "fmt" + "runtime" + "strings" + "syscall" +) + +func init() { + // this is a hack, but until we need to do it another way, this works. + platformFuseChecks = darwinFuseCheckVersion +} + +func darwinFuseCheckVersion() error { + // on OSX, check FUSE version. + if runtime.GOOS != "darwin" { + return nil + } + + ov, err := syscall.Sysctl("osxfuse.version.number") + if err != nil { + return err + } + + if strings.HasPrefix(ov, "2.7.") || strings.HasPrefix(ov, "2.8.") { + return nil + } + + return fmt.Errorf("osxfuse version %s not supported.\n%s\n%s", ov, + "Older versions of osxfuse have kernel panic bugs; please upgrade!", + "https://github.com/jbenet/go-ipfs/issues/177") +} diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go new file mode 100644 index 000000000..e13d7c287 --- /dev/null +++ b/core/commands2/mount_unix.go @@ -0,0 +1,117 @@ +// +build linux darwin freebsd + +package commands + +import ( + "fmt" + "time" + + cmds "github.com/jbenet/go-ipfs/commands" + "github.com/jbenet/go-ipfs/config" + core "github.com/jbenet/go-ipfs/core" + ipns "github.com/jbenet/go-ipfs/fuse/ipns" + rofs "github.com/jbenet/go-ipfs/fuse/readonly" +) + +// amount of time to wait for mount errors +const mountTimeout = time.Second + +var mountCmd = &cmds.Command{ + Options: []cmds.Option{ + cmds.Option{[]string{"f"}, cmds.String}, + cmds.Option{[]string{"n"}, cmds.String}, + }, + Help: `ipfs mount - Mount an ipfs read-only mountpoint. + + Mount ipfs at a read-only mountpoint on the OS. All ipfs objects + will be accessible under that directory. Note that the root will + not be listable, as it is virtual. Accessing known paths directly. +`, + Run: func(res cmds.Response, req cmds.Request) { + ctx := req.Context() + + // error if we aren't running node in online mode + if ctx.Node.Network == nil { + res.SetError(errNotOnline, cmds.ErrNormal) + return + } + + if err := platformFuseChecks(); err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + // update fsdir with flag. + fsdir := ctx.Config.Mounts.IPFS + opt, _ := req.Option("f") + if val, ok := opt.(string); ok && val != "" { + fsdir = val + } + fsdone := mountIpfs(ctx.Node, fsdir) + + // get default mount points + nsdir := ctx.Config.Mounts.IPNS + opt, _ = req.Option("f") + if val, ok := opt.(string); ok && val != "" { + nsdir = val + } + nsdone := mountIpns(ctx.Node, nsdir, fsdir) + + // wait until mounts return an error (or timeout if successful) + var err error + select { + case err = <-fsdone: + case err = <-nsdone: + + // mounted successfully, we timed out with no errors + case <-time.After(mountTimeout): + output := ctx.Config.Mounts + res.SetOutput(&output) + return + } + + res.SetError(err, cmds.ErrNormal) + }, + Type: &config.Mounts{}, + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + v := res.Output().(*config.Mounts) + s := fmt.Sprintf("IPFS mounted at: %s\n", v.IPFS) + s += fmt.Sprintf("IPNS mounted at: %s\n", v.IPNS) + return []byte(s), nil + }, + }, +} + +func mountIpfs(node *core.IpfsNode, fsdir string) <-chan error { + done := make(chan error) + log.Info("Mounting IPFS at ", fsdir) + + go func() { + err := rofs.Mount(node, fsdir) + done <- err + close(done) + }() + + return done +} + +func mountIpns(node *core.IpfsNode, nsdir, fsdir string) <-chan error { + if nsdir == "" { + return nil + } + done := make(chan error) + log.Info("Mounting IPNS at ", nsdir) + + go func() { + err := ipns.Mount(node, nsdir, fsdir) + done <- err + close(done) + }() + + return done +} + +var platformFuseChecks = func() error { + return nil +} diff --git a/core/commands2/mount_windows.go b/core/commands2/mount_windows.go new file mode 100644 index 000000000..2660605c2 --- /dev/null +++ b/core/commands2/mount_windows.go @@ -0,0 +1,14 @@ +package commands + +import ( + "errors" + + cmds "github.com/jbenet/go-ipfs/commands" +) + +var ipfsMount = &cmds.Command{ + Help: `Not yet implemented on Windows.`, + Run: func(res cmds.Response, req cmds.Request) { + res.SetError(errors.New("Mount isn't compatible with Windows yet"), cmds.ErrNormal) + }, +} diff --git a/core/commands2/root.go b/core/commands2/root.go index d55afe286..364717a2b 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -68,6 +68,7 @@ var rootSubcommands = map[string]*cmds.Command{ "version": versionCmd, "config": configCmd, "bootstrap": bootstrapCmd, + "mount": mountCmd, // test subcommands // TODO: remove these when we don't need them anymore From 18cce63ab6952984072a04ed157b5c235287d737 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 6 Nov 2014 01:52:48 -0800 Subject: [PATCH 136/383] core/commands2: Added 'block' command --- core/commands2/block.go | 122 ++++++++++++++++++++++++++++++++++++++++ core/commands2/root.go | 1 + 2 files changed, 123 insertions(+) create mode 100644 core/commands2/block.go diff --git a/core/commands2/block.go b/core/commands2/block.go new file mode 100644 index 000000000..0fd571a3d --- /dev/null +++ b/core/commands2/block.go @@ -0,0 +1,122 @@ +package commands + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "time" + + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" + + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + "github.com/jbenet/go-ipfs/blocks" + cmds "github.com/jbenet/go-ipfs/commands" + u "github.com/jbenet/go-ipfs/util" +) + +type Block struct { + Key string + Length int +} + +var blockCmd = &cmds.Command{ + Help: `ipfs block - manipulate raw ipfs blocks + + ipfs block get - get and output block named by + ipfs block put - store stdin as a block, outputs + +ipfs block is a plumbing command used to manipulate raw ipfs blocks. +Reads from stdin or writes to stdout, and is a base58 encoded +multihash.`, + Subcommands: map[string]*cmds.Command{ + "get": blockGetCmd, + "put": blockPutCmd, + }, +} + +var blockGetCmd = &cmds.Command{ + Arguments: []cmds.Argument{ + cmds.Argument{"key", cmds.ArgString, true, false}, + }, + Help: `ipfs get - gets and outputs block named by + +'ipfs block get' is a plumbing command for retreiving raw ipfs blocks. + is a base58 encoded multihash`, + Run: func(res cmds.Response, req cmds.Request) { + n := req.Context().Node + + key, ok := req.Arguments()[0].(string) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + + if !u.IsValidHash(key) { + res.SetError(errors.New("Not a valid hash"), cmds.ErrClient) + return + } + + h, err := mh.FromB58String(key) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + k := u.Key(h) + ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) + b, err := n.Blocks.GetBlock(ctx, k) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(bytes.NewReader(b.Data)) + }, +} + +var blockPutCmd = &cmds.Command{ + Arguments: []cmds.Argument{ + cmds.Argument{"data", cmds.ArgFile, true, false}, + }, + Help: `ipfs put - stores input as a block, outputs its key + +ipfs block put is a plumbing command for storing raw ipfs blocks.`, + Run: func(res cmds.Response, req cmds.Request) { + n := req.Context().Node + + in, ok := req.Arguments()[0].(io.Reader) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + + data, err := ioutil.ReadAll(in) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + b := blocks.NewBlock(data) + + k, err := n.Blocks.AddBlock(b) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(&Block{ + Key: k.String(), + Length: len(data), + }) + }, + Type: &Block{}, + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + block := res.Output().(*Block) + s := fmt.Sprintf("Block added (%v bytes): %s\n", block.Length, block.Key) + return []byte(s), nil + }, + }, +} diff --git a/core/commands2/root.go b/core/commands2/root.go index 364717a2b..75334e611 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -69,6 +69,7 @@ var rootSubcommands = map[string]*cmds.Command{ "config": configCmd, "bootstrap": bootstrapCmd, "mount": mountCmd, + "block": blockCmd, // test subcommands // TODO: remove these when we don't need them anymore From 8cc1f67f7656002ebf54ad3675c55e85083cd0d1 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 6 Nov 2014 03:13:58 -0800 Subject: [PATCH 137/383] fmt --- core/commands2/add.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/commands2/add.go b/core/commands2/add.go index 3dbce4430..5350dc725 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -29,9 +29,9 @@ var addCmd = &cmds.Command{ n := req.Context().Node // if recursive, set depth to reflect so - //opt, found := req.Option("r") - //if r, _ := opt.(bool); found && r { - //} + // opt, found := req.Option("r") + // if r, _ := opt.(bool); found && r { + // } added := make([]Object, len(req.Arguments())) From e096060b9049441b076246ee71a0f8bb313fc658 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 6 Nov 2014 03:25:07 -0800 Subject: [PATCH 138/383] refactor(core/commands2/add) split loop @mappum --- core/commands2/add.go | 69 +++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/core/commands2/add.go b/core/commands2/add.go index 5350dc725..b8774b047 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -1,12 +1,13 @@ package commands import ( + "errors" "fmt" "io" cmds "github.com/jbenet/go-ipfs/commands" - "github.com/jbenet/go-ipfs/core" - "github.com/jbenet/go-ipfs/importer" + core "github.com/jbenet/go-ipfs/core" + importer "github.com/jbenet/go-ipfs/importer" dag "github.com/jbenet/go-ipfs/merkledag" ) @@ -33,24 +34,31 @@ var addCmd = &cmds.Command{ // if r, _ := opt.(bool); found && r { // } + readers := make([]io.Reader, 0) + for _, arg := range req.Arguments() { + reader, ok := arg.(io.Reader) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + readers = append(readers, reader) + } + + dagnodes, err := add(n, readers) + if err != nil { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + added := make([]Object, len(req.Arguments())) + for _, dagnode := range dagnodes { - // add every path in args - for i, arg := range req.Arguments() { - // Add the file - node, err := add(n, arg.(io.Reader)) + k, err := dagnode.Key() if err != nil { res.SetError(err, cmds.ErrNormal) return } - - k, err := node.Key() - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - added[i] = Object{k.String(), nil} + added = append(added, Object{Hash: k.String(), Links: nil}) } res.SetOutput(&AddOutput{added}) @@ -73,18 +81,27 @@ var addCmd = &cmds.Command{ Type: &AddOutput{}, } -func add(n *core.IpfsNode, in io.Reader) (*dag.Node, error) { - node, err := importer.NewDagFromReader(in) - if err != nil { - return nil, err - } +func add(n *core.IpfsNode, readers []io.Reader) ([]*dag.Node, error) { - // add the file to the graph + local storage - err = n.DAG.AddRecursive(node) - if err != nil { - return nil, err - } + dagnodes := make([]*dag.Node, 0) - // ensure we keep it - return node, n.Pinning.Pin(node, true) + for _, reader := range readers { + node, err := importer.NewDagFromReader(reader) + if err != nil { + return nil, err + } + + err = n.DAG.AddRecursive(node) // add the file to the graph + local storage + if err != nil { + return nil, err + } + + err = n.Pinning.Pin(node, true) // ensure we keep it + if err != nil { + return nil, err + } + + dagnodes = append(dagnodes, node) + } + return dagnodes, nil } From 772f657fcd348a1980ce37501bcc573b4d48a32e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 6 Nov 2014 03:37:39 -0800 Subject: [PATCH 139/383] add online method to node --- core/core.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/core.go b/core/core.go index 5ceacab92..a68cd2d3d 100644 --- a/core/core.go +++ b/core/core.go @@ -74,6 +74,8 @@ type IpfsNode struct { Pinning pin.Pinner ctxc.ContextCloser + + online bool // alternatively, offline } // NewIpfsNode constructs a new IpfsNode based on the given config. @@ -92,6 +94,7 @@ func NewIpfsNode(cfg *config.Config, online bool) (n *IpfsNode, err error) { // derive this from a higher context. ctx := context.TODO() n = &IpfsNode{ + online: online, Config: cfg, ContextCloser: ctxc.NewContextCloser(ctx, nil), } @@ -172,6 +175,10 @@ func NewIpfsNode(cfg *config.Config, online bool) (n *IpfsNode, err error) { return n, nil } +func (n *IpfsNode) Online() bool { + return n.online +} + func initIdentity(cfg *config.Config, peers peer.Peerstore, online bool) (peer.Peer, error) { if cfg.Identity.PeerID == "" { return nil, errors.New("Identity was not set in config (was ipfs init run?)") From 8b6931cb02b7f51e6e026adb908fc0463b4581ec Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 6 Nov 2014 03:41:19 -0800 Subject: [PATCH 140/383] check mode with use func node.OnlineMode() bool --- core/commands2/diag.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/commands2/diag.go b/core/commands2/diag.go index 197cfd130..f265c2a8d 100644 --- a/core/commands2/diag.go +++ b/core/commands2/diag.go @@ -31,7 +31,7 @@ var diagCmd = &cmds.Command{ Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node - if n.Diagnostics == nil { + if !n.Online() { res.SetError(errors.New("Cannot run diagnostic in offline mode!"), cmds.ErrNormal) return } From 6d89094d41d56e3c17ef5e0363992d9603cf4073 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 6 Nov 2014 03:42:30 -0800 Subject: [PATCH 141/383] rename -> OnlineMode() because this method doesn't check actual network status --- core/commands2/diag.go | 2 +- core/core.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/commands2/diag.go b/core/commands2/diag.go index f265c2a8d..b63486979 100644 --- a/core/commands2/diag.go +++ b/core/commands2/diag.go @@ -31,7 +31,7 @@ var diagCmd = &cmds.Command{ Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node - if !n.Online() { + if !n.OnlineMode() { res.SetError(errors.New("Cannot run diagnostic in offline mode!"), cmds.ErrNormal) return } diff --git a/core/core.go b/core/core.go index a68cd2d3d..75fd7d2c8 100644 --- a/core/core.go +++ b/core/core.go @@ -75,7 +75,7 @@ type IpfsNode struct { ctxc.ContextCloser - online bool // alternatively, offline + onlineMode bool // alternatively, offline } // NewIpfsNode constructs a new IpfsNode based on the given config. @@ -94,7 +94,7 @@ func NewIpfsNode(cfg *config.Config, online bool) (n *IpfsNode, err error) { // derive this from a higher context. ctx := context.TODO() n = &IpfsNode{ - online: online, + onlineMode: online, Config: cfg, ContextCloser: ctxc.NewContextCloser(ctx, nil), } @@ -175,8 +175,8 @@ func NewIpfsNode(cfg *config.Config, online bool) (n *IpfsNode, err error) { return n, nil } -func (n *IpfsNode) Online() bool { - return n.online +func (n *IpfsNode) OnlineMode() bool { + return n.onlineMode } func initIdentity(cfg *config.Config, peers peer.Peerstore, online bool) (peer.Peer, error) { From 353cb39921ee8e2c13359e37cb3039d542d7dfba Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 6 Nov 2014 03:52:37 -0800 Subject: [PATCH 142/383] refac(commands2/ls) split loop @mappum --- core/commands2/ls.go | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/core/commands2/ls.go b/core/commands2/ls.go index 1f67510cb..6176c242b 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -1,9 +1,11 @@ package commands import ( + "errors" "fmt" cmds "github.com/jbenet/go-ipfs/commands" + merkledag "github.com/jbenet/go-ipfs/merkledag" ) type Link struct { @@ -27,18 +29,31 @@ var lsCmd = &cmds.Command{ Help: "TODO", Run: func(res cmds.Response, req cmds.Request) { node := req.Context().Node - output := make([]Object, len(req.Arguments())) - for i, arg := range req.Arguments() { - path := arg.(string) + paths := make([]string, 0) + for _, arg := range req.Arguments() { + path, ok := arg.(string) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + paths = append(paths, path) + } + + dagnodes := make([]*merkledag.Node, 0) + for _, path := range paths { dagnode, err := node.Resolver.ResolvePath(path) if err != nil { res.SetError(err, cmds.ErrNormal) return } + dagnodes = append(dagnodes, dagnode) + } + output := make([]Object, len(req.Arguments())) + for i, dagnode := range dagnodes { output[i] = Object{ - Hash: path, + Hash: paths[i], Links: make([]Link, len(dagnode.Links)), } for j, link := range dagnode.Links { From 86f0aac88c2144604b0e2a7adeeda89129f05024 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 6 Nov 2014 04:14:39 -0800 Subject: [PATCH 143/383] refac(commands2/pin) loop --- core/commands2/pin.go | 45 ++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/core/commands2/pin.go b/core/commands2/pin.go index eb362a624..b93b33060 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -5,6 +5,8 @@ import ( "fmt" cmds "github.com/jbenet/go-ipfs/commands" + "github.com/jbenet/go-ipfs/core" + "github.com/jbenet/go-ipfs/merkledag" ) var pinCmd = &cmds.Command{ @@ -35,27 +37,17 @@ var pinCmd = &cmds.Command{ } }*/ + paths := make([]string, 0) for _, arg := range req.Arguments() { path, ok := arg.(string) if !ok { res.SetError(errors.New("cast error"), cmds.ErrNormal) return } - - dagnode, err := n.Resolver.ResolvePath(path) - if err != nil { - res.SetError(fmt.Errorf("pin error: %v", err), cmds.ErrNormal) - return - } - - err = n.Pinning.Pin(dagnode, recursive) - if err != nil { - res.SetError(fmt.Errorf("pin: %v", err), cmds.ErrNormal) - return - } + paths = append(paths, path) } - err := n.Pinning.Flush() + _, err := pin(n, paths, recursive) if err != nil { res.SetError(err, cmds.ErrNormal) } @@ -107,3 +99,30 @@ var unpinCmd = &cmds.Command{ // TODO: create some output to show what got unpinned }, } + +func pin(n *core.IpfsNode, paths []string, recursive bool) ([]*merkledag.Node, error) { + + dagnodes := make([]*merkledag.Node, 0) + for _, path := range paths { + + dagnode, err := n.Resolver.ResolvePath(path) + if err != nil { + return nil, fmt.Errorf("pin error: %v", err) + } + dagnodes = append(dagnodes, dagnode) + } + + for _, dagnode := range dagnodes { + err := n.Pinning.Pin(dagnode, recursive) + if err != nil { + return nil, fmt.Errorf("pin: %v", err) + } + } + + err := n.Pinning.Flush() + if err != nil { + return nil, err + } + + return dagnodes, nil +} From 4c029a86178a4aa058e58c4f99ad376f0ff4e962 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 6 Nov 2014 04:19:00 -0800 Subject: [PATCH 144/383] extract toStrings method --- core/commands2/pin.go | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/core/commands2/pin.go b/core/commands2/pin.go index b93b33060..f16f52f05 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -37,17 +37,13 @@ var pinCmd = &cmds.Command{ } }*/ - paths := make([]string, 0) - for _, arg := range req.Arguments() { - path, ok := arg.(string) - if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return - } - paths = append(paths, path) + paths, err := toStrings(req.Arguments()) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return } - _, err := pin(n, paths, recursive) + _, err = pin(n, paths, recursive) if err != nil { res.SetError(err, cmds.ErrNormal) } @@ -126,3 +122,15 @@ func pin(n *core.IpfsNode, paths []string, recursive bool) ([]*merkledag.Node, e return dagnodes, nil } + +func toStrings(slice []interface{}) ([]string, error) { + strs := make([]string, 0) + for _, maybe := range slice { + str, ok := maybe.(string) + if !ok { + return nil, errors.New("cast error") + } + strs = append(strs, str) + } + return strs, nil +} From 92d20e429d49c55e5c64171c37f3d469696814ec Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 6 Nov 2014 04:42:21 -0800 Subject: [PATCH 145/383] add slice utils ToStrings and ToReaders --- core/commands2/add.go | 13 +++++------ core/commands2/cat.go | 16 +++++--------- core/commands2/internal/slice_util.go | 32 +++++++++++++++++++++++++++ core/commands2/ls.go | 14 +++++------- core/commands2/pin.go | 12 ---------- 5 files changed, 48 insertions(+), 39 deletions(-) create mode 100644 core/commands2/internal/slice_util.go diff --git a/core/commands2/add.go b/core/commands2/add.go index b8774b047..ccb5e86c1 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -7,6 +7,7 @@ import ( cmds "github.com/jbenet/go-ipfs/commands" core "github.com/jbenet/go-ipfs/core" + internal "github.com/jbenet/go-ipfs/core/commands2/internal" importer "github.com/jbenet/go-ipfs/importer" dag "github.com/jbenet/go-ipfs/merkledag" ) @@ -34,14 +35,10 @@ var addCmd = &cmds.Command{ // if r, _ := opt.(bool); found && r { // } - readers := make([]io.Reader, 0) - for _, arg := range req.Arguments() { - reader, ok := arg.(io.Reader) - if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return - } - readers = append(readers, reader) + readers, err := internal.ToReaders(req.Arguments()) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return } dagnodes, err := add(n, readers) diff --git a/core/commands2/cat.go b/core/commands2/cat.go index 36416f33c..62f67b91d 100644 --- a/core/commands2/cat.go +++ b/core/commands2/cat.go @@ -1,11 +1,11 @@ package commands import ( - "errors" "io" cmds "github.com/jbenet/go-ipfs/commands" core "github.com/jbenet/go-ipfs/core" + "github.com/jbenet/go-ipfs/core/commands2/internal" uio "github.com/jbenet/go-ipfs/unixfs/io" ) @@ -20,19 +20,15 @@ var catCmd = &cmds.Command{ `, Run: func(res cmds.Response, req cmds.Request) { node := req.Context().Node - paths := make([]string, 0, len(req.Arguments())) readers := make([]io.Reader, 0, len(req.Arguments())) - for _, arg := range req.Arguments() { - path, ok := arg.(string) - if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return - } - paths = append(paths, path) + paths, err := internal.ToStrings(req.Arguments()) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return } - readers, err := cat(node, paths) + readers, err = cat(node, paths) if err != nil { res.SetError(err, cmds.ErrNormal) return diff --git a/core/commands2/internal/slice_util.go b/core/commands2/internal/slice_util.go new file mode 100644 index 000000000..e2d1ef54a --- /dev/null +++ b/core/commands2/internal/slice_util.go @@ -0,0 +1,32 @@ +package internal + +import ( + "errors" + "io" +) + +var CastErr = errors.New("cast error") + +func ToReaders(slice []interface{}) ([]io.Reader, error) { + readers := make([]io.Reader, 0) + for _, arg := range slice { + reader, ok := arg.(io.Reader) + if !ok { + return nil, CastErr + } + readers = append(readers, reader) + } + return readers, nil +} + +func ToStrings(slice []interface{}) ([]string, error) { + strs := make([]string, 0) + for _, maybe := range slice { + str, ok := maybe.(string) + if !ok { + return nil, CastErr + } + strs = append(strs, str) + } + return strs, nil +} diff --git a/core/commands2/ls.go b/core/commands2/ls.go index 6176c242b..5caa71179 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -1,10 +1,10 @@ package commands import ( - "errors" "fmt" cmds "github.com/jbenet/go-ipfs/commands" + "github.com/jbenet/go-ipfs/core/commands2/internal" merkledag "github.com/jbenet/go-ipfs/merkledag" ) @@ -30,14 +30,10 @@ var lsCmd = &cmds.Command{ Run: func(res cmds.Response, req cmds.Request) { node := req.Context().Node - paths := make([]string, 0) - for _, arg := range req.Arguments() { - path, ok := arg.(string) - if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return - } - paths = append(paths, path) + paths, err := internal.ToStrings(req.Arguments()) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return } dagnodes := make([]*merkledag.Node, 0) diff --git a/core/commands2/pin.go b/core/commands2/pin.go index f16f52f05..5ef9034ea 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -122,15 +122,3 @@ func pin(n *core.IpfsNode, paths []string, recursive bool) ([]*merkledag.Node, e return dagnodes, nil } - -func toStrings(slice []interface{}) ([]string, error) { - strs := make([]string, 0) - for _, maybe := range slice { - str, ok := maybe.(string) - if !ok { - return nil, errors.New("cast error") - } - strs = append(strs, str) - } - return strs, nil -} From 440c90be2490af0b6754eb19a089f7f896fd8286 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 6 Nov 2014 04:41:22 -0800 Subject: [PATCH 146/383] extract unpin @mappum --- core/commands2/pin.go | 56 +++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/core/commands2/pin.go b/core/commands2/pin.go index 5ef9034ea..4a43fa96c 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -1,11 +1,11 @@ package commands import ( - "errors" "fmt" cmds "github.com/jbenet/go-ipfs/commands" "github.com/jbenet/go-ipfs/core" + "github.com/jbenet/go-ipfs/core/commands2/internal" "github.com/jbenet/go-ipfs/merkledag" ) @@ -37,7 +37,7 @@ var pinCmd = &cmds.Command{ } }*/ - paths, err := toStrings(req.Arguments()) + paths, err := internal.ToStrings(req.Arguments()) if err != nil { res.SetError(err, cmds.ErrNormal) return @@ -66,28 +66,13 @@ var unpinCmd = &cmds.Command{ opt, _ := req.Option("recursive") recursive, _ := opt.(bool) // false if cast fails. - for _, arg := range req.Arguments() { - path, ok := arg.(string) - if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return - } - - dagnode, err := n.Resolver.ResolvePath(path) - if err != nil { - res.SetError(fmt.Errorf("pin error: %v", err), cmds.ErrNormal) - return - } - - k, _ := dagnode.Key() - err = n.Pinning.Unpin(k, recursive) - if err != nil { - res.SetError(fmt.Errorf("pin: %v", err), cmds.ErrNormal) - return - } + paths, err := internal.ToStrings(req.Arguments()) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return } - err := n.Pinning.Flush() + _, err = unpin(n, paths, recursive) if err != nil { res.SetError(err, cmds.ErrNormal) } @@ -100,7 +85,6 @@ func pin(n *core.IpfsNode, paths []string, recursive bool) ([]*merkledag.Node, e dagnodes := make([]*merkledag.Node, 0) for _, path := range paths { - dagnode, err := n.Resolver.ResolvePath(path) if err != nil { return nil, fmt.Errorf("pin error: %v", err) @@ -122,3 +106,29 @@ func pin(n *core.IpfsNode, paths []string, recursive bool) ([]*merkledag.Node, e return dagnodes, nil } + +func unpin(n *core.IpfsNode, paths []string, recursive bool) ([]*merkledag.Node, error) { + + dagnodes := make([]*merkledag.Node, 0) + for _, path := range paths { + dagnode, err := n.Resolver.ResolvePath(path) + if err != nil { + return nil, err + } + dagnodes = append(dagnodes, dagnode) + } + + for _, dagnode := range dagnodes { + k, _ := dagnode.Key() + err := n.Pinning.Unpin(k, recursive) + if err != nil { + return nil, err + } + } + + err := n.Pinning.Flush() + if err != nil { + return nil, err + } + return dagnodes, nil +} From 084ffd97aaa23ee68cc1334cd81e4a3000daf0e9 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 01:51:13 -0800 Subject: [PATCH 147/383] fix(cli) tear down node --- cmd/ipfs2/main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 4a4516ed8..5adc4181a 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -177,6 +177,7 @@ func callCommand(req cmds.Request, root *cmds.Command) cmds.Response { fmt.Println(err) os.Exit(1) } + defer node.Close() req.Context().Node = node res = root.Call(req) From c65b01c55cd770177a7377e11cb2406445f7f0c5 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 05:47:04 -0800 Subject: [PATCH 148/383] fix(add) cast safely --- cmd/ipfs2/daemon.go | 2 +- core/commands2/add.go | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cmd/ipfs2/daemon.go b/cmd/ipfs2/daemon.go index 9ffe96d9d..da3603cad 100644 --- a/cmd/ipfs2/daemon.go +++ b/cmd/ipfs2/daemon.go @@ -9,7 +9,7 @@ import ( cmds "github.com/jbenet/go-ipfs/commands" cmdsHttp "github.com/jbenet/go-ipfs/commands/http" - "github.com/jbenet/go-ipfs/core" + core "github.com/jbenet/go-ipfs/core" commands "github.com/jbenet/go-ipfs/core/commands2" daemon "github.com/jbenet/go-ipfs/daemon2" ) diff --git a/core/commands2/add.go b/core/commands2/add.go index ccb5e86c1..ab22bf3c7 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -62,14 +62,18 @@ var addCmd = &cmds.Command{ }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ cmds.Text: func(res cmds.Response) ([]byte, error) { - v := res.Output().(*AddOutput).Added - if len(v) == 1 { - s := fmt.Sprintf("Added object: %s\n", v[0].Hash) + val, ok := res.Output().(*AddOutput) + if !ok { + return nil, errors.New("cast err") + } + added := val.Added + if len(added) == 1 { + s := fmt.Sprintf("Added object: %s\n", added[0].Hash) return []byte(s), nil } - s := fmt.Sprintf("Added %v objects:\n", len(v)) - for _, obj := range v { + s := fmt.Sprintf("Added %v objects:\n", len(added)) + for _, obj := range added { s += fmt.Sprintf("- %s\n", obj.Hash) } return []byte(s), nil From 520240d3850b2242d0b91cb32989bc5af3bf18ef Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 07:47:19 -0800 Subject: [PATCH 149/383] feat(commands2/tour) impl --- cmd/ipfs2/ipfs.go | 5 +- cmd/ipfs2/tour.go | 151 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 cmd/ipfs2/tour.go diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go index bb328085a..ef359e370 100644 --- a/cmd/ipfs2/ipfs.go +++ b/cmd/ipfs2/ipfs.go @@ -9,7 +9,8 @@ var Root = &cmds.Command{ Options: commands.Root.Options, Help: commands.Root.Help, Subcommands: map[string]*cmds.Command{ - "daemon": daemonCmd, - "init": initCmd, + "daemon": daemonCmd, // TODO name + "init": initCmd, // TODO name + "tour": cmdTour, }, } diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go new file mode 100644 index 000000000..b604e843e --- /dev/null +++ b/cmd/ipfs2/tour.go @@ -0,0 +1,151 @@ +package main + +import ( + "bytes" + "fmt" + "io" + "os" + + cmds "github.com/jbenet/go-ipfs/commands" + "github.com/jbenet/go-ipfs/config" + "github.com/jbenet/go-ipfs/core/commands2/internal" + "github.com/jbenet/go-ipfs/tour" +) + +var cmdTour = &cmds.Command{ + + Arguments: []cmds.Argument{ + cmds.Argument{"number", cmds.ArgString, false, true}, + }, + + // TODO UsageLine: "tour []", + // TODO Short: "Take the IPFS Tour.", + + Help: `ipfs tour - Take the IPFS Tour. + + ipfs tour [] - Show tour topic. Default to current. + ipfs tour next - Show the next tour topic. + ipfs tour list - Show a list of topics. + ipfs tour restart - Restart the tour. + +This is a tour that takes you through various IPFS concepts, +features, and tools to make sure you get up to speed with +IPFS very quickly. To start, run: + + ipfs tour +`, + Subcommands: map[string]*cmds.Command{ + "list": cmdIpfsTourList, + "next": cmdIpfsTourNext, + "restart": cmdIpfsTourRestart, + }, + Run: func(res cmds.Response, req cmds.Request) { + + out := new(bytes.Buffer) + cfg := req.Context().Config + strs, err := internal.ToStrings(req.Arguments()) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + topic := tour.TopicID(cfg.Tour.Last) + if len(strs) > 0 { + topic = tour.TopicID(strs[0]) + } + + err = tourShow(out, topic) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(out) + }, +} + +var cmdIpfsTourNext = &cmds.Command{ + Help: "Show the next IPFS Tour topic.", + Run: func(res cmds.Response, req cmds.Request) { + var w bytes.Buffer + cfg := req.Context().Config + path := req.Context().ConfigRoot + + topic := tour.NextTopic(tour.TopicID(cfg.Tour.Last)) + if err := tourShow(&w, topic); err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + // topic changed, not last. write it out. + if string(topic) != cfg.Tour.Last { + cfg.Tour.Last = string(topic) + err := writeConfig(path, cfg) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + } + + w.WriteTo(os.Stdout) // TODO write to res.SetValue + }, +} + +var cmdIpfsTourRestart = &cmds.Command{ + Help: "Restart the IPFS Tour.", + Run: func(res cmds.Response, req cmds.Request) { + path := req.Context().ConfigRoot + cfg := req.Context().Config + + cfg.Tour.Last = "" + err := writeConfig(path, cfg) + if err != nil { + res.SetError(err, cmds.ErrNormal) + } + }, +} + +var cmdIpfsTourList = &cmds.Command{ + Help: "Show a list of IPFS Tour topics.", + Run: func(res cmds.Response, req cmds.Request) { + var w bytes.Buffer + tourListCmd(&w, req.Context().Config) + w.WriteTo(os.Stdout) // TODO use res.SetOutput(output) + }, +} + +func tourListCmd(w io.Writer, cfg *config.Config) { + + lastid := tour.TopicID(cfg.Tour.Last) + for _, id := range tour.IDs { + c := ' ' + switch { + case id == lastid: + c = '*' + case id.LessThan(lastid): + c = '✓' + } + + t := tour.Topics[id] + fmt.Fprintf(w, "- %c %-5.5s %s\n", c, id, t.Title) + } +} + +func tourShow(w io.Writer, id tour.ID) error { + t, found := tour.Topics[id] + if !found { + return fmt.Errorf("no topic with id: %s", id) + } + + fmt.Fprintf(w, "Tour %s - %s\n\n%s\n", t.ID, t.Title, t.Text) + return nil +} + +// TODO share func +func writeConfig(path string, cfg *config.Config) error { + filename, err := config.Filename(path) + if err != nil { + return err + } + return config.WriteConfigFile(filename, cfg) +} From 7ead660738a855052660564967eba841917f86d9 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 6 Nov 2014 17:10:19 -0800 Subject: [PATCH 150/383] cmd/ipfs2: Set all loggers to DEBUG level when running with 'debug' flag --- cmd/ipfs2/main.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 5adc4181a..f56094685 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -6,6 +6,7 @@ import ( "os" "runtime/pprof" + logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-logging" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net" @@ -107,6 +108,8 @@ func handleOptions(req cmds.Request, root *cmds.Command) { if debugBool, ok := debug.(bool); debugBool && ok { u.Debug = true + u.SetAllLoggers(logging.DEBUG) + // if debugging, setup profiling. if u.Debug { ofi, err := os.Create("cpu.prof") From 3df5202f8a36d3654018f033f33b63cc73774a12 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 6 Nov 2014 17:11:11 -0800 Subject: [PATCH 151/383] commands/http: Log incoming requests (with DEBUG log level) --- commands/http/handler.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/commands/http/handler.go b/commands/http/handler.go index 8ac3e987f..e5959b4a8 100644 --- a/commands/http/handler.go +++ b/commands/http/handler.go @@ -6,8 +6,11 @@ import ( "net/http" cmds "github.com/jbenet/go-ipfs/commands" + u "github.com/jbenet/go-ipfs/util" ) +var log = u.Logger("commands/http") + type Handler struct { ctx cmds.Context root *cmds.Command @@ -26,6 +29,8 @@ func NewHandler(ctx cmds.Context, root *cmds.Command) *Handler { } func (i Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + log.Debug("Incoming API request: ", r.URL) + req, err := Parse(r, i.root) if err != nil { if err == ErrNotFound { From 36dfb7bc43eaadb295ba7c9741077480433de411 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 7 Nov 2014 15:41:01 -0800 Subject: [PATCH 152/383] core/commands2: Added 'update' command --- core/commands2/root.go | 1 + core/commands2/update.go | 155 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 core/commands2/update.go diff --git a/core/commands2/root.go b/core/commands2/root.go index 75334e611..fed8c3f43 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -70,6 +70,7 @@ var rootSubcommands = map[string]*cmds.Command{ "bootstrap": bootstrapCmd, "mount": mountCmd, "block": blockCmd, + "update": updateCmd, // test subcommands // TODO: remove these when we don't need them anymore diff --git a/core/commands2/update.go b/core/commands2/update.go new file mode 100644 index 000000000..9e1edd186 --- /dev/null +++ b/core/commands2/update.go @@ -0,0 +1,155 @@ +package commands + +import ( + "errors" + "fmt" + + cmds "github.com/jbenet/go-ipfs/commands" + "github.com/jbenet/go-ipfs/core" + "github.com/jbenet/go-ipfs/updates" +) + +type UpdateOutput struct { + OldVersion string + NewVersion string +} + +var updateCmd = &cmds.Command{ + Help: `ipfs update - check for updates and apply them + + ipfs update - apply + ipfs update check - just check + ipfs update log - list the changelogs + +ipfs update is a utility command used to check for updates and apply them. +`, + Run: func(res cmds.Response, req cmds.Request) { + n := req.Context().Node + + output, err := updateApply(n) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(output) + }, + Type: &UpdateOutput{}, + Subcommands: map[string]*cmds.Command{ + "check": updateCheckCmd, + "log": updateLogCmd, + }, + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + v := res.Output().(*UpdateOutput) + s := "" + if v.NewVersion != v.OldVersion { + s = fmt.Sprintf("Successfully updated to IPFS version '%s' (from '%s')", + v.NewVersion, v.OldVersion) + } else { + s = fmt.Sprintf("Already updated to latest version ('%s')", v.NewVersion) + } + return []byte(s), nil + }, + }, +} + +var updateCheckCmd = &cmds.Command{ + Help: `ipfs update check `, + Run: func(res cmds.Response, req cmds.Request) { + n := req.Context().Node + + output, err := updateCheck(n) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(output) + }, + Type: &UpdateOutput{}, + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + v := res.Output().(*UpdateOutput) + s := "" + if v.NewVersion != v.OldVersion { + s = fmt.Sprintf("A new version of IPFS is available ('%s', currently running '%s')", + v.NewVersion, v.OldVersion) + } else { + s = fmt.Sprintf("Already updated to latest version ('%s')", v.NewVersion) + } + return []byte(s), nil + }, + }, +} + +var updateLogCmd = &cmds.Command{ + Help: `ipfs update log - list the last versions and their changelog`, + Run: func(res cmds.Response, req cmds.Request) { + n := req.Context().Node + + output, err := updateLog(n) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(output) + }, +} + +// updateApply applies an update of the ipfs binary and shuts down the node if successful +func updateApply(n *core.IpfsNode) (*UpdateOutput, error) { + // TODO: 'force bool' param that stops the daemon (if running) before update + + output := &UpdateOutput{ + OldVersion: updates.Version, + } + + u, err := updates.CheckForUpdate() + if err != nil { + return nil, err + } + + if u == nil { + output.NewVersion = updates.Version + return output, nil + } + + output.NewVersion = u.Version + + if n.OnlineMode() { + return nil, errors.New(`You must stop the IPFS daemon before updating.`) + } + + if err = updates.Apply(u); err != nil { + return nil, err + } + + return output, nil +} + +// updateCheck checks wether there is an update available +func updateCheck(n *core.IpfsNode) (*UpdateOutput, error) { + output := &UpdateOutput{ + OldVersion: updates.Version, + } + + u, err := updates.CheckForUpdate() + if err != nil { + return nil, err + } + + if u == nil { + output.NewVersion = updates.Version + return output, nil + } + + output.NewVersion = u.Version + return output, nil +} + +// updateLog lists the version available online +func updateLog(n *core.IpfsNode) (interface{}, error) { + return nil, errors.New("Not yet implemented") +} From 99d509845035759f32d07fc9640bbfac3e076311 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 08:01:19 -0800 Subject: [PATCH 153/383] named imports --- cmd/ipfs2/tour.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index b604e843e..f5c5d5941 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -7,9 +7,9 @@ import ( "os" cmds "github.com/jbenet/go-ipfs/commands" - "github.com/jbenet/go-ipfs/config" - "github.com/jbenet/go-ipfs/core/commands2/internal" - "github.com/jbenet/go-ipfs/tour" + config "github.com/jbenet/go-ipfs/config" + internal "github.com/jbenet/go-ipfs/core/commands2/internal" + tour "github.com/jbenet/go-ipfs/tour" ) var cmdTour = &cmds.Command{ From aa1c3cae4a9c464c6ed7d04600c54ffb2f5dafa4 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 08:10:48 -0800 Subject: [PATCH 154/383] fix(tour) variadic -> false --- cmd/ipfs2/tour.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index f5c5d5941..afc5964af 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -15,7 +15,7 @@ import ( var cmdTour = &cmds.Command{ Arguments: []cmds.Argument{ - cmds.Argument{"number", cmds.ArgString, false, true}, + cmds.Argument{"number", cmds.ArgString, false, false}, }, // TODO UsageLine: "tour []", From a631d745d06527aa10a98b14615c655096549052 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 18:26:04 -0800 Subject: [PATCH 155/383] docs(add) help --- core/commands2/add.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/commands2/add.go b/core/commands2/add.go index ab22bf3c7..ed4a676fc 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -26,7 +26,15 @@ var addCmd = &cmds.Command{ Arguments: []cmds.Argument{ cmds.Argument{"file", cmds.ArgFile, false, true}, }, - Help: "TODO", + // TODO UsageLine: "add", + // TODO Short: "Add an object to ipfs.", + Help: `ipfs add ... - Add objects to ipfs. + + Adds contents of to ipfs. Use -r to add directories. + Note that directories are added recursively, to form the ipfs + MerkleDAG. A smarter partial add with a staging area (like git) + remains to be implemented. +`, Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node From 1c7ef8926dbe6a7cdc3b5ca7ba25b91cb913cfa7 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 18:27:56 -0800 Subject: [PATCH 156/383] docs(commands) help --- core/commands2/commands.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/commands2/commands.go b/core/commands2/commands.go index 0f102b2c4..f1d7f4fc6 100644 --- a/core/commands2/commands.go +++ b/core/commands2/commands.go @@ -13,7 +13,12 @@ type Command struct { } var commandsCmd = &cmds.Command{ - Help: "TODO", + // TODO UsageLine: "commands", + // TODO Short: "List all available commands.", + Help: `ipfs commands - List all available commands. + + Lists all available commands (and sub-commands) and exits. + `, Run: func(res cmds.Response, req cmds.Request) { root := outputCommand("ipfs", Root) res.SetOutput(&root) From 48d65c6d9538a3053d14c0e5f6f7af84a3c22568 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 18:32:53 -0800 Subject: [PATCH 157/383] docs(net-diag) help + name @jbenet @whyrusleeping Docs read net-diag. It seems the command was previously registered as diag. Which do you prefer? --- core/commands2/diag.go | 8 ++++++++ core/commands2/root.go | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/core/commands2/diag.go b/core/commands2/diag.go index b63486979..ec18476a9 100644 --- a/core/commands2/diag.go +++ b/core/commands2/diag.go @@ -28,6 +28,14 @@ type DiagnosticOutput struct { } var diagCmd = &cmds.Command{ + // TODO UsageLine: "net-diag", + // TODO Short: "Generate a diagnostics report", + Help: `ipfs net-diag - Generate a diagnostics report. + + Sends out a message to each node in the network recursively + requesting a listing of data about them including number of + connected peers and latencies between them. +`, Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node diff --git a/core/commands2/root.go b/core/commands2/root.go index fed8c3f43..9417b9b7a 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -62,7 +62,7 @@ var rootSubcommands = map[string]*cmds.Command{ "name": nameCmd, "add": addCmd, "log": logCmd, - "diag": diagCmd, + "net-diag": diagCmd, "pin": pinCmd, "unpin": unpinCmd, "version": versionCmd, From 229b22f9c4d6637bebf2c0505d09f7e4c313fae8 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 18:35:19 -0800 Subject: [PATCH 158/383] docs(log) help making docs match command: name -> subsystem. Is the desired name? --- core/commands2/log.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/commands2/log.go b/core/commands2/log.go index 461107b1d..510085c85 100644 --- a/core/commands2/log.go +++ b/core/commands2/log.go @@ -12,7 +12,15 @@ var logCmd = &cmds.Command{ cmds.Argument{"subsystem", cmds.ArgString, true, false}, cmds.Argument{"level", cmds.ArgString, true, false}, }, - Help: "TODO", + // TODO UsageLine: "log ", + // TODO Short: "switch logging levels of a running daemon", + Help: `ipfs log - switch logging levels of a running daemon + + is a the subsystem logging identifier. Use * for all subsystems. + is one of: debug, info, notice, warning, error, critical + +ipfs log is a utility command used to change the logging output of a running daemon. +`, Run: func(res cmds.Response, req cmds.Request) { args := req.Arguments() if err := u.SetLogLevel(args[0].(string), args[1].(string)); err != nil { From 0e8e3cccb6136ec7b8cf5933ab382b76a2dda7f7 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 18:36:33 -0800 Subject: [PATCH 159/383] docs(ls) help --- core/commands2/ls.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/commands2/ls.go b/core/commands2/ls.go index 5caa71179..af8e410f5 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -26,7 +26,16 @@ var lsCmd = &cmds.Command{ Arguments: []cmds.Argument{ cmds.Argument{"object", cmds.ArgString, false, true}, }, - Help: "TODO", + // TODO UsageLine: "ls", + // TODO Short: "List links from an object.", + Help: `ipfs ls - List links from an object. + + Retrieves the object named by and displays the links + it contains, with the following format: + + + +`, Run: func(res cmds.Response, req cmds.Request) { node := req.Context().Node From d066f15c37b21a59caf7679caf5bed8b470e1751 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 18:37:15 -0800 Subject: [PATCH 160/383] todo(ls) @jbenet --- core/commands2/ls.go | 1 + 1 file changed, 1 insertion(+) diff --git a/core/commands2/ls.go b/core/commands2/ls.go index af8e410f5..569bfde65 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -28,6 +28,7 @@ var lsCmd = &cmds.Command{ }, // TODO UsageLine: "ls", // TODO Short: "List links from an object.", + // TODO docs read ipfs-path. argument says option. which? Help: `ipfs ls - List links from an object. Retrieves the object named by and displays the links From ff55968b4cc1878c4f135c6d9630763c4a7e45d9 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 18:40:16 -0800 Subject: [PATCH 161/383] fix(mount) add argument --- core/commands2/mount_unix.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go index e13d7c287..1ea744d05 100644 --- a/core/commands2/mount_unix.go +++ b/core/commands2/mount_unix.go @@ -17,6 +17,9 @@ import ( const mountTimeout = time.Second var mountCmd = &cmds.Command{ + Arguments: []cmds.Argument{ + cmds.Argument{Name: "os-path", Type: cmds.ArgString, Required: false, Variadic: false}, + }, Options: []cmds.Option{ cmds.Option{[]string{"f"}, cmds.String}, cmds.Option{[]string{"n"}, cmds.String}, From c69ea0d6e24d4e075640e9d4f83709da5e3f9faa Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 18:41:03 -0800 Subject: [PATCH 162/383] add fixmes --- core/commands2/mount_unix.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go index 1ea744d05..a7d894d0d 100644 --- a/core/commands2/mount_unix.go +++ b/core/commands2/mount_unix.go @@ -21,7 +21,13 @@ var mountCmd = &cmds.Command{ cmds.Argument{Name: "os-path", Type: cmds.ArgString, Required: false, Variadic: false}, }, Options: []cmds.Option{ + + // TODO text: specify a mountpoint for ipfs + // TODO longform cmds.Option{[]string{"f"}, cmds.String}, + + // TODO text: specify a mountpoint for ipns + // TODO longform cmds.Option{[]string{"n"}, cmds.String}, }, Help: `ipfs mount - Mount an ipfs read-only mountpoint. From 092e346f9494ddb5341d404dce746727d14e89cb Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 18:47:51 -0800 Subject: [PATCH 163/383] docs(name/publish/resolve) help --- core/commands2/name.go | 81 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/core/commands2/name.go b/core/commands2/name.go index 4a9bc7997..3d1d0ab56 100644 --- a/core/commands2/name.go +++ b/core/commands2/name.go @@ -23,7 +23,41 @@ type ResolveOutput struct { var errNotOnline = errors.New("This command must be run in online mode. Try running 'ipfs daemon' first.") var nameCmd = &cmds.Command{ - Help: "TODO", + // TODO UsageLine: "name [publish | resolve]", + // TODO Short: "ipfs namespace (ipns) tool", + Help: `ipfs name - Get/Set ipfs config values. + + ipfs name publish [] - Assign the to + ipfs name resolve [] - Resolve the value of + +IPNS is a PKI namespace, where names are the hashes of public keys, and +the private key enables publishing new (signed) values. In both publish +and resolve, the default value of is your own identity public key. + + +Examples: + +Publish a to your identity name: + + > ipfs name publish QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +Publish a to another public key: + + > ipfs name publish QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +Resolve the value of your identity: + + > ipfs name resolve + QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +Resolve te value of another name: + + > ipfs name resolve QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n + QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +`, Subcommands: map[string]*cmds.Command{ "publish": publishCmd, "resolve": resolveCmd, @@ -31,11 +65,31 @@ var nameCmd = &cmds.Command{ } var publishCmd = &cmds.Command{ + // TODO UsageLine: "publish", + // TODO Short: "publish a to ipns.", + Help: `ipfs publish [] - publish a to ipns. + +IPNS is a PKI namespace, where names are the hashes of public keys, and +the private key enables publishing new (signed) values. In publish, the +default value of is your own identity public key. + +Examples: + +Publish a to your identity name: + + > ipfs name publish QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +Publish a to another public key: + + > ipfs name publish QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +`, Arguments: []cmds.Argument{ cmds.Argument{"name", cmds.ArgString, false, false}, cmds.Argument{"object", cmds.ArgString, true, false}, }, - Help: "TODO", Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node args := req.Arguments() @@ -85,6 +139,29 @@ var publishCmd = &cmds.Command{ } var resolveCmd = &cmds.Command{ + // TODO UsageLine: "resolve", + // TODO Short: "resolve an ipns name to a ", + Help: `ipfs resolve [] - Resolve an ipns name to a . + +IPNS is a PKI namespace, where names are the hashes of public keys, and +the private key enables publishing new (signed) values. In resolve, the +default value of is your own identity public key. + + +Examples: + +Resolve the value of your identity: + + > ipfs name resolve + QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +Resolve te value of another name: + + > ipfs name resolve QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n + QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +`, + Arguments: []cmds.Argument{ cmds.Argument{"name", cmds.ArgString, false, true}, }, From 63feb68ce77cb6e69054325f378cc91b2516f281 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 18:52:16 -0800 Subject: [PATCH 164/383] refactor(name, pub, resolve) separate files --- core/commands2/name.go | 189 +------------------------------------- core/commands2/publish.go | 106 +++++++++++++++++++++ core/commands2/resolve.go | 92 +++++++++++++++++++ 3 files changed, 199 insertions(+), 188 deletions(-) create mode 100644 core/commands2/publish.go create mode 100644 core/commands2/resolve.go diff --git a/core/commands2/name.go b/core/commands2/name.go index 3d1d0ab56..7066641f8 100644 --- a/core/commands2/name.go +++ b/core/commands2/name.go @@ -1,27 +1,12 @@ package commands -import ( - "errors" - "fmt" - - cmds "github.com/jbenet/go-ipfs/commands" - core "github.com/jbenet/go-ipfs/core" - crypto "github.com/jbenet/go-ipfs/crypto" - nsys "github.com/jbenet/go-ipfs/namesys" - u "github.com/jbenet/go-ipfs/util" -) +import cmds "github.com/jbenet/go-ipfs/commands" type IpnsEntry struct { Name string Value string } -type ResolveOutput struct { - Entries []IpnsEntry -} - -var errNotOnline = errors.New("This command must be run in online mode. Try running 'ipfs daemon' first.") - var nameCmd = &cmds.Command{ // TODO UsageLine: "name [publish | resolve]", // TODO Short: "ipfs namespace (ipns) tool", @@ -63,175 +48,3 @@ Resolve te value of another name: "resolve": resolveCmd, }, } - -var publishCmd = &cmds.Command{ - // TODO UsageLine: "publish", - // TODO Short: "publish a to ipns.", - Help: `ipfs publish [] - publish a to ipns. - -IPNS is a PKI namespace, where names are the hashes of public keys, and -the private key enables publishing new (signed) values. In publish, the -default value of is your own identity public key. - -Examples: - -Publish a to your identity name: - - > ipfs name publish QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - -Publish a to another public key: - - > ipfs name publish QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - -`, - Arguments: []cmds.Argument{ - cmds.Argument{"name", cmds.ArgString, false, false}, - cmds.Argument{"object", cmds.ArgString, true, false}, - }, - Run: func(res cmds.Response, req cmds.Request) { - n := req.Context().Node - args := req.Arguments() - - if n.Network == nil { - res.SetError(errNotOnline, cmds.ErrNormal) - return - } - - if n.Identity == nil { - res.SetError(errors.New("Identity not loaded!"), cmds.ErrNormal) - return - } - - // name := "" - ref := "" - - switch len(args) { - case 2: - // name = args[0] - ref = args[1].(string) - res.SetError(errors.New("keychains not yet implemented"), cmds.ErrNormal) - return - case 1: - // name = n.Identity.ID.String() - ref = args[0].(string) - } - - // TODO n.Keychain.Get(name).PrivKey - k := n.Identity.PrivKey() - publishOutput, err := publish(n, k, ref) - - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - res.SetOutput(publishOutput) - }, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ - cmds.Text: func(res cmds.Response) ([]byte, error) { - v := res.Output().(*IpnsEntry) - s := fmt.Sprintf("Published name %s to %s\n", v.Name, v.Value) - return []byte(s), nil - }, - }, - Type: &IpnsEntry{}, -} - -var resolveCmd = &cmds.Command{ - // TODO UsageLine: "resolve", - // TODO Short: "resolve an ipns name to a ", - Help: `ipfs resolve [] - Resolve an ipns name to a . - -IPNS is a PKI namespace, where names are the hashes of public keys, and -the private key enables publishing new (signed) values. In resolve, the -default value of is your own identity public key. - - -Examples: - -Resolve the value of your identity: - - > ipfs name resolve - QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - -Resolve te value of another name: - - > ipfs name resolve QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n - QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - -`, - - Arguments: []cmds.Argument{ - cmds.Argument{"name", cmds.ArgString, false, true}, - }, - Run: func(res cmds.Response, req cmds.Request) { - - n := req.Context().Node - var names []string - - if n.Network == nil { - res.SetError(errNotOnline, cmds.ErrNormal) - return - } - - if len(req.Arguments()) == 0 { - if n.Identity == nil { - res.SetError(errors.New("Identity not loaded!"), cmds.ErrNormal) - return - } - names = append(names, n.Identity.ID().String()) - } else { - for _, arg := range req.Arguments() { - name, ok := arg.(string) - if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return - } - names = append(names, name) - } - } - - entries, err := resolve(n, names) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - res.SetOutput(&ResolveOutput{entries}) - }, - Type: &ResolveOutput{}, -} - -func publish(n *core.IpfsNode, k crypto.PrivKey, ref string) (*IpnsEntry, error) { - pub := nsys.NewRoutingPublisher(n.Routing) - err := pub.Publish(k, ref) - if err != nil { - return nil, err - } - - hash, err := k.GetPublic().Hash() - if err != nil { - return nil, err - } - - return &IpnsEntry{ - Name: u.Key(hash).String(), - Value: ref, - }, nil -} - -func resolve(n *core.IpfsNode, names []string) ([]IpnsEntry, error) { - var entries []IpnsEntry - for _, name := range names { - resolved, err := n.Namesys.Resolve(name) - if err != nil { - return nil, err - } - entries = append(entries, IpnsEntry{ - Name: name, - Value: resolved, - }) - } - return entries, nil -} diff --git a/core/commands2/publish.go b/core/commands2/publish.go new file mode 100644 index 000000000..3433f76fd --- /dev/null +++ b/core/commands2/publish.go @@ -0,0 +1,106 @@ +package commands + +import ( + "errors" + "fmt" + + cmds "github.com/jbenet/go-ipfs/commands" + core "github.com/jbenet/go-ipfs/core" + crypto "github.com/jbenet/go-ipfs/crypto" + nsys "github.com/jbenet/go-ipfs/namesys" + u "github.com/jbenet/go-ipfs/util" +) + +var errNotOnline = errors.New("This command must be run in online mode. Try running 'ipfs daemon' first.") + +var publishCmd = &cmds.Command{ + // TODO UsageLine: "publish", + // TODO Short: "publish a to ipns.", + Help: `ipfs publish [] - publish a to ipns. + +IPNS is a PKI namespace, where names are the hashes of public keys, and +the private key enables publishing new (signed) values. In publish, the +default value of is your own identity public key. + +Examples: + +Publish a to your identity name: + + > ipfs name publish QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +Publish a to another public key: + + > ipfs name publish QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +`, + Arguments: []cmds.Argument{ + cmds.Argument{"name", cmds.ArgString, false, false}, + cmds.Argument{"object", cmds.ArgString, true, false}, + }, + Run: func(res cmds.Response, req cmds.Request) { + n := req.Context().Node + args := req.Arguments() + + if n.Network == nil { + res.SetError(errNotOnline, cmds.ErrNormal) + return + } + + if n.Identity == nil { + res.SetError(errors.New("Identity not loaded!"), cmds.ErrNormal) + return + } + + // name := "" + ref := "" + + switch len(args) { + case 2: + // name = args[0] + ref = args[1].(string) + res.SetError(errors.New("keychains not yet implemented"), cmds.ErrNormal) + return + case 1: + // name = n.Identity.ID.String() + ref = args[0].(string) + } + + // TODO n.Keychain.Get(name).PrivKey + k := n.Identity.PrivKey() + publishOutput, err := publish(n, k, ref) + + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + res.SetOutput(publishOutput) + }, + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + v := res.Output().(*IpnsEntry) + s := fmt.Sprintf("Published name %s to %s\n", v.Name, v.Value) + return []byte(s), nil + }, + }, + Type: &IpnsEntry{}, +} + +func publish(n *core.IpfsNode, k crypto.PrivKey, ref string) (*IpnsEntry, error) { + pub := nsys.NewRoutingPublisher(n.Routing) + err := pub.Publish(k, ref) + if err != nil { + return nil, err + } + + hash, err := k.GetPublic().Hash() + if err != nil { + return nil, err + } + + return &IpnsEntry{ + Name: u.Key(hash).String(), + Value: ref, + }, nil +} diff --git a/core/commands2/resolve.go b/core/commands2/resolve.go new file mode 100644 index 000000000..3e1ad5782 --- /dev/null +++ b/core/commands2/resolve.go @@ -0,0 +1,92 @@ +package commands + +import ( + "errors" + + cmds "github.com/jbenet/go-ipfs/commands" + core "github.com/jbenet/go-ipfs/core" +) + +type ResolveOutput struct { + Entries []IpnsEntry +} + +var resolveCmd = &cmds.Command{ + // TODO UsageLine: "resolve", + // TODO Short: "resolve an ipns name to a ", + Help: `ipfs resolve [] - Resolve an ipns name to a . + +IPNS is a PKI namespace, where names are the hashes of public keys, and +the private key enables publishing new (signed) values. In resolve, the +default value of is your own identity public key. + + +Examples: + +Resolve the value of your identity: + + > ipfs name resolve + QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +Resolve te value of another name: + + > ipfs name resolve QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n + QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + +`, + + Arguments: []cmds.Argument{ + cmds.Argument{"name", cmds.ArgString, false, true}, + }, + Run: func(res cmds.Response, req cmds.Request) { + + n := req.Context().Node + var names []string + + if n.Network == nil { + res.SetError(errNotOnline, cmds.ErrNormal) + return + } + + if len(req.Arguments()) == 0 { + if n.Identity == nil { + res.SetError(errors.New("Identity not loaded!"), cmds.ErrNormal) + return + } + names = append(names, n.Identity.ID().String()) + } else { + for _, arg := range req.Arguments() { + name, ok := arg.(string) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + names = append(names, name) + } + } + + entries, err := resolve(n, names) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(&ResolveOutput{entries}) + }, + Type: &ResolveOutput{}, +} + +func resolve(n *core.IpfsNode, names []string) ([]IpnsEntry, error) { + var entries []IpnsEntry + for _, name := range names { + resolved, err := n.Namesys.Resolve(name) + if err != nil { + return nil, err + } + entries = append(entries, IpnsEntry{ + Name: name, + Value: resolved, + }) + } + return entries, nil +} From 852674b16777c9a460eda737a4b5cc5be72eeff4 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 18:53:25 -0800 Subject: [PATCH 165/383] chore(pin) rm commented code --- core/commands2/pin.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/core/commands2/pin.go b/core/commands2/pin.go index 4a43fa96c..2b06bdf22 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -24,19 +24,6 @@ var pinCmd = &cmds.Command{ opt, _ := req.Option("recursive") recursive, _ := opt.(bool) // false if cast fails. - /*depth := 1 // default (non recursive) - - // if recursive, set depth flag - if recursive { - opt, found := req.Option("depth") - if d, ok := opt.(int); found && ok { - depth = d - } else { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return - } - }*/ - paths, err := internal.ToStrings(req.Arguments()) if err != nil { res.SetError(err, cmds.ErrNormal) From 3dcae0e079385d007cb0e7e3b8fcf8e20391e155 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 19:05:15 -0800 Subject: [PATCH 166/383] pin/unpin -> add rm addresses https://github.com/jbenet/go-ipfs/pull/263#issuecomment-62242970 --- core/commands2/pin.go | 9 ++++++++- core/commands2/root.go | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/core/commands2/pin.go b/core/commands2/pin.go index 2b06bdf22..75e19d711 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -10,6 +10,13 @@ import ( ) var pinCmd = &cmds.Command{ + Subcommands: map[string]*cmds.Command{ + "add": addPinCmd, + "rm": rmPinCmd, + }, +} + +var addPinCmd = &cmds.Command{ Options: []cmds.Option{ cmds.Option{[]string{"recursive", "r"}, cmds.Bool}, cmds.Option{[]string{"depth", "d"}, cmds.Uint}, @@ -39,7 +46,7 @@ var pinCmd = &cmds.Command{ }, } -var unpinCmd = &cmds.Command{ +var rmPinCmd = &cmds.Command{ Options: []cmds.Option{ cmds.Option{[]string{"recursive", "r"}, cmds.Bool}, }, diff --git a/core/commands2/root.go b/core/commands2/root.go index 9417b9b7a..9cc6a228f 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -64,7 +64,6 @@ var rootSubcommands = map[string]*cmds.Command{ "log": logCmd, "net-diag": diagCmd, "pin": pinCmd, - "unpin": unpinCmd, "version": versionCmd, "config": configCmd, "bootstrap": bootstrapCmd, @@ -90,12 +89,14 @@ var rootSubcommands = map[string]*cmds.Command{ }, Type: &TestOutput{}, }, + // TODO rm "boop": &cmds.Command{ Run: func(res cmds.Response, req cmds.Request) { v := strings.NewReader("hello, world") res.SetOutput(v) }, }, + // TODO rm "warp": &cmds.Command{ Options: []cmds.Option{ cmds.Option{[]string{"power", "p"}, cmds.Float}, @@ -115,6 +116,7 @@ var rootSubcommands = map[string]*cmds.Command{ } }, }, + // TODO rm "args": &cmds.Command{ Run: func(res cmds.Response, req cmds.Request) { res.SetOutput(req.Arguments()) From 101601195c87bfae48372f989e7762ef3d63f77a Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 19:08:27 -0800 Subject: [PATCH 167/383] docs(pin) help --- core/commands2/pin.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/core/commands2/pin.go b/core/commands2/pin.go index 75e19d711..2852c5b9f 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -10,6 +10,10 @@ import ( ) var pinCmd = &cmds.Command{ + // TODO UsageLine: "pin", + // TODO Short: "", + Help: `ipfs pin [add|rm] - object pinning commands + `, Subcommands: map[string]*cmds.Command{ "add": addPinCmd, "rm": rmPinCmd, @@ -17,6 +21,13 @@ var pinCmd = &cmds.Command{ } var addPinCmd = &cmds.Command{ + // TODO UsageLine: "add", + // TODO Short: "pin an ipfs object to local storage.", + Help: `ipfs pin add - pin ipfs object to local storage. + + Retrieves the object named by and stores it locally + on disk. +`, Options: []cmds.Option{ cmds.Option{[]string{"recursive", "r"}, cmds.Bool}, cmds.Option{[]string{"depth", "d"}, cmds.Uint}, @@ -47,6 +58,13 @@ var addPinCmd = &cmds.Command{ } var rmPinCmd = &cmds.Command{ + // TODO UsageLine: "rm", + // TODO Short: "unpin an ipfs object from local storage.", + Help: `ipfs pin rm - unpin ipfs object from local storage. + + Removes the pin from the given object allowing it to be garbage + collected if needed. +`, Options: []cmds.Option{ cmds.Option{[]string{"recursive", "r"}, cmds.Bool}, }, From 2720c4f1c604b297cc7caafa1b6084bc91928ea8 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 19:20:34 -0800 Subject: [PATCH 168/383] fix(name:resolve, name:publish) docs --- core/commands2/publish.go | 2 +- core/commands2/resolve.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/commands2/publish.go b/core/commands2/publish.go index 3433f76fd..f442b3636 100644 --- a/core/commands2/publish.go +++ b/core/commands2/publish.go @@ -16,7 +16,7 @@ var errNotOnline = errors.New("This command must be run in online mode. Try runn var publishCmd = &cmds.Command{ // TODO UsageLine: "publish", // TODO Short: "publish a to ipns.", - Help: `ipfs publish [] - publish a to ipns. + Help: `ipfs name publish [] - publish a to ipns. IPNS is a PKI namespace, where names are the hashes of public keys, and the private key enables publishing new (signed) values. In publish, the diff --git a/core/commands2/resolve.go b/core/commands2/resolve.go index 3e1ad5782..d04d0a0bf 100644 --- a/core/commands2/resolve.go +++ b/core/commands2/resolve.go @@ -14,7 +14,7 @@ type ResolveOutput struct { var resolveCmd = &cmds.Command{ // TODO UsageLine: "resolve", // TODO Short: "resolve an ipns name to a ", - Help: `ipfs resolve [] - Resolve an ipns name to a . + Help: `ipfs name resolve [] - Resolve an ipns name to a . IPNS is a PKI namespace, where names are the hashes of public keys, and the private key enables publishing new (signed) values. In resolve, the From 7046ff0e57bb040e1b30b7e806ad9e3de2e8e2ef Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 19:23:46 -0800 Subject: [PATCH 169/383] net-diag -> diag net --- core/commands2/diag.go | 8 +++++++- core/commands2/root.go | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/core/commands2/diag.go b/core/commands2/diag.go index ec18476a9..e5023778d 100644 --- a/core/commands2/diag.go +++ b/core/commands2/diag.go @@ -28,9 +28,15 @@ type DiagnosticOutput struct { } var diagCmd = &cmds.Command{ + Subcommands: map[string]*cmds.Command{ + "net": diagNetCmd, + }, +} + +var diagNetCmd = &cmds.Command{ // TODO UsageLine: "net-diag", // TODO Short: "Generate a diagnostics report", - Help: `ipfs net-diag - Generate a diagnostics report. + Help: `ipfs diag net - Generate a diagnostics report. Sends out a message to each node in the network recursively requesting a listing of data about them including number of diff --git a/core/commands2/root.go b/core/commands2/root.go index 9cc6a228f..8a8933972 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -62,7 +62,7 @@ var rootSubcommands = map[string]*cmds.Command{ "name": nameCmd, "add": addCmd, "log": logCmd, - "net-diag": diagCmd, + "diag": diagCmd, "pin": pinCmd, "version": versionCmd, "config": configCmd, From 538dac153b2a24b744a911759ff7a646aa5f8537 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 7 Nov 2014 19:26:13 -0800 Subject: [PATCH 170/383] diag doc --- core/commands2/diag.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/commands2/diag.go b/core/commands2/diag.go index e5023778d..9be4eb8d5 100644 --- a/core/commands2/diag.go +++ b/core/commands2/diag.go @@ -28,6 +28,10 @@ type DiagnosticOutput struct { } var diagCmd = &cmds.Command{ + Help: `ipfs diag - Generate diagnostic reports. + + ipfs diag net - Generate a network diagnostic report. + `, Subcommands: map[string]*cmds.Command{ "net": diagNetCmd, }, @@ -36,7 +40,7 @@ var diagCmd = &cmds.Command{ var diagNetCmd = &cmds.Command{ // TODO UsageLine: "net-diag", // TODO Short: "Generate a diagnostics report", - Help: `ipfs diag net - Generate a diagnostics report. + Help: `ipfs diag net - Generate a network diagnostics report. Sends out a message to each node in the network recursively requesting a listing of data about them including number of From 351ed9589a5396f8dcfba38f0410a9100fdff712 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 7 Nov 2014 20:56:06 -0800 Subject: [PATCH 171/383] commands: Added 'Description' fields to Command, Argument, Option --- commands/argument.go | 9 +++++---- commands/command.go | 2 ++ commands/option.go | 8 +++++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/commands/argument.go b/commands/argument.go index d8b1605e9..6c9c7a3d0 100644 --- a/commands/argument.go +++ b/commands/argument.go @@ -8,8 +8,9 @@ const ( ) type Argument struct { - Name string - Type ArgumentType - Required bool - Variadic bool + Name string + Type ArgumentType + Required bool + Variadic bool + Description string } diff --git a/commands/command.go b/commands/command.go index 351b249c5..5d9f584cd 100644 --- a/commands/command.go +++ b/commands/command.go @@ -28,7 +28,9 @@ type Marshaller func(Response) ([]byte, error) // Command is a runnable command, with input arguments and options (flags). // It can also have Subcommands, to group units of work into sets. type Command struct { + Description string Help string + Options []Option Arguments []Argument Run Function diff --git a/commands/option.go b/commands/option.go index 8dbf3c1bd..f068ec228 100644 --- a/commands/option.go +++ b/commands/option.go @@ -14,8 +14,9 @@ const ( // Option is used to specify a field that will be provided by a consumer type Option struct { - Names []string // a list of unique names to - Type reflect.Kind // value must be this type + Names []string // a list of unique names to + Type reflect.Kind // value must be this type + Description string // a short string to describe this option // TODO: add more features(?): //Default interface{} // the default value (ignored if `Required` is true) @@ -30,7 +31,8 @@ const ( // options that are used by this package var globalOptions = []Option{ - Option{[]string{EncShort, EncLong}, String}, + Option{[]string{EncShort, EncLong}, String, + "The encoding type the output should be encoded with (json, xml, or text)"}, } // the above array of Options, wrapped in a Command From 218d52642cd6962b09a48eb7aae184c851da89f2 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 7 Nov 2014 21:09:18 -0800 Subject: [PATCH 172/383] core/commands2: Added descriptions for 'add', 'block' --- core/commands2/add.go | 11 ++++------- core/commands2/block.go | 33 ++++++++++++++------------------- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/core/commands2/add.go b/core/commands2/add.go index ed4a676fc..104f506ad 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -21,16 +21,13 @@ type AddOutput struct { var addCmd = &cmds.Command{ Options: []cmds.Option{ - cmds.Option{[]string{"recursive", "r"}, cmds.Bool}, + cmds.Option{[]string{"recursive", "r"}, cmds.Bool, "Must be specified when adding directories"}, }, Arguments: []cmds.Argument{ - cmds.Argument{"file", cmds.ArgFile, false, true}, + cmds.Argument{"file", cmds.ArgFile, false, true, "The path to a file to be added to IPFS"}, }, - // TODO UsageLine: "add", - // TODO Short: "Add an object to ipfs.", - Help: `ipfs add ... - Add objects to ipfs. - - Adds contents of to ipfs. Use -r to add directories. + Description: "Add an object to ipfs.", + Help: `Adds contents of to ipfs. Use -r to add directories. Note that directories are added recursively, to form the ipfs MerkleDAG. A smarter partial add with a staging area (like git) remains to be implemented. diff --git a/core/commands2/block.go b/core/commands2/block.go index 0fd571a3d..1c6fb386c 100644 --- a/core/commands2/block.go +++ b/core/commands2/block.go @@ -22,14 +22,9 @@ type Block struct { } var blockCmd = &cmds.Command{ - Help: `ipfs block - manipulate raw ipfs blocks - - ipfs block get - get and output block named by - ipfs block put - store stdin as a block, outputs - -ipfs block is a plumbing command used to manipulate raw ipfs blocks. -Reads from stdin or writes to stdout, and is a base58 encoded -multihash.`, + Description: "Manipulate raw IPFS blocks", + Help: `'ipfs block' is a plumbing command used to manipulate raw ipfs blocks. +Reads from stdin or writes to stdout.`, Subcommands: map[string]*cmds.Command{ "get": blockGetCmd, "put": blockPutCmd, @@ -37,13 +32,12 @@ multihash.`, } var blockGetCmd = &cmds.Command{ - Arguments: []cmds.Argument{ - cmds.Argument{"key", cmds.ArgString, true, false}, - }, - Help: `ipfs get - gets and outputs block named by + Description: "Get a raw IPFS block", + Help: `'ipfs block get' is a plumbing command for retreiving raw ipfs blocks.`, -'ipfs block get' is a plumbing command for retreiving raw ipfs blocks. - is a base58 encoded multihash`, + Arguments: []cmds.Argument{ + cmds.Argument{"key", cmds.ArgString, true, false, "The base58 multihash of an existing block to get"}, + }, Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node @@ -77,12 +71,13 @@ var blockGetCmd = &cmds.Command{ } var blockPutCmd = &cmds.Command{ - Arguments: []cmds.Argument{ - cmds.Argument{"data", cmds.ArgFile, true, false}, - }, - Help: `ipfs put - stores input as a block, outputs its key + Description: "Stores input as an IPFS block", + Help: `'ipfs block put' is a plumbing command for storing raw ipfs blocks. +It outputs the key of the stored block.`, -ipfs block put is a plumbing command for storing raw ipfs blocks.`, + Arguments: []cmds.Argument{ + cmds.Argument{"data", cmds.ArgFile, true, false, "The data to be stored as an IPFS block"}, + }, Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node From bb7d4683f14f7880d88af4bd29d8dd553a9aa467 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 7 Nov 2014 23:23:22 -0800 Subject: [PATCH 173/383] core/commands2: Removed test subcommands --- core/commands2/root.go | 52 ------------------------------------------ 1 file changed, 52 deletions(-) diff --git a/core/commands2/root.go b/core/commands2/root.go index 8a8933972..f41b54b86 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -70,58 +70,6 @@ var rootSubcommands = map[string]*cmds.Command{ "mount": mountCmd, "block": blockCmd, "update": updateCmd, - - // test subcommands - // TODO: remove these when we don't need them anymore - "beep": &cmds.Command{ - Run: func(res cmds.Response, req cmds.Request) { - v := &TestOutput{"hello, world", 1337} - log.Info("beep") - res.SetOutput(v) - }, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ - cmds.Text: func(res cmds.Response) ([]byte, error) { - v := res.Output().(*TestOutput) - s := fmt.Sprintf("Foo: %s\n", v.Foo) - s += fmt.Sprintf("Bar: %v\n", v.Bar) - return []byte(s), nil - }, - }, - Type: &TestOutput{}, - }, - // TODO rm - "boop": &cmds.Command{ - Run: func(res cmds.Response, req cmds.Request) { - v := strings.NewReader("hello, world") - res.SetOutput(v) - }, - }, - // TODO rm - "warp": &cmds.Command{ - Options: []cmds.Option{ - cmds.Option{[]string{"power", "p"}, cmds.Float}, - }, - Run: func(res cmds.Response, req cmds.Request) { - threshold := 1.21 - - if power, found := req.Option("power"); found && power.(float64) >= threshold { - res.SetOutput(struct { - Status string - Power float64 - }{"Flux capacitor activated!", power.(float64)}) - - } else { - err := fmt.Errorf("Insufficient power (%v jiggawatts required)", threshold) - res.SetError(err, cmds.ErrClient) - } - }, - }, - // TODO rm - "args": &cmds.Command{ - Run: func(res cmds.Response, req cmds.Request) { - res.SetOutput(req.Arguments()) - }, - }, } func init() { From c2615d34810a8de3e472d4fd102a5153abb30d5c Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 01:02:22 -0800 Subject: [PATCH 174/383] core/commands2: Added descriptions for all the existing commands --- core/commands2/bootstrap.go | 43 ++++++++++++++++--------------- core/commands2/cat.go | 13 +++++----- core/commands2/commands.go | 8 +++--- core/commands2/config.go | 32 +++++++++++++---------- core/commands2/diag.go | 16 +++++------- core/commands2/log.go | 22 ++++++++-------- core/commands2/ls.go | 21 ++++++--------- core/commands2/mount_unix.go | 21 ++++++++------- core/commands2/mount_windows.go | 4 ++- core/commands2/name.go | 13 +++------- core/commands2/pin.go | 45 +++++++++++++++------------------ core/commands2/publish.go | 17 ++++++------- core/commands2/resolve.go | 13 ++++------ core/commands2/root.go | 25 +++++++++--------- core/commands2/update.go | 23 ++++++++++------- core/commands2/version.go | 13 +++++----- 16 files changed, 160 insertions(+), 169 deletions(-) diff --git a/core/commands2/bootstrap.go b/core/commands2/bootstrap.go index 2a5782f14..26b15e809 100644 --- a/core/commands2/bootstrap.go +++ b/core/commands2/bootstrap.go @@ -10,26 +10,19 @@ import ( cmds "github.com/jbenet/go-ipfs/commands" config "github.com/jbenet/go-ipfs/config" - //peer "github.com/jbenet/go-ipfs/peer" - //u "github.com/jbenet/go-ipfs/util" ) type BootstrapOutput struct { Peers []*config.BootstrapPeer } +var peerOptionDesc = "A peer to add to the bootstrap list (in the format '/')" + var bootstrapCmd = &cmds.Command{ - Help: `ipfs bootstrap - show, or manipulate bootstrap node addresses - -Running 'ipfs bootstrap' with no arguments will run 'ipfs bootstrap list'. - -Commands: - - list Show the boostrap list. - add
Add a node's address to the bootstrap list. - remove
Remove an address from the bootstrap list. - + Description: "Show or edit the list of bootstrap peers", + Help: `Running 'ipfs bootstrap' with no arguments will run 'ipfs bootstrap list'. ` + bootstrapSecurityWarning, + Run: bootstrapListCmd.Run, Marshallers: bootstrapListCmd.Marshallers, Subcommands: map[string]*cmds.Command{ @@ -40,11 +33,14 @@ Commands: } var bootstrapAddCmd = &cmds.Command{ - Arguments: []cmds.Argument{ - cmds.Argument{"peer", cmds.ArgString, true, true}, - }, - Help: `ipfs bootstrap add - add addresses to the bootstrap list + Description: "Add peers to the bootstrap list", + Help: `Outputs a list of peers that were added (that weren't already +in the bootstrap list). ` + bootstrapSecurityWarning, + + Arguments: []cmds.Argument{ + cmds.Argument{"peer", cmds.ArgString, true, true, peerOptionDesc}, + }, Run: func(res cmds.Response, req cmds.Request) { input, err := bootstrapInputToPeers(req.Arguments()) if err != nil { @@ -81,11 +77,13 @@ var bootstrapAddCmd = &cmds.Command{ } var bootstrapRemoveCmd = &cmds.Command{ - Arguments: []cmds.Argument{ - cmds.Argument{"peer", cmds.ArgString, true, true}, - }, - Help: `ipfs bootstrap remove - remove addresses from the bootstrap list + Description: "Removes peers from the bootstrap list", + Help: `Outputs the list of peers that were removed. ` + bootstrapSecurityWarning, + + Arguments: []cmds.Argument{ + cmds.Argument{"peer", cmds.ArgString, true, true, peerOptionDesc}, + }, Run: func(res cmds.Response, req cmds.Request) { input, err := bootstrapInputToPeers(req.Arguments()) if err != nil { @@ -122,7 +120,10 @@ var bootstrapRemoveCmd = &cmds.Command{ } var bootstrapListCmd = &cmds.Command{ - Help: "ipfs bootstrap list - Show addresses in the bootstrap list", + Description: "Lists peers in the bootstrap list", + Help: `Peers are output in the format '/'. +`, + Run: func(res cmds.Response, req cmds.Request) { peers := req.Context().Config.Bootstrap res.SetOutput(&BootstrapOutput{peers}) diff --git a/core/commands2/cat.go b/core/commands2/cat.go index 62f67b91d..7acfe5435 100644 --- a/core/commands2/cat.go +++ b/core/commands2/cat.go @@ -10,14 +10,15 @@ import ( ) var catCmd = &cmds.Command{ - Arguments: []cmds.Argument{ - cmds.Argument{"object", cmds.ArgString, true, true}, - }, - Help: `ipfs cat - Show ipfs object data. - - Retrieves the object named by and outputs the data + Description: "Show IPFS object data", + Help: `Retrieves the object named by and outputs the data it contains. `, + + Arguments: []cmds.Argument{ + cmds.Argument{"ipfs-path", cmds.ArgString, true, true, + "The path to the IPFS object(s) to be outputted"}, + }, Run: func(res cmds.Response, req cmds.Request) { node := req.Context().Node readers := make([]io.Reader, 0, len(req.Arguments())) diff --git a/core/commands2/commands.go b/core/commands2/commands.go index f1d7f4fc6..6a5ab653d 100644 --- a/core/commands2/commands.go +++ b/core/commands2/commands.go @@ -13,12 +13,10 @@ type Command struct { } var commandsCmd = &cmds.Command{ - // TODO UsageLine: "commands", - // TODO Short: "List all available commands.", - Help: `ipfs commands - List all available commands. + Description: "List all available commands.", + Help: `Lists all available commands (and subcommands) and exits. +`, - Lists all available commands (and sub-commands) and exits. - `, Run: func(res cmds.Response, req cmds.Request) { root := outputCommand("ipfs", Root) res.SetOutput(&root) diff --git a/core/commands2/config.go b/core/commands2/config.go index 1eaf2332d..e0583b4ca 100644 --- a/core/commands2/config.go +++ b/core/commands2/config.go @@ -18,18 +18,8 @@ type ConfigField struct { } var configCmd = &cmds.Command{ - Arguments: []cmds.Argument{ - cmds.Argument{"key", cmds.ArgString, true, false}, - cmds.Argument{"value", cmds.ArgString, false, false}, - }, - Help: `ipfs config [value] - Get/Set ipfs config values. - - ipfs config - Get value of - ipfs config - Set value of to - ipfs config show - Show config file - ipfs config edit - Edit config file in $EDITOR - -Examples: + Description: "Get/set IPFS config values", + Help: `Examples: Get the value of the 'datastore.path' key: @@ -38,8 +28,14 @@ Examples: Set the value of the 'datastore.path' key: ipfs config datastore.path ~/.go-ipfs/datastore - `, + + Arguments: []cmds.Argument{ + cmds.Argument{"key", cmds.ArgString, true, false, + "The key of the config entry (e.g. \"Addresses.API\")"}, + cmds.Argument{"value", cmds.ArgString, false, false, + "The value to set the config entry to"}, + }, Run: func(res cmds.Response, req cmds.Request) { args := req.Arguments() @@ -110,6 +106,11 @@ Examples: } var configShowCmd = &cmds.Command{ + Description: "Outputs the content of the config file", + Help: `WARNING: Your private key is stored in the config file, and it will be +included in the output of this command. +`, + Run: func(res cmds.Response, req cmds.Request) { filename, err := config.Filename(req.Context().ConfigRoot) if err != nil { @@ -128,6 +129,11 @@ var configShowCmd = &cmds.Command{ } var configEditCmd = &cmds.Command{ + Description: "Opens the config file for editing in $EDITOR", + Help: `To use 'ipfs config edit', you must have the $EDITOR environment +variable set to your preferred text editor. +`, + Run: func(res cmds.Response, req cmds.Request) { filename, err := config.Filename(req.Context().ConfigRoot) if err != nil { diff --git a/core/commands2/diag.go b/core/commands2/diag.go index 9be4eb8d5..ef1abf150 100644 --- a/core/commands2/diag.go +++ b/core/commands2/diag.go @@ -28,24 +28,20 @@ type DiagnosticOutput struct { } var diagCmd = &cmds.Command{ - Help: `ipfs diag - Generate diagnostic reports. + Description: "Generates diagnostic reports", - ipfs diag net - Generate a network diagnostic report. - `, Subcommands: map[string]*cmds.Command{ "net": diagNetCmd, }, } var diagNetCmd = &cmds.Command{ - // TODO UsageLine: "net-diag", - // TODO Short: "Generate a diagnostics report", - Help: `ipfs diag net - Generate a network diagnostics report. - - Sends out a message to each node in the network recursively - requesting a listing of data about them including number of - connected peers and latencies between them. + Description: "Generates a network diagnostics report", + Help: `Sends out a message to each node in the network recursively +requesting a listing of data about them including number of +connected peers and latencies between them. `, + Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node diff --git a/core/commands2/log.go b/core/commands2/log.go index 510085c85..742aab526 100644 --- a/core/commands2/log.go +++ b/core/commands2/log.go @@ -8,19 +8,17 @@ import ( ) var logCmd = &cmds.Command{ - Arguments: []cmds.Argument{ - cmds.Argument{"subsystem", cmds.ArgString, true, false}, - cmds.Argument{"level", cmds.ArgString, true, false}, - }, - // TODO UsageLine: "log ", - // TODO Short: "switch logging levels of a running daemon", - Help: `ipfs log - switch logging levels of a running daemon - - is a the subsystem logging identifier. Use * for all subsystems. - is one of: debug, info, notice, warning, error, critical - -ipfs log is a utility command used to change the logging output of a running daemon. + Description: "Change the logging level", + Help: `'ipfs log' is a utility command used to change the logging +output of a running daemon. `, + + Arguments: []cmds.Argument{ + cmds.Argument{"subsystem", cmds.ArgString, true, false, + "the subsystem logging identifier. Use * for all subsystems."}, + cmds.Argument{"level", cmds.ArgString, true, false, + "one of: debug, info, notice, warning, error, critical"}, + }, Run: func(res cmds.Response, req cmds.Request) { args := req.Arguments() if err := u.SetLogLevel(args[0].(string), args[1].(string)); err != nil { diff --git a/core/commands2/ls.go b/core/commands2/ls.go index 569bfde65..c94e48e3d 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -23,20 +23,15 @@ type LsOutput struct { } var lsCmd = &cmds.Command{ - Arguments: []cmds.Argument{ - cmds.Argument{"object", cmds.ArgString, false, true}, - }, - // TODO UsageLine: "ls", - // TODO Short: "List links from an object.", - // TODO docs read ipfs-path. argument says option. which? - Help: `ipfs ls - List links from an object. - - Retrieves the object named by and displays the links - it contains, with the following format: - - - + Description: "List links from an object.", + Help: `Retrieves the object named by and displays the links +it contains. `, + + Arguments: []cmds.Argument{ + cmds.Argument{"ipfs-path", cmds.ArgString, false, true, + "The path to the IPFS object(s) to list links from"}, + }, Run: func(res cmds.Response, req cmds.Request) { node := req.Context().Node diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go index a7d894d0d..2946459b0 100644 --- a/core/commands2/mount_unix.go +++ b/core/commands2/mount_unix.go @@ -17,25 +17,24 @@ import ( const mountTimeout = time.Second var mountCmd = &cmds.Command{ - Arguments: []cmds.Argument{ - cmds.Argument{Name: "os-path", Type: cmds.ArgString, Required: false, Variadic: false}, - }, + Description: "Mounts IPFS to the filesystem (read-only)", + Help: `Mount ipfs at a read-only mountpoint on the OS. All ipfs objects +will be accessible under that directory. Note that the root will +not be listable, as it is virtual. Accessing known paths directly. +`, + Options: []cmds.Option{ // TODO text: specify a mountpoint for ipfs // TODO longform - cmds.Option{[]string{"f"}, cmds.String}, + cmds.Option{[]string{"f"}, cmds.String, + "The path where IPFS should be mounted (default is '/ipfs')"}, // TODO text: specify a mountpoint for ipns // TODO longform - cmds.Option{[]string{"n"}, cmds.String}, + cmds.Option{[]string{"n"}, cmds.String, + "The path where IPNS should be mounted (default is '/ipns')"}, }, - Help: `ipfs mount - Mount an ipfs read-only mountpoint. - - Mount ipfs at a read-only mountpoint on the OS. All ipfs objects - will be accessible under that directory. Note that the root will - not be listable, as it is virtual. Accessing known paths directly. -`, Run: func(res cmds.Response, req cmds.Request) { ctx := req.Context() diff --git a/core/commands2/mount_windows.go b/core/commands2/mount_windows.go index 2660605c2..278192c6d 100644 --- a/core/commands2/mount_windows.go +++ b/core/commands2/mount_windows.go @@ -7,7 +7,9 @@ import ( ) var ipfsMount = &cmds.Command{ - Help: `Not yet implemented on Windows.`, + Description: "Not yet implemented on Windows", + Help: `Not yet implemented on Windows. :(`, + Run: func(res cmds.Response, req cmds.Request) { res.SetError(errors.New("Mount isn't compatible with Windows yet"), cmds.ErrNormal) }, diff --git a/core/commands2/name.go b/core/commands2/name.go index 7066641f8..33971d9ca 100644 --- a/core/commands2/name.go +++ b/core/commands2/name.go @@ -8,14 +8,8 @@ type IpnsEntry struct { } var nameCmd = &cmds.Command{ - // TODO UsageLine: "name [publish | resolve]", - // TODO Short: "ipfs namespace (ipns) tool", - Help: `ipfs name - Get/Set ipfs config values. - - ipfs name publish [] - Assign the to - ipfs name resolve [] - Resolve the value of - -IPNS is a PKI namespace, where names are the hashes of public keys, and + Description: "IPFS namespace (IPNS) tool", + Help: `IPNS is a PKI namespace, where names are the hashes of public keys, and the private key enables publishing new (signed) values. In both publish and resolve, the default value of is your own identity public key. @@ -37,12 +31,13 @@ Resolve the value of your identity: > ipfs name resolve QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy -Resolve te value of another name: +Resolve the value of another name: > ipfs name resolve QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy `, + Subcommands: map[string]*cmds.Command{ "publish": publishCmd, "resolve": resolveCmd, diff --git a/core/commands2/pin.go b/core/commands2/pin.go index 2852c5b9f..076d252c8 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -10,10 +10,8 @@ import ( ) var pinCmd = &cmds.Command{ - // TODO UsageLine: "pin", - // TODO Short: "", - Help: `ipfs pin [add|rm] - object pinning commands - `, + Description: "Keeps objects stored locally", + Subcommands: map[string]*cmds.Command{ "add": addPinCmd, "rm": rmPinCmd, @@ -21,19 +19,18 @@ var pinCmd = &cmds.Command{ } var addPinCmd = &cmds.Command{ - // TODO UsageLine: "add", - // TODO Short: "pin an ipfs object to local storage.", - Help: `ipfs pin add - pin ipfs object to local storage. - - Retrieves the object named by and stores it locally - on disk. + Description: "Pins objects to local storage", + Help: `Keeps the object(s) named by in local storage. If the object +isn't already being stored, IPFS retrieves it. `, - Options: []cmds.Option{ - cmds.Option{[]string{"recursive", "r"}, cmds.Bool}, - cmds.Option{[]string{"depth", "d"}, cmds.Uint}, - }, + Arguments: []cmds.Argument{ - cmds.Argument{"object", cmds.ArgString, true, true}, + cmds.Argument{"ipfs-path", cmds.ArgString, true, true, + "Path to object(s) to be pinned"}, + }, + Options: []cmds.Option{ + cmds.Option{[]string{"recursive", "r"}, cmds.Bool, + "Recursively pin the object linked to by the specified object(s)"}, }, Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node @@ -58,18 +55,18 @@ var addPinCmd = &cmds.Command{ } var rmPinCmd = &cmds.Command{ - // TODO UsageLine: "rm", - // TODO Short: "unpin an ipfs object from local storage.", - Help: `ipfs pin rm - unpin ipfs object from local storage. - - Removes the pin from the given object allowing it to be garbage + Description: "Unpin an object from local storage", + Help: `Removes the pin from the given object allowing it to be garbage collected if needed. `, - Options: []cmds.Option{ - cmds.Option{[]string{"recursive", "r"}, cmds.Bool}, - }, + Arguments: []cmds.Argument{ - cmds.Argument{"object", cmds.ArgString, true, true}, + cmds.Argument{"ipfs-path", cmds.ArgString, true, true, + "Path to object(s) to be unpinned"}, + }, + Options: []cmds.Option{ + cmds.Option{[]string{"recursive", "r"}, cmds.Bool, + "Recursively unpin the object linked to by the specified object(s)"}, }, Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node diff --git a/core/commands2/publish.go b/core/commands2/publish.go index f442b3636..59c6f9072 100644 --- a/core/commands2/publish.go +++ b/core/commands2/publish.go @@ -14,13 +14,9 @@ import ( var errNotOnline = errors.New("This command must be run in online mode. Try running 'ipfs daemon' first.") var publishCmd = &cmds.Command{ - // TODO UsageLine: "publish", - // TODO Short: "publish a to ipns.", - Help: `ipfs name publish [] - publish a to ipns. - -IPNS is a PKI namespace, where names are the hashes of public keys, and -the private key enables publishing new (signed) values. In publish, the -default value of is your own identity public key. + Description: "Publish an object to IPNS", + Help: `IPNS is a PKI namespace, where names are the hashes of public keys, and +the private key enables publishing new (signed) values. Examples: @@ -35,9 +31,12 @@ Publish a to another public key: published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy `, + Arguments: []cmds.Argument{ - cmds.Argument{"name", cmds.ArgString, false, false}, - cmds.Argument{"object", cmds.ArgString, true, false}, + cmds.Argument{"name", cmds.ArgString, false, false, + "The IPNS name to publish to. Defaults to your node's peerID"}, + cmds.Argument{"ipfs-path", cmds.ArgString, true, false, + "IPFS path of the obejct to be published at "}, }, Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node diff --git a/core/commands2/resolve.go b/core/commands2/resolve.go index d04d0a0bf..ecb6e276e 100644 --- a/core/commands2/resolve.go +++ b/core/commands2/resolve.go @@ -12,13 +12,9 @@ type ResolveOutput struct { } var resolveCmd = &cmds.Command{ - // TODO UsageLine: "resolve", - // TODO Short: "resolve an ipns name to a ", - Help: `ipfs name resolve [] - Resolve an ipns name to a . - -IPNS is a PKI namespace, where names are the hashes of public keys, and -the private key enables publishing new (signed) values. In resolve, the -default value of is your own identity public key. + Description: "Gets the value currently published at an IPNS name", + Help: `IPNS is a PKI namespace, where names are the hashes of public keys, and +the private key enables publishing new (signed) values. Examples: @@ -36,7 +32,8 @@ Resolve te value of another name: `, Arguments: []cmds.Argument{ - cmds.Argument{"name", cmds.ArgString, false, true}, + cmds.Argument{"name", cmds.ArgString, false, true, + "The IPNS name to resolve. Defaults to your node's peerID."}, }, Run: func(res cmds.Response, req cmds.Request) { diff --git a/core/commands2/root.go b/core/commands2/root.go index f41b54b86..f6a2a1898 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -1,9 +1,6 @@ package commands import ( - "fmt" - "strings" - cmds "github.com/jbenet/go-ipfs/commands" u "github.com/jbenet/go-ipfs/util" ) @@ -16,15 +13,8 @@ type TestOutput struct { } var Root = &cmds.Command{ - Options: []cmds.Option{ - cmds.Option{[]string{"config", "c"}, cmds.String}, - cmds.Option{[]string{"debug", "D"}, cmds.Bool}, - cmds.Option{[]string{"help", "h"}, cmds.Bool}, - cmds.Option{[]string{"local", "L"}, cmds.Bool}, - }, - Help: `ipfs - global versioned p2p merkledag file system - -Basic commands: + Description: "Global P2P Merkle-DAG filesystem", + Help: `Basic commands: init Initialize ipfs local configuration. add Add an object to ipfs. @@ -53,6 +43,17 @@ Plumbing commands: Use "ipfs help " for more information about a command. `, + + Options: []cmds.Option{ + cmds.Option{[]string{"config", "c"}, cmds.String, + "Path to the configuration file to use"}, + cmds.Option{[]string{"debug", "D"}, cmds.Bool, + "Operate in debug mode"}, + cmds.Option{[]string{"help", "h"}, cmds.Bool, + "Show the command help text"}, + cmds.Option{[]string{"local", "L"}, cmds.Bool, + "Run the command locally, instead of using the daemon"}, + }, } var rootSubcommands = map[string]*cmds.Command{ diff --git a/core/commands2/update.go b/core/commands2/update.go index 9e1edd186..8702e0154 100644 --- a/core/commands2/update.go +++ b/core/commands2/update.go @@ -15,14 +15,10 @@ type UpdateOutput struct { } var updateCmd = &cmds.Command{ - Help: `ipfs update - check for updates and apply them - - ipfs update - apply - ipfs update check - just check - ipfs update log - list the changelogs - -ipfs update is a utility command used to check for updates and apply them. + Description: "Downloads and installs updates for IPFS", + Help: `ipfs update is a utility command used to check for updates and apply them. `, + Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node @@ -55,7 +51,12 @@ ipfs update is a utility command used to check for updates and apply them. } var updateCheckCmd = &cmds.Command{ - Help: `ipfs update check `, + Description: "Checks if updates are available", + Help: `'ipfs update check' checks if any updates are available for IPFS. + +Nothing will be downloaded or installed. +`, + Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node @@ -84,7 +85,10 @@ var updateCheckCmd = &cmds.Command{ } var updateLogCmd = &cmds.Command{ - Help: `ipfs update log - list the last versions and their changelog`, + Description: "List the changelog for the latest versions of IPFS", + Help: `This command is not yet implemented. +`, + Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node @@ -151,5 +155,6 @@ func updateCheck(n *core.IpfsNode) (*UpdateOutput, error) { // updateLog lists the version available online func updateLog(n *core.IpfsNode) (interface{}, error) { + // TODO return nil, errors.New("Not yet implemented") } diff --git a/core/commands2/version.go b/core/commands2/version.go index 019c6959b..9d085dd61 100644 --- a/core/commands2/version.go +++ b/core/commands2/version.go @@ -12,13 +12,14 @@ type VersionOutput struct { } var versionCmd = &cmds.Command{ - Options: []cmds.Option{ - cmds.Option{[]string{"number", "n"}, cmds.Bool}, - }, - Help: `ipfs version - Show ipfs version information. + Description: "Outputs the current version of IPFS", + Help: `Returns the version number of IPFS. +`, - Returns the current version of ipfs and exits. - `, + Options: []cmds.Option{ + cmds.Option{[]string{"number", "n"}, cmds.Bool, + "Only output the version number"}, + }, Run: func(res cmds.Response, req cmds.Request) { res.SetOutput(&VersionOutput{ Version: config.CurrentVersionNumber, From 2b8ef917b8fb17c9a1c77f20da3523e9d8d56b6c Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 01:02:53 -0800 Subject: [PATCH 175/383] cmd/ipfs2: Added descriptions for 'init', 'tour' --- cmd/ipfs2/init.go | 23 +++++++++++++---------- cmd/ipfs2/tour.go | 32 +++++++++++++------------------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 241929f52..ae639f510 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -16,17 +16,20 @@ import ( ) var initCmd = &cmds.Command{ - Options: []cmds.Option{ - cmds.Option{[]string{"bits", "b"}, cmds.Int}, - cmds.Option{[]string{"passphrase", "p"}, cmds.String}, - cmds.Option{[]string{"force", "f"}, cmds.Bool}, - cmds.Option{[]string{"datastore", "d"}, cmds.String}, - }, - Help: `ipfs init + Description: "Initializes IPFS config file", + Help: `Initializes IPFS configuration files and generates a new keypair. +`, - Initializes ipfs configuration files and generates a - new keypair. - `, + Options: []cmds.Option{ + cmds.Option{[]string{"bits", "b"}, cmds.Int, + "Number of bits to use in the generated RSA private key (defaults to 4096)"}, + cmds.Option{[]string{"passphrase", "p"}, cmds.String, + "Passphrase for encrypting the private key"}, + cmds.Option{[]string{"force", "f"}, cmds.Bool, + "Overwrite existing config (if it exists)"}, + cmds.Option{[]string{"datastore", "d"}, cmds.String, + "Location for the IPFS data store"}, + }, Run: func(res cmds.Response, req cmds.Request) { arg, found := req.Option("d") diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index afc5964af..9f3cba2ee 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -13,27 +13,18 @@ import ( ) var cmdTour = &cmds.Command{ - - Arguments: []cmds.Argument{ - cmds.Argument{"number", cmds.ArgString, false, false}, - }, - - // TODO UsageLine: "tour []", - // TODO Short: "Take the IPFS Tour.", - - Help: `ipfs tour - Take the IPFS Tour. - - ipfs tour [] - Show tour topic. Default to current. - ipfs tour next - Show the next tour topic. - ipfs tour list - Show a list of topics. - ipfs tour restart - Restart the tour. - -This is a tour that takes you through various IPFS concepts, + Description: "An introduction to IPFS", + Help: `This is a tour that takes you through various IPFS concepts, features, and tools to make sure you get up to speed with IPFS very quickly. To start, run: ipfs tour `, + + Arguments: []cmds.Argument{ + cmds.Argument{"number", cmds.ArgString, false, false, + "The number of the topic you would like to tour"}, + }, Subcommands: map[string]*cmds.Command{ "list": cmdIpfsTourList, "next": cmdIpfsTourNext, @@ -65,7 +56,8 @@ IPFS very quickly. To start, run: } var cmdIpfsTourNext = &cmds.Command{ - Help: "Show the next IPFS Tour topic.", + Description: "Show the next IPFS Tour topic", + Run: func(res cmds.Response, req cmds.Request) { var w bytes.Buffer cfg := req.Context().Config @@ -92,7 +84,8 @@ var cmdIpfsTourNext = &cmds.Command{ } var cmdIpfsTourRestart = &cmds.Command{ - Help: "Restart the IPFS Tour.", + Description: "Restart the IPFS Tour", + Run: func(res cmds.Response, req cmds.Request) { path := req.Context().ConfigRoot cfg := req.Context().Config @@ -106,7 +99,8 @@ var cmdIpfsTourRestart = &cmds.Command{ } var cmdIpfsTourList = &cmds.Command{ - Help: "Show a list of IPFS Tour topics.", + Description: "Show a list of IPFS Tour topics", + Run: func(res cmds.Response, req cmds.Request) { var w bytes.Buffer tourListCmd(&w, req.Context().Config) From c8696a8d322f4649c70f2ba6f6de1cd4bdc36476 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 01:51:13 -0800 Subject: [PATCH 176/383] core/commands2: Better formatted descriptions for 'mount' --- core/commands2/mount_unix.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go index 2946459b0..e82240380 100644 --- a/core/commands2/mount_unix.go +++ b/core/commands2/mount_unix.go @@ -28,12 +28,12 @@ not be listable, as it is virtual. Accessing known paths directly. // TODO text: specify a mountpoint for ipfs // TODO longform cmds.Option{[]string{"f"}, cmds.String, - "The path where IPFS should be mounted (default is '/ipfs')"}, + "The path where IPFS should be mounted\n(default is '/ipfs')"}, // TODO text: specify a mountpoint for ipns // TODO longform cmds.Option{[]string{"n"}, cmds.String, - "The path where IPNS should be mounted (default is '/ipns')"}, + "The path where IPNS should be mounted\n(default is '/ipns')"}, }, Run: func(res cmds.Response, req cmds.Request) { ctx := req.Context() From c169fca5a2826af8bb360bb12e8d7e9886de6fdb Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 01:52:39 -0800 Subject: [PATCH 177/383] commands/cli: Added a helptext generator --- commands/cli/helptext.go | 193 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 commands/cli/helptext.go diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go new file mode 100644 index 000000000..cbf3da94a --- /dev/null +++ b/commands/cli/helptext.go @@ -0,0 +1,193 @@ +package cli + +import ( + "fmt" + "strings" + + cmds "github.com/jbenet/go-ipfs/commands" +) + +const ( + requiredArg = "<%v>" + optionalArg = "[<%v>]" + variadicArg = "%v..." + optionFlag = "-%v" + optionType = "(%v)" + + whitespace = "\r\n\t " +) + +// HelpText returns a formatted CLI helptext string, generated for the given command +func HelpText(rootName string, root *cmds.Command, path []string) (string, error) { + cmd, err := root.Get(path) + if err != nil { + return "", err + } + + s := "" + usage := usageText(cmd) + if len(usage) > 0 { + usage += " " + } + s += fmt.Sprintf("%v %v %v- %v\n\n", rootName, strings.Join(path, " "), usage, cmd.Description) + + if len(cmd.Help) > 0 { + s += fmt.Sprintf("%v\n\n", strings.Trim(cmd.Help, whitespace)) + } + + if cmd.Arguments != nil { + lines := indent(argumentText(cmd), " ") + s += fmt.Sprintf("Arguments:\n%v\n\n", strings.Join(lines, "\n")) + } + + if cmd.Subcommands != nil { + lines := indent(subcommandText(cmd, rootName, path), " ") + s += fmt.Sprintf("Subcommands:\n%v\n\n", strings.Join(lines, "\n")) + } + + if cmd.Options != nil { + lines := indent(optionText(cmd), " ") + s += fmt.Sprintf("Options:\n%v\n\n", strings.Join(lines, "\n")) + } + + return s, nil +} + +func argumentText(cmd *cmds.Command) []string { + lines := make([]string, len(cmd.Arguments)) + + for i, arg := range cmd.Arguments { + lines[i] = argUsageText(arg) + lines[i] += "\n" + arg.Description + lines[i] = indentString(lines[i], " ") + "\n" + } + + return lines +} + +func optionText(cmd ...*cmds.Command) []string { + // get a slice of the options we want to list out + options := make([]cmds.Option, 0) + for _, c := range cmd { + for _, opt := range c.Options { + options = append(options, opt) + } + } + + // add option names to output (with each name aligned) + lines := make([]string, 0) + j := 0 + for { + done := true + i := 0 + for _, opt := range options { + if len(lines) < i+1 { + lines = append(lines, "") + } + if len(opt.Names) >= j+1 { + lines[i] += fmt.Sprintf(optionFlag, opt.Names[j]) + } + if len(opt.Names) > j+1 { + lines[i] += ", " + done = false + } + + i++ + } + + if done { + break + } + + lines = align(lines) + j++ + } + + // add option types to output + for i, opt := range options { + lines[i] += " " + fmt.Sprintf(optionType, opt.Type) + } + lines = align(lines) + + // add option descriptions to output + for i, opt := range options { + lines[i] += "\n" + opt.Description + lines[i] = indentString(lines[i], " ") + "\n" + } + + return lines +} + +func subcommandText(cmd *cmds.Command, rootName string, path []string) []string { + prefix := fmt.Sprintf("%v %v", rootName, strings.Join(path, " ")) + lines := make([]string, len(cmd.Subcommands)) + + i := 0 + for name, sub := range cmd.Subcommands { + usage := usageText(sub) + lines[i] = fmt.Sprintf("%v %v %v", prefix, name, usage) + lines[i] += fmt.Sprintf("\n%v", sub.Description) + lines[i] = indentString(lines[i], " ") + "\n" + i++ + } + + return lines +} + +func usageText(cmd *cmds.Command) string { + s := "" + for i, arg := range cmd.Arguments { + if i != 0 { + s += " " + } + s += argUsageText(arg) + } + + return s +} + +func argUsageText(arg cmds.Argument) string { + s := arg.Name + + if arg.Required { + s = fmt.Sprintf(requiredArg, s) + } else { + s = fmt.Sprintf(optionalArg, s) + } + + if arg.Variadic { + s = fmt.Sprintf(variadicArg, s) + } + + return s +} + +func align(lines []string) []string { + longest := 0 + for _, line := range lines { + length := len(line) + if length > longest { + longest = length + } + } + + for i, line := range lines { + length := len(line) + if length > 0 { + lines[i] += strings.Repeat(" ", longest-length) + } + } + + return lines +} + +func indent(lines []string, prefix string) []string { + for i, line := range lines { + lines[i] = prefix + indentString(line, prefix) + } + return lines +} + +func indentString(line string, prefix string) string { + return strings.Replace(line, "\n", "\n"+prefix, -1) +} From c827573e322fe6d7a049a040a0adac0a05d8b390 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 03:17:23 -0800 Subject: [PATCH 178/383] commands/cli: Helptext spacing fix for root command output --- commands/cli/helptext.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index cbf3da94a..3a82d58ad 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -120,12 +120,15 @@ func optionText(cmd ...*cmds.Command) []string { func subcommandText(cmd *cmds.Command, rootName string, path []string) []string { prefix := fmt.Sprintf("%v %v", rootName, strings.Join(path, " ")) + if len(path) > 0 { + prefix += " " + } lines := make([]string, len(cmd.Subcommands)) i := 0 for name, sub := range cmd.Subcommands { usage := usageText(sub) - lines[i] = fmt.Sprintf("%v %v %v", prefix, name, usage) + lines[i] = fmt.Sprintf("%v%v %v", prefix, name, usage) lines[i] += fmt.Sprintf("\n%v", sub.Description) lines[i] = indentString(lines[i], " ") + "\n" i++ From 3255bb02d1b1835675a7710766e5af6059ceb921 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 03:18:00 -0800 Subject: [PATCH 179/383] commands/cli: Output command path in Parse --- commands/cli/parse.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index 8972fa96d..e9e91fbfe 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -11,7 +11,7 @@ import ( // Parse parses the input commandline string (cmd, flags, and args). // returns the corresponding command Request object. -func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, *cmds.Command, error) { +func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, *cmds.Command, []string, error) { var root, cmd *cmds.Command var path, stringArgs []string var opts map[string]interface{} @@ -22,7 +22,7 @@ func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, p, i, c := parsePath(input, r) o, s, err := parseOptions(i) if err != nil { - return nil, root, c, err + return nil, root, c, p, err } length := len(p) @@ -37,22 +37,22 @@ func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, } if maxLength == 0 { - return nil, root, nil, errors.New("Not a valid subcommand") + return nil, root, nil, path, errors.New("Not a valid subcommand") } args, err := parseArgs(stringArgs, cmd) if err != nil { - return nil, root, cmd, err + return nil, root, cmd, path, err } req := cmds.NewRequest(path, opts, args, cmd) err = cmd.CheckArguments(req) if err != nil { - return nil, root, cmd, err + return req, root, cmd, path, err } - return req, root, cmd, nil + return req, root, cmd, path, nil } // parsePath separates the command path and the opts and args from a command string From 165c69543bb37c64b6dca975cbfda8a4d5116ee1 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 03:18:58 -0800 Subject: [PATCH 180/383] cmd/ipfs2: Output generated help text on error or help flag --- cmd/ipfs2/main.go | 58 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index f56094685..2660b5b49 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -41,22 +41,49 @@ func main() { } func createRequest(args []string) (cmds.Request, *cmds.Command) { - req, root, cmd, err := cmdsCli.Parse(args, Root, commands.Root) - if err != nil { - fmt.Println(err) - if cmd != nil { - if cmd.Help != "" { - fmt.Println(cmd.Help) - } - } else { - fmt.Println(Root.Help) + req, root, cmd, path, err := cmdsCli.Parse(args, Root, commands.Root) + + var options cmds.Request + if req != nil && root != nil { + var err2 error + options, err2 = getOptions(req, root) + if err2 != nil { + fmt.Println(err2) + os.Exit(1) } - os.Exit(1) } - options, err := getOptions(req, root) + // handle parse error (which means the commandline input was wrong, + // e.g. incorrect number of args, or nonexistent subcommand) if err != nil { - fmt.Println(err) + // if the -help flag wasn't specified, show the error message + if options != nil { + opt, _ := options.Option("help") + help, _ := opt.(bool) + if !help { + fmt.Println(err) + } + + } else if path != nil && len(path) > 0 { + // if a path was returned (user specified a valid subcommand), show the error message + // (this means there was an option or argument error) + fmt.Println(err) + } + + // when generating help for the root command, we don't want the autogenerated subcommand text + // (since we have better hand-made subcommand list in the root Help field) + if cmd == nil { + root = &*commands.Root + root.Subcommands = nil + } + + // generate the help text for the command the user was trying to call (or root) + helpText, err := cmdsCli.HelpText("ipfs", root, path) + if err != nil { + fmt.Println(err) + } else { + fmt.Println(helpText) + } os.Exit(1) } @@ -96,7 +123,12 @@ func handleOptions(req cmds.Request, root *cmds.Command) { if help, found := options.Option("help"); found { if helpBool, ok := help.(bool); helpBool && ok { - fmt.Println(req.Command().Help) + helpText, err := cmdsCli.HelpText("ipfs", root, req.Path()) + if err != nil { + fmt.Println(err.Error()) + } else { + fmt.Println(helpText) + } os.Exit(0) } else if !ok { fmt.Println("error: expected 'help' option to be a bool") From 92f2e8440a7d2858f9c56729ed1021238f666807 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 03:19:23 -0800 Subject: [PATCH 181/383] core/commands2: Updated root help text --- core/commands2/root.go | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/core/commands2/root.go b/core/commands2/root.go index f6a2a1898..1afac44f4 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -16,24 +16,23 @@ var Root = &cmds.Command{ Description: "Global P2P Merkle-DAG filesystem", Help: `Basic commands: - init Initialize ipfs local configuration. - add Add an object to ipfs. - cat Show ipfs object data. - ls List links from an object. - refs List link hashes from an object. + init Initialize ipfs local configurationx + add Add an object to ipfs + cat Show ipfs object data + ls List links from an object Tool commands: - config Manage configuration. - update Download and apply go-ipfs updates. - version Show ipfs version information. - commands List all available commands. + config Manage configuration + update Download and apply go-ipfs updates + version Show ipfs version information + commands List all available commands Advanced Commands: - mount Mount an ipfs read-only mountpoint. - serve Serve an interface to ipfs. - net-diag Print network diagnostic + mount Mount an ipfs read-only mountpoint + serve Serve an interface to ipfs + diag Print diagnostics Plumbing commands: @@ -41,7 +40,7 @@ Plumbing commands: object Interact with raw dag nodes -Use "ipfs help " for more information about a command. +Use "ipfs --help" for more information about a command. `, Options: []cmds.Option{ From cdf221078d8910c97e46560cce531a1c6d4817d3 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 03:25:53 -0800 Subject: [PATCH 182/383] commands: Fixed tests --- commands/command_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/commands/command_test.go b/commands/command_test.go index c41f1ce96..c113a324b 100644 --- a/commands/command_test.go +++ b/commands/command_test.go @@ -5,8 +5,8 @@ import "testing" func TestOptionValidation(t *testing.T) { cmd := Command{ Options: []Option{ - Option{[]string{"b", "beep"}, Int}, - Option{[]string{"B", "boop"}, String}, + Option{[]string{"b", "beep"}, Int, "enables beeper"}, + Option{[]string{"B", "boop"}, String, "password for booper"}, }, Run: func(res Response, req Request) {}, } @@ -83,14 +83,14 @@ func TestRegistration(t *testing.T) { cmdA := &Command{ Options: []Option{ - Option{[]string{"beep"}, Int}, + Option{[]string{"beep"}, Int, "number of beeps"}, }, Run: noop, } cmdB := &Command{ Options: []Option{ - Option{[]string{"beep"}, Int}, + Option{[]string{"beep"}, Int, "number of beeps"}, }, Run: noop, Subcommands: map[string]*Command{ @@ -100,7 +100,7 @@ func TestRegistration(t *testing.T) { cmdC := &Command{ Options: []Option{ - Option{[]string{"encoding"}, String}, + Option{[]string{"encoding"}, String, "data encoding type"}, }, Run: noop, } From c542cb52aa082d2e346979e02c30996accfb7d93 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 19:56:44 -0800 Subject: [PATCH 183/383] core/commands2: Added 'object' command --- core/commands2/add.go | 21 ++- core/commands2/object.go | 297 +++++++++++++++++++++++++++++++++++++++ core/commands2/root.go | 1 + 3 files changed, 313 insertions(+), 6 deletions(-) create mode 100644 core/commands2/object.go diff --git a/core/commands2/add.go b/core/commands2/add.go index 104f506ad..c96a920c4 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -97,12 +97,7 @@ func add(n *core.IpfsNode, readers []io.Reader) ([]*dag.Node, error) { return nil, err } - err = n.DAG.AddRecursive(node) // add the file to the graph + local storage - if err != nil { - return nil, err - } - - err = n.Pinning.Pin(node, true) // ensure we keep it + err = addNode(n, node) if err != nil { return nil, err } @@ -111,3 +106,17 @@ func add(n *core.IpfsNode, readers []io.Reader) ([]*dag.Node, error) { } return dagnodes, nil } + +func addNode(n *core.IpfsNode, node *dag.Node) error { + err := n.DAG.AddRecursive(node) // add the file to the graph + local storage + if err != nil { + return err + } + + err = n.Pinning.Pin(node, true) // ensure we keep it + if err != nil { + return err + } + + return nil +} diff --git a/core/commands2/object.go b/core/commands2/object.go new file mode 100644 index 000000000..3affff3c7 --- /dev/null +++ b/core/commands2/object.go @@ -0,0 +1,297 @@ +package commands + +import ( + "bytes" + "encoding/json" + "errors" + "io" + "io/ioutil" + + cmds "github.com/jbenet/go-ipfs/commands" + "github.com/jbenet/go-ipfs/core" + dag "github.com/jbenet/go-ipfs/merkledag" +) + +// 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 + +var objectCmd = &cmds.Command{ + Description: "Interact with ipfs objects", + Help: `'ipfs object' is a plumbing command used to manipulate DAG objects directly.`, + + Subcommands: map[string]*cmds.Command{ + "data": objectDataCmd, + "links": objectLinksCmd, + "get": objectGetCmd, + "put": objectPutCmd, + }, +} + +var objectDataCmd = &cmds.Command{ + Description: "Outputs the raw bytes in an IPFS object", + Help: `ipfs data is a plumbing command for retreiving the raw bytes stored in a DAG node. +It outputs to stdout, and is a base58 encoded multihash. + +Note that the "--encoding" option does not affect the output, since the +output is the raw data of the object. +`, + + Arguments: []cmds.Argument{ + cmds.Argument{"key", cmds.ArgString, true, false, + "Key of the object to retrieve, in base58-encoded multihash format"}, + }, + Run: func(res cmds.Response, req cmds.Request) { + n := req.Context().Node + + key, ok := req.Arguments()[0].(string) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + + reader, err := objectData(n, key) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(reader) + }, +} + +var objectLinksCmd = &cmds.Command{ + Description: "Outputs the links pointed to by the specified object", + Help: `'ipfs block get' is a plumbing command for retreiving raw IPFS blocks. +It outputs to stdout, and is a base58 encoded multihash.`, + + Arguments: []cmds.Argument{ + cmds.Argument{"key", cmds.ArgString, true, false, + "Key of the object to retrieve, in base58-encoded multihash format"}, + }, + Run: func(res cmds.Response, req cmds.Request) { + n := req.Context().Node + + key, ok := req.Arguments()[0].(string) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + + output, err := objectLinks(n, key) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(output) + }, + Type: &Object{}, +} + +var objectGetCmd = &cmds.Command{ + Description: "Get and serialize the DAG node named by ", + Help: `'ipfs object get' is a plumbing command for retreiving DAG nodes. +It serializes the DAG node to the format specified by the "--encoding" flag. +It outputs to stdout, and is a base58 encoded multihash. + +This command outputs data in the following encodings: "protobuf", "json", "xml" +(Specified by the "--encoding" flag)`, + + Arguments: []cmds.Argument{ + cmds.Argument{"key", cmds.ArgString, true, false, + "Key of the object to retrieve, in base58-encoded multihash format"}, + }, + Run: func(res cmds.Response, req cmds.Request) { + n := req.Context().Node + + key, ok := req.Arguments()[0].(string) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + + object, err := objectGet(n, key) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(object) + }, + Type: &dag.Node{}, + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.EncodingType("protobuf"): func(res cmds.Response) ([]byte, error) { + object := res.Output().(*dag.Node) + return object.Marshal() + }, + }, +} + +var objectPutCmd = &cmds.Command{ + Description: "Stores input as a DAG object, outputs its key", + Help: `'ipfs object put' is a plumbing command for storing DAG nodes. +It reads from stdin, and the output is a base58 encoded multihash. + +Data should be in the format specified by . + may be one of the following: + * "protobuf" + * "json" +`, + + Arguments: []cmds.Argument{ + cmds.Argument{"data", cmds.ArgFile, true, false, + "Data to be stored as a DAG object, encoded as specified in "}, + cmds.Argument{"encoding", cmds.ArgString, true, false, + "Encoding type of , either \"protobuf\" or \"json\""}, + }, + Run: func(res cmds.Response, req cmds.Request) { + n := req.Context().Node + + input, ok := req.Arguments()[0].(io.Reader) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + + encoding, ok := req.Arguments()[1].(string) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return + } + + output, err := objectPut(n, input, encoding) + if err != nil { + errType := cmds.ErrNormal + if err == ErrUnknownObjectEnc { + errType = cmds.ErrClient + } + res.SetError(err, errType) + return + } + + res.SetOutput(output) + }, + Type: &Object{}, +} + +// objectData takes a key string and writes out the raw bytes of that node (if there is one) +func objectData(n *core.IpfsNode, key string) (io.Reader, error) { + dagnode, err := n.Resolver.ResolvePath(key) + if err != nil { + return nil, err + } + + log.Debugf("objectData: found dagnode %q (# of bytes: %d - # links: %d)", key, len(dagnode.Data), len(dagnode.Links)) + + return bytes.NewReader(dagnode.Data), nil +} + +// objectLinks takes a key string and lists the links it points to +func objectLinks(n *core.IpfsNode, key string) (*Object, error) { + dagnode, err := n.Resolver.ResolvePath(key) + if err != nil { + return nil, err + } + + log.Debugf("objectLinks: found dagnode %q (# of bytes: %d - # links: %d)", key, len(dagnode.Data), len(dagnode.Links)) + + return getOutput(dagnode) +} + +// objectGet takes a key string from args and a format option and serializes the dagnode to that format +func objectGet(n *core.IpfsNode, key string) (*dag.Node, error) { + dagnode, err := n.Resolver.ResolvePath(key) + if err != nil { + return nil, err + } + + log.Debugf("objectGet: found dagnode %q (# of bytes: %d - # links: %d)", key, len(dagnode.Data), len(dagnode.Links)) + + return dagnode, nil +} + +// objectPut takes a format option, serializes bytes from stdin and updates the dag with that data +func objectPut(n *core.IpfsNode, input io.Reader, encoding string) (*Object, error) { + var ( + dagnode *dag.Node + data []byte + err error + ) + + data, err = ioutil.ReadAll(io.LimitReader(input, inputLimit+10)) + if err != nil { + return nil, err + } + + if len(data) >= inputLimit { + return nil, ErrObjectTooLarge + } + + switch getObjectEnc(encoding) { + case objectEncodingJSON: + dagnode = new(dag.Node) + err = json.Unmarshal(data, dagnode) + + case objectEncodingProtobuf: + dagnode, err = dag.Decoded(data) + + default: + return nil, ErrUnknownObjectEnc + } + + if err != nil { + return nil, err + } + + err = addNode(n, dagnode) + if err != nil { + return nil, err + } + + return getOutput(dagnode) +} + +// 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) +} + +func getOutput(dagnode *dag.Node) (*Object, error) { + key, err := dagnode.Key() + if err != nil { + return nil, err + } + + output := &Object{ + Hash: key.Pretty(), + Links: make([]Link, len(dagnode.Links)), + } + + for i, link := range dagnode.Links { + output.Links[i] = Link{ + Name: link.Name, + Hash: link.Hash.B58String(), + Size: link.Size, + } + } + + return output, nil +} diff --git a/core/commands2/root.go b/core/commands2/root.go index 1afac44f4..e66c71cf6 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -70,6 +70,7 @@ var rootSubcommands = map[string]*cmds.Command{ "mount": mountCmd, "block": blockCmd, "update": updateCmd, + "object": objectCmd, } func init() { From 3d94e89dd1a9177479ab9866b0ef8c4f4e12401b Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 20:13:20 -0800 Subject: [PATCH 184/383] cmd/ipfs2: Made error messages more visible --- cmd/ipfs2/main.go | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 2660b5b49..11d7c9622 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -23,14 +23,17 @@ import ( // log is the command logger var log = u.Logger("cmd/ipfs") -const heapProfile = "ipfs.mprof" +const ( + heapProfile = "ipfs.mprof" + errorFormat = "ERROR: %v\n\n" +) func main() { args := os.Args[1:] req, root := createRequest(args) handleOptions(req, root) res := callCommand(req, root) - outputResponse(res) + outputResponse(res, root) if u.Debug { err := writeHeapProfileToFile() @@ -57,17 +60,18 @@ func createRequest(args []string) (cmds.Request, *cmds.Command) { // e.g. incorrect number of args, or nonexistent subcommand) if err != nil { // if the -help flag wasn't specified, show the error message - if options != nil { - opt, _ := options.Option("help") - help, _ := opt.(bool) - if !help { - fmt.Println(err) + // or if a path was returned (user specified a valid subcommand), show the error message + // (this means there was an option or argument error) + if options != nil || path != nil && len(path) > 0 { + help := false + if options != nil { + opt, _ := options.Option("help") + help, _ = opt.(bool) } - } else if path != nil && len(path) > 0 { - // if a path was returned (user specified a valid subcommand), show the error message - // (this means there was an option or argument error) - fmt.Println(err) + if !help { + fmt.Printf(errorFormat, err) + } } // when generating help for the root command, we don't want the autogenerated subcommand text @@ -222,13 +226,17 @@ func callCommand(req cmds.Request, root *cmds.Command) cmds.Response { return res } -func outputResponse(res cmds.Response) { +func outputResponse(res cmds.Response, root *cmds.Command) { if res.Error() != nil { - fmt.Println(res.Error().Error()) + fmt.Printf(errorFormat, res.Error().Error()) - if res.Request().Command().Help != "" && res.Error().Code == cmds.ErrClient { - // TODO: convert from markdown to ANSI terminal format? - fmt.Println(res.Request().Command().Help) + if res.Error().Code == cmds.ErrClient { + helpText, err := cmdsCli.HelpText("ipfs", root, res.Request().Path()) + if err != nil { + fmt.Println(err.Error()) + } else { + fmt.Println(helpText) + } } os.Exit(1) From c8345bb5c64849115d4b5bac8cb0507f217cf54d Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 20:13:39 -0800 Subject: [PATCH 185/383] core/commands2: 'object' description formatting improvements --- core/commands2/object.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/core/commands2/object.go b/core/commands2/object.go index 3affff3c7..22ce11322 100644 --- a/core/commands2/object.go +++ b/core/commands2/object.go @@ -96,12 +96,15 @@ var objectGetCmd = &cmds.Command{ It serializes the DAG node to the format specified by the "--encoding" flag. It outputs to stdout, and is a base58 encoded multihash. -This command outputs data in the following encodings: "protobuf", "json", "xml" -(Specified by the "--encoding" flag)`, +This command outputs data in the following encodings: + * "protobuf" + * "json" + * "xml" +(Specified by the "--encoding" or "-enc" flags)`, Arguments: []cmds.Argument{ cmds.Argument{"key", cmds.ArgString, true, false, - "Key of the object to retrieve, in base58-encoded multihash format"}, + "Key of the object to retrieve\n(in base58-encoded multihash format)"}, }, Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node @@ -142,7 +145,7 @@ Data should be in the format specified by . Arguments: []cmds.Argument{ cmds.Argument{"data", cmds.ArgFile, true, false, - "Data to be stored as a DAG object, encoded as specified in "}, + "Data to be stored as a DAG object\nMust be encoded as specified in "}, cmds.Argument{"encoding", cmds.ArgString, true, false, "Encoding type of , either \"protobuf\" or \"json\""}, }, From 20285ead82384d87145994548dd4e18ac1f5629e Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 20:19:27 -0800 Subject: [PATCH 186/383] commands: Indent JSON-marshalled Response output --- commands/response.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commands/response.go b/commands/response.go index c79734528..912eddb0a 100644 --- a/commands/response.go +++ b/commands/response.go @@ -43,9 +43,9 @@ const ( var marshallers = map[EncodingType]Marshaller{ JSON: func(res Response) ([]byte, error) { if res.Error() != nil { - return json.Marshal(res.Error()) + return json.MarshalIndent(res.Error(), "", " ") } - return json.Marshal(res.Output()) + return json.MarshalIndent(res.Output(), "", " ") }, XML: func(res Response) ([]byte, error) { if res.Error() != nil { From 652e3d0523e5de427877e7b33cf8844cfad0b9e8 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 20:31:41 -0800 Subject: [PATCH 187/383] core/commands2: Fixed 'add' output adding an empty object --- core/commands2/add.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/commands2/add.go b/core/commands2/add.go index c96a920c4..349a5be92 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -16,7 +16,7 @@ import ( var ErrDepthLimitExceeded = fmt.Errorf("depth limit exceeded") type AddOutput struct { - Added []Object + Added []*Object } var addCmd = &cmds.Command{ @@ -24,7 +24,7 @@ var addCmd = &cmds.Command{ cmds.Option{[]string{"recursive", "r"}, cmds.Bool, "Must be specified when adding directories"}, }, Arguments: []cmds.Argument{ - cmds.Argument{"file", cmds.ArgFile, false, true, "The path to a file to be added to IPFS"}, + cmds.Argument{"file", cmds.ArgFile, true, true, "The path to a file to be added to IPFS"}, }, Description: "Add an object to ipfs.", Help: `Adds contents of to ipfs. Use -r to add directories. @@ -52,15 +52,15 @@ var addCmd = &cmds.Command{ return } - added := make([]Object, len(req.Arguments())) + added := make([]*Object, 0, len(req.Arguments())) for _, dagnode := range dagnodes { - - k, err := dagnode.Key() + object, err := getOutput(dagnode) if err != nil { res.SetError(err, cmds.ErrNormal) return } - added = append(added, Object{Hash: k.String(), Links: nil}) + + added = append(added, object) } res.SetOutput(&AddOutput{added}) @@ -88,7 +88,6 @@ var addCmd = &cmds.Command{ } func add(n *core.IpfsNode, readers []io.Reader) ([]*dag.Node, error) { - dagnodes := make([]*dag.Node, 0) for _, reader := range readers { @@ -104,6 +103,7 @@ func add(n *core.IpfsNode, readers []io.Reader) ([]*dag.Node, error) { dagnodes = append(dagnodes, node) } + return dagnodes, nil } From 5a18554f6e2f725b30fdca60091fa91bbc5b1569 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 20:45:07 -0800 Subject: [PATCH 188/383] commands: Fixed tests --- commands/response_test.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/commands/response_test.go b/commands/response_test.go index ef6240673..c8033f2f9 100644 --- a/commands/response_test.go +++ b/commands/response_test.go @@ -2,6 +2,7 @@ package commands import ( "fmt" + "strings" "testing" ) @@ -36,17 +37,25 @@ func TestMarshalling(t *testing.T) { t.Error(err, "Should have passed") } output := string(bytes) - if output != "{\"Foo\":\"beep\",\"Bar\":\"boop\",\"Baz\":1337}" { + if removeWhitespace(output) != "{\"Foo\":\"beep\",\"Bar\":\"boop\",\"Baz\":1337}" { t.Error("Incorrect JSON output") } - res.SetError(fmt.Errorf("You broke something!"), ErrClient) + res.SetError(fmt.Errorf("Oops!"), ErrClient) bytes, err = res.Marshal() if err != nil { t.Error("Should have passed") } output = string(bytes) - if output != "{\"Message\":\"You broke something!\",\"Code\":1}" { + fmt.Println(removeWhitespace(output)) + if removeWhitespace(output) != "{\"Message\":\"Oops!\",\"Code\":1}" { t.Error("Incorrect JSON output") } } + +func removeWhitespace(input string) string { + input = strings.Replace(input, " ", "", -1) + input = strings.Replace(input, "\t", "", -1) + input = strings.Replace(input, "\n", "", -1) + return strings.Replace(input, "\r", "", -1) +} From 897e0f869f9ec6300a5615a061cdee26402bd654 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 21:09:29 -0800 Subject: [PATCH 189/383] cmds/ipfs2: Added '/ipfs' HTTP handling --- cmd/ipfs2/daemon.go | 7 +++- cmd/ipfs2/ipfsHandler.go | 86 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 cmd/ipfs2/ipfsHandler.go diff --git a/cmd/ipfs2/daemon.go b/cmd/ipfs2/daemon.go index da3603cad..50d605e7c 100644 --- a/cmd/ipfs2/daemon.go +++ b/cmd/ipfs2/daemon.go @@ -50,8 +50,11 @@ func daemonFunc(res cmds.Response, req cmds.Request) { return } - handler := cmdsHttp.NewHandler(*ctx, commands.Root) - http.Handle(cmdsHttp.ApiPath+"/", handler) + cmdHandler := cmdsHttp.NewHandler(*ctx, commands.Root) + http.Handle(cmdsHttp.ApiPath+"/", cmdHandler) + + ifpsHandler := &ipfsHandler{node} + http.Handle("/ipfs/", ifpsHandler) fmt.Printf("API server listening on '%s'\n", host) diff --git a/cmd/ipfs2/ipfsHandler.go b/cmd/ipfs2/ipfsHandler.go new file mode 100644 index 000000000..46336026b --- /dev/null +++ b/cmd/ipfs2/ipfsHandler.go @@ -0,0 +1,86 @@ +package main + +import ( + "io" + "net/http" + + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + + core "github.com/jbenet/go-ipfs/core" + "github.com/jbenet/go-ipfs/importer" + dag "github.com/jbenet/go-ipfs/merkledag" + uio "github.com/jbenet/go-ipfs/unixfs/io" + u "github.com/jbenet/go-ipfs/util" +) + +type ipfs interface { + ResolvePath(string) (*dag.Node, error) + NewDagFromReader(io.Reader) (*dag.Node, error) + AddNodeToDAG(nd *dag.Node) (u.Key, error) + NewDagReader(nd *dag.Node) (io.Reader, error) +} + +type ipfsHandler struct { + node *core.IpfsNode +} + +func (i *ipfsHandler) ResolvePath(path string) (*dag.Node, error) { + return i.node.Resolver.ResolvePath(path) +} + +func (i *ipfsHandler) NewDagFromReader(r io.Reader) (*dag.Node, error) { + return importer.NewDagFromReader(r) +} + +func (i *ipfsHandler) AddNodeToDAG(nd *dag.Node) (u.Key, error) { + return i.node.DAG.Add(nd) +} + +func (i *ipfsHandler) NewDagReader(nd *dag.Node) (io.Reader, error) { + return uio.NewDagReader(nd, i.node.DAG) +} + +func (i *ipfsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + path := r.URL.Path[5:] + + nd, err := i.ResolvePath(path) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + log.Error(err) + w.Write([]byte(err.Error())) + return + } + + dr, err := i.NewDagReader(nd) + if err != nil { + // TODO: return json object containing the tree data if it's a directory (err == ErrIsDir) + w.WriteHeader(http.StatusInternalServerError) + log.Error(err) + w.Write([]byte(err.Error())) + return + } + + io.Copy(w, dr) +} + +func (i *ipfsHandler) postHandler(w http.ResponseWriter, r *http.Request) { + nd, err := i.NewDagFromReader(r.Body) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + log.Error(err) + w.Write([]byte(err.Error())) + return + } + + k, err := i.AddNodeToDAG(nd) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + log.Error(err) + w.Write([]byte(err.Error())) + return + } + + //TODO: return json representation of list instead + w.WriteHeader(http.StatusCreated) + w.Write([]byte(mh.Multihash(k).B58String())) +} From 35983b480ad73d09101d2a2ba32da0d672e8412f Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 21:41:57 -0800 Subject: [PATCH 190/383] cmd/ipfs2: Made '/ipfs' handler return more accurate HTTP response codes, resolves #287 --- cmd/ipfs2/ipfsHandler.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/cmd/ipfs2/ipfsHandler.go b/cmd/ipfs2/ipfsHandler.go index 46336026b..b522607b0 100644 --- a/cmd/ipfs2/ipfsHandler.go +++ b/cmd/ipfs2/ipfsHandler.go @@ -4,11 +4,13 @@ import ( "io" "net/http" + "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" core "github.com/jbenet/go-ipfs/core" "github.com/jbenet/go-ipfs/importer" dag "github.com/jbenet/go-ipfs/merkledag" + "github.com/jbenet/go-ipfs/routing" uio "github.com/jbenet/go-ipfs/unixfs/io" u "github.com/jbenet/go-ipfs/util" ) @@ -45,7 +47,14 @@ func (i *ipfsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { nd, err := i.ResolvePath(path) if err != nil { - w.WriteHeader(http.StatusInternalServerError) + if err == routing.ErrNotFound { + w.WriteHeader(http.StatusNotFound) + } else if err == context.DeadlineExceeded { + w.WriteHeader(http.StatusRequestTimeout) + } else { + w.WriteHeader(http.StatusBadRequest) + } + log.Error(err) w.Write([]byte(err.Error())) return From f1c788d71072c24ea491d1c25bb40238a1522ac9 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sat, 8 Nov 2014 21:59:26 -0800 Subject: [PATCH 191/383] commands/http: Don't set Content-Type for stream outputs so browsers can MIME-sniff the actual content type --- commands/http/client.go | 2 +- commands/http/handler.go | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/commands/http/client.go b/commands/http/client.go index 748f50365..9e8c1d3a7 100644 --- a/commands/http/client.go +++ b/commands/http/client.go @@ -131,7 +131,7 @@ func getResponse(httpRes *http.Response, req cmds.Request) (cmds.Response, error contentType := httpRes.Header["Content-Type"][0] contentType = strings.Split(contentType, ";")[0] - if contentType == "application/octet-stream" { + if len(httpRes.Header.Get(streamHeader)) > 0 { res.SetOutput(httpRes.Body) return res, nil } diff --git a/commands/http/handler.go b/commands/http/handler.go index e5959b4a8..a8fec766e 100644 --- a/commands/http/handler.go +++ b/commands/http/handler.go @@ -18,6 +18,8 @@ type Handler struct { var ErrNotFound = errors.New("404 page not found") +const streamHeader = "X-Stream-Output" + var mimeTypes = map[string]string{ cmds.JSON: "application/json", cmds.XML: "application/xml", @@ -48,8 +50,12 @@ func (i Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // set the Content-Type based on res output if _, ok := res.Output().(io.Reader); ok { - // TODO: set based on actual Content-Type of file - w.Header().Set("Content-Type", "application/octet-stream") + // we don't set the Content-Type for streams, so that browsers can MIME-sniff the type themselves + // we set this header so clients have a way to know this is an output stream + // (not marshalled command output) + // TODO: set a specific Content-Type if the command response needs it to be a certain type + w.Header().Set(streamHeader, "1") + } else { enc, _ := req.Option(cmds.EncShort) encStr, ok := enc.(string) From f3733873de14682bb44186f9d2b4ddb2cd0befe1 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sun, 9 Nov 2014 00:50:14 -0800 Subject: [PATCH 192/383] core/commands2: Fixed 'object get' dagnode output format --- core/commands2/object.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/core/commands2/object.go b/core/commands2/object.go index 22ce11322..bc08dcebe 100644 --- a/core/commands2/object.go +++ b/core/commands2/object.go @@ -17,6 +17,11 @@ var ErrObjectTooLarge = errors.New("input object was too large. limit is 512kbyt const inputLimit = 512 * 1024 +type Node struct { + Links []Link + Data []byte +} + var objectCmd = &cmds.Command{ Description: "Interact with ipfs objects", Help: `'ipfs object' is a plumbing command used to manipulate DAG objects directly.`, @@ -121,9 +126,22 @@ This command outputs data in the following encodings: return } - res.SetOutput(object) + node := &Node{ + Links: make([]Link, len(object.Links)), + Data: object.Data, + } + + for i, link := range object.Links { + node.Links[i] = Link{ + Hash: link.Hash.B58String(), + Name: link.Name, + Size: link.Size, + } + } + + res.SetOutput(node) }, - Type: &dag.Node{}, + Type: &Node{}, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ cmds.EncodingType("protobuf"): func(res cmds.Response) ([]byte, error) { object := res.Output().(*dag.Node) From cc519c47a3778a3a0b4160c980f3e5ff02913baf Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sun, 9 Nov 2014 00:56:47 -0800 Subject: [PATCH 193/383] cmd/ipfs2: Ensure process exits smoothly --- cmd/ipfs2/main.go | 78 +++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 11d7c9622..dc0d3fbc7 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -28,19 +28,29 @@ const ( errorFormat = "ERROR: %v\n\n" ) +var ofi io.WriteCloser + func main() { args := os.Args[1:] req, root := createRequest(args) handleOptions(req, root) + + // if debugging, setup profiling. + if u.Debug { + var err error + ofi, err = os.Create("cpu.prof") + if err != nil { + fmt.Println(err) + return + } + + pprof.StartCPUProfile(ofi) + } + res := callCommand(req, root) outputResponse(res, root) - if u.Debug { - err := writeHeapProfileToFile() - if err != nil { - log.Critical(err) - } - } + exit(0) } func createRequest(args []string) (cmds.Request, *cmds.Command) { @@ -52,7 +62,7 @@ func createRequest(args []string) (cmds.Request, *cmds.Command) { options, err2 = getOptions(req, root) if err2 != nil { fmt.Println(err2) - os.Exit(1) + exit(1) } } @@ -88,19 +98,19 @@ func createRequest(args []string) (cmds.Request, *cmds.Command) { } else { fmt.Println(helpText) } - os.Exit(1) + exit(1) } configPath, err := getConfigRoot(options) if err != nil { fmt.Println(err) - os.Exit(1) + exit(1) } conf, err := getConfig(configPath) if err != nil { fmt.Println(err) - os.Exit(1) + exit(1) } ctx := req.Context() @@ -122,7 +132,7 @@ func handleOptions(req cmds.Request, root *cmds.Command) { options, err := getOptions(req, root) if err != nil { fmt.Println(err) - os.Exit(1) + exit(1) } if help, found := options.Option("help"); found { @@ -133,10 +143,10 @@ func handleOptions(req cmds.Request, root *cmds.Command) { } else { fmt.Println(helpText) } - os.Exit(0) + exit(0) } else if !ok { fmt.Println("error: expected 'help' option to be a bool") - os.Exit(1) + exit(1) } } @@ -145,21 +155,9 @@ func handleOptions(req cmds.Request, root *cmds.Command) { u.Debug = true u.SetAllLoggers(logging.DEBUG) - - // if debugging, setup profiling. - if u.Debug { - ofi, err := os.Create("cpu.prof") - if err != nil { - fmt.Println(err) - return - } - pprof.StartCPUProfile(ofi) - defer ofi.Close() - defer pprof.StopCPUProfile() - } } else if !ok { fmt.Println("error: expected 'debug' option to be a bool") - os.Exit(1) + exit(1) } } } @@ -174,7 +172,7 @@ func callCommand(req cmds.Request, root *cmds.Command) cmds.Response { options, err := getOptions(req, root) if err != nil { fmt.Println(err) - os.Exit(1) + exit(1) } var found bool @@ -185,7 +183,7 @@ func callCommand(req cmds.Request, root *cmds.Command) cmds.Response { localBool, ok = local.(bool) if !ok { fmt.Println("error: expected 'local' option to be a bool") - os.Exit(1) + exit(1) } } @@ -193,13 +191,13 @@ func callCommand(req cmds.Request, root *cmds.Command) cmds.Response { addr, err := ma.NewMultiaddr(req.Context().Config.Addresses.API) if err != nil { fmt.Println(err) - os.Exit(1) + exit(1) } _, host, err := manet.DialArgs(addr) if err != nil { fmt.Println(err) - os.Exit(1) + exit(1) } client := cmdsHttp.NewClient(host) @@ -207,14 +205,14 @@ func callCommand(req cmds.Request, root *cmds.Command) cmds.Response { res, err = client.Send(req) if err != nil { fmt.Println(err) - os.Exit(1) + exit(1) } } else { node, err := core.NewIpfsNode(req.Context().Config, false) if err != nil { fmt.Println(err) - os.Exit(1) + exit(1) } defer node.Close() req.Context().Node = node @@ -239,7 +237,7 @@ func outputResponse(res cmds.Response, root *cmds.Command) { } } - os.Exit(1) + exit(1) } out, err := res.Reader() @@ -300,3 +298,17 @@ func writeHeapProfileToFile() error { defer mprof.Close() return pprof.WriteHeapProfile(mprof) } + +func exit(code int) { + if u.Debug { + pprof.StopCPUProfile() + ofi.Close() + + err := writeHeapProfileToFile() + if err != nil { + log.Critical(err) + } + } + + os.Exit(code) +} From b3ea49954a173177c84155b66a887d223c4f07ac Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sun, 9 Nov 2014 00:57:20 -0800 Subject: [PATCH 194/383] cmd/ipfs2: Handle SIGTERM --- cmd/ipfs2/main.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index dc0d3fbc7..fafba34e7 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "os" + "os/signal" "runtime/pprof" logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-logging" @@ -31,6 +32,8 @@ const ( var ofi io.WriteCloser func main() { + handleInterrupt() + args := os.Args[1:] req, root := createRequest(args) handleOptions(req, root) @@ -299,6 +302,19 @@ func writeHeapProfileToFile() error { return pprof.WriteHeapProfile(mprof) } +// listen for and handle SIGTERM +func handleInterrupt() { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + + go func() { + for _ = range c { + log.Info("Received interrupt signal, terminating...") + exit(0) + } + }() +} + func exit(code int) { if u.Debug { pprof.StopCPUProfile() From 8ffd27a6e172c27f621150890ec1e84db99cc1a6 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 9 Nov 2014 02:03:28 -0800 Subject: [PATCH 195/383] fix(init) inline bootstrap peers --- cmd/ipfs2/init.go | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index ae639f510..e4b76d89a 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -63,15 +63,6 @@ var initCmd = &cmds.Command{ }, } -// Use these hardcoded bootstrap peers for now. -var defaultPeers = []*config.BootstrapPeer{ - &config.BootstrapPeer{ - // mars.i.ipfs.io - PeerID: "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", - Address: "/ip4/104.131.131.82/tcp/4001", - }, -} - // TODO add default welcome hash: eaa68bedae247ed1e5bd0eb4385a3c0959b976e4 func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) error { @@ -108,7 +99,13 @@ func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) e API: "/ip4/127.0.0.1/tcp/5001", }, - Bootstrap: defaultPeers, + Bootstrap: []*config.BootstrapPeer{ + &config.BootstrapPeer{ // Use these hardcoded bootstrap peers for now. + // mars.i.ipfs.io + PeerID: "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + Address: "/ip4/104.131.131.82/tcp/4001", + }, + }, Datastore: ds, From 38a6d0ead0f4332676311221858637f447007f63 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 9 Nov 2014 02:05:24 -0800 Subject: [PATCH 196/383] fix(init) version --- cmd/ipfs2/init.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index e4b76d89a..7f7cc05cb 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -11,7 +11,6 @@ import ( config "github.com/jbenet/go-ipfs/config" ci "github.com/jbenet/go-ipfs/crypto" peer "github.com/jbenet/go-ipfs/peer" - updates "github.com/jbenet/go-ipfs/updates" u "github.com/jbenet/go-ipfs/util" ) @@ -119,13 +118,7 @@ func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) e // tracking ipfs version used to generate the init folder and adding // update checker default setting. - - // FIXME(brian): before merging into master, change this to... - // Version: config.VersionDefaultValue() - Version: config.Version{ - Check: "error", - Current: updates.Version, - }, + Version: config.VersionDefaultValue(), } err = config.WriteConfigFile(configFilename, conf) From f4d0b8baa046810d001f13755ab8d7d7ff667c9f Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Sun, 9 Nov 2014 16:13:49 -0800 Subject: [PATCH 197/383] clarity(util) ToReaders -> CastToReaders --- cmd/ipfs2/tour.go | 2 +- core/commands2/add.go | 2 +- core/commands2/cat.go | 2 +- core/commands2/internal/slice_util.go | 4 ++-- core/commands2/ls.go | 2 +- core/commands2/pin.go | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index 9f3cba2ee..0c0928479 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -34,7 +34,7 @@ IPFS very quickly. To start, run: out := new(bytes.Buffer) cfg := req.Context().Config - strs, err := internal.ToStrings(req.Arguments()) + strs, err := internal.CastToStrings(req.Arguments()) if err != nil { res.SetError(err, cmds.ErrNormal) return diff --git a/core/commands2/add.go b/core/commands2/add.go index 349a5be92..adc5a9993 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -40,7 +40,7 @@ var addCmd = &cmds.Command{ // if r, _ := opt.(bool); found && r { // } - readers, err := internal.ToReaders(req.Arguments()) + readers, err := internal.CastToReaders(req.Arguments()) if err != nil { res.SetError(err, cmds.ErrNormal) return diff --git a/core/commands2/cat.go b/core/commands2/cat.go index 7acfe5435..b1c8ed336 100644 --- a/core/commands2/cat.go +++ b/core/commands2/cat.go @@ -23,7 +23,7 @@ var catCmd = &cmds.Command{ node := req.Context().Node readers := make([]io.Reader, 0, len(req.Arguments())) - paths, err := internal.ToStrings(req.Arguments()) + paths, err := internal.CastToStrings(req.Arguments()) if err != nil { res.SetError(err, cmds.ErrNormal) return diff --git a/core/commands2/internal/slice_util.go b/core/commands2/internal/slice_util.go index e2d1ef54a..2f9cf45c4 100644 --- a/core/commands2/internal/slice_util.go +++ b/core/commands2/internal/slice_util.go @@ -7,7 +7,7 @@ import ( var CastErr = errors.New("cast error") -func ToReaders(slice []interface{}) ([]io.Reader, error) { +func CastToReaders(slice []interface{}) ([]io.Reader, error) { readers := make([]io.Reader, 0) for _, arg := range slice { reader, ok := arg.(io.Reader) @@ -19,7 +19,7 @@ func ToReaders(slice []interface{}) ([]io.Reader, error) { return readers, nil } -func ToStrings(slice []interface{}) ([]string, error) { +func CastToStrings(slice []interface{}) ([]string, error) { strs := make([]string, 0) for _, maybe := range slice { str, ok := maybe.(string) diff --git a/core/commands2/ls.go b/core/commands2/ls.go index c94e48e3d..071209437 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -35,7 +35,7 @@ it contains. Run: func(res cmds.Response, req cmds.Request) { node := req.Context().Node - paths, err := internal.ToStrings(req.Arguments()) + paths, err := internal.CastToStrings(req.Arguments()) if err != nil { res.SetError(err, cmds.ErrNormal) return diff --git a/core/commands2/pin.go b/core/commands2/pin.go index 076d252c8..7f8a34618 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -39,7 +39,7 @@ isn't already being stored, IPFS retrieves it. opt, _ := req.Option("recursive") recursive, _ := opt.(bool) // false if cast fails. - paths, err := internal.ToStrings(req.Arguments()) + paths, err := internal.CastToStrings(req.Arguments()) if err != nil { res.SetError(err, cmds.ErrNormal) return @@ -75,7 +75,7 @@ var rmPinCmd = &cmds.Command{ opt, _ := req.Option("recursive") recursive, _ := opt.(bool) // false if cast fails. - paths, err := internal.ToStrings(req.Arguments()) + paths, err := internal.CastToStrings(req.Arguments()) if err != nil { res.SetError(err, cmds.ErrNormal) return From 91da11ae66fcd5fe758728f26b874725b6f54cb9 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sun, 9 Nov 2014 17:59:20 -0800 Subject: [PATCH 198/383] core/commands2: Replaced NewDagFromReader with BuildDagFromReader --- core/commands2/add.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core/commands2/add.go b/core/commands2/add.go index adc5a9993..9b6a3ae73 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -9,7 +9,9 @@ import ( core "github.com/jbenet/go-ipfs/core" internal "github.com/jbenet/go-ipfs/core/commands2/internal" importer "github.com/jbenet/go-ipfs/importer" + "github.com/jbenet/go-ipfs/importer/chunk" dag "github.com/jbenet/go-ipfs/merkledag" + pinning "github.com/jbenet/go-ipfs/pin" ) // Error indicating the max depth has been exceded. @@ -35,11 +37,6 @@ var addCmd = &cmds.Command{ Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node - // if recursive, set depth to reflect so - // opt, found := req.Option("r") - // if r, _ := opt.(bool); found && r { - // } - readers, err := internal.CastToReaders(req.Arguments()) if err != nil { res.SetError(err, cmds.ErrNormal) @@ -88,10 +85,15 @@ var addCmd = &cmds.Command{ } func add(n *core.IpfsNode, readers []io.Reader) ([]*dag.Node, error) { + mp, ok := n.Pinning.(pinning.ManualPinner) + if !ok { + return nil, errors.New("invalid pinner type! expected manual pinner") + } + dagnodes := make([]*dag.Node, 0) for _, reader := range readers { - node, err := importer.NewDagFromReader(reader) + node, err := importer.BuildDagFromReader(reader, n.DAG, mp, chunk.DefaultSplitter) if err != nil { return nil, err } From a1d34365e43468383c5c18187730e4dfeee5d0a5 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sun, 9 Nov 2014 18:37:34 -0800 Subject: [PATCH 199/383] core/commands2: Restored logging that got removed --- core/commands2/add.go | 3 +++ core/commands2/block.go | 1 + core/commands2/log.go | 1 + core/commands2/publish.go | 2 ++ 4 files changed, 7 insertions(+) diff --git a/core/commands2/add.go b/core/commands2/add.go index 9b6a3ae73..294b9626f 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -49,6 +49,7 @@ var addCmd = &cmds.Command{ return } + // TODO: include fs paths in output (will need a way to specify paths in underlying filearg system) added := make([]*Object, 0, len(req.Arguments())) for _, dagnode := range dagnodes { object, err := getOutput(dagnode) @@ -92,6 +93,8 @@ func add(n *core.IpfsNode, readers []io.Reader) ([]*dag.Node, error) { dagnodes := make([]*dag.Node, 0) + // TODO: allow adding directories (will need support for multiple files in filearg system) + for _, reader := range readers { node, err := importer.BuildDagFromReader(reader, n.DAG, mp, chunk.DefaultSplitter) if err != nil { diff --git a/core/commands2/block.go b/core/commands2/block.go index 1c6fb386c..4e9df7e7d 100644 --- a/core/commands2/block.go +++ b/core/commands2/block.go @@ -94,6 +94,7 @@ It outputs the key of the stored block.`, } b := blocks.NewBlock(data) + log.Debugf("BlockPut key: '%q'", b.Key()) k, err := n.Blocks.AddBlock(b) if err != nil { diff --git a/core/commands2/log.go b/core/commands2/log.go index 742aab526..28d4ab0b5 100644 --- a/core/commands2/log.go +++ b/core/commands2/log.go @@ -27,6 +27,7 @@ output of a running daemon. } s := fmt.Sprintf("Changed log level of '%s' to '%s'", args[0], args[1]) + log.Info(s) res.SetOutput(&MessageOutput{s}) }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ diff --git a/core/commands2/publish.go b/core/commands2/publish.go index 59c6f9072..37ba749b9 100644 --- a/core/commands2/publish.go +++ b/core/commands2/publish.go @@ -39,6 +39,8 @@ Publish a to another public key: "IPFS path of the obejct to be published at "}, }, Run: func(res cmds.Response, req cmds.Request) { + log.Debug("Begin Publish") + n := req.Context().Node args := req.Arguments() From 3406ee0ef4ee8549621a658c4a08392519380ee0 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sun, 9 Nov 2014 19:22:53 -0800 Subject: [PATCH 200/383] core/commands2: Added 'refs' command --- core/commands2/refs.go | 132 +++++++++++++++++++++++++++++++++++++++++ core/commands2/root.go | 1 + 2 files changed, 133 insertions(+) create mode 100644 core/commands2/refs.go diff --git a/core/commands2/refs.go b/core/commands2/refs.go new file mode 100644 index 000000000..f66746ff8 --- /dev/null +++ b/core/commands2/refs.go @@ -0,0 +1,132 @@ +package commands + +import ( + "fmt" + + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + cmds "github.com/jbenet/go-ipfs/commands" + "github.com/jbenet/go-ipfs/core" + "github.com/jbenet/go-ipfs/core/commands2/internal" + dag "github.com/jbenet/go-ipfs/merkledag" + u "github.com/jbenet/go-ipfs/util" +) + +type RefsOutput struct { + Refs []string +} + +var refsCmd = &cmds.Command{ + Description: "Lists link hashes from an object", + Help: `Retrieves the object named by and displays the link + hashes it contains, with the following format: + + `, + + Arguments: []cmds.Argument{ + cmds.Argument{"ipfs-path", cmds.ArgString, true, true, + "Path to the object(s) to list refs from"}, + }, + Options: []cmds.Option{ + cmds.Option{[]string{"unique", "u"}, cmds.Bool, + "Omit duplicate refs from output"}, + cmds.Option{[]string{"recursive", "r"}, cmds.Bool, + "Recursively list links of child nodes"}, + }, + Run: func(res cmds.Response, req cmds.Request) { + n := req.Context().Node + + opt, found := req.Option("unique") + unique, ok := opt.(bool) + if !ok && found { + unique = false + } + + opt, found = req.Option("recursive") + recursive, ok := opt.(bool) + if !ok && found { + recursive = false + } + + paths, err := internal.CastToStrings(req.Arguments()) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + output, err := getRefs(n, paths, unique, recursive) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(output) + }, + Type: &RefsOutput{}, + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + output := res.Output().(*RefsOutput) + s := "" + for _, ref := range output.Refs { + s += fmt.Sprintln(ref) + } + return []byte(s), nil + }, + }, +} + +func getRefs(n *core.IpfsNode, paths []string, unique, recursive bool) (*RefsOutput, error) { + var refsSeen map[u.Key]bool + if unique { + refsSeen = make(map[u.Key]bool) + } + + refs := make([]string, 0) + + for _, path := range paths { + object, err := n.Resolver.ResolvePath(path) + if err != nil { + return nil, err + } + + refs, err = addRefs(n, object, refs, refsSeen, recursive) + if err != nil { + return nil, err + } + } + + return &RefsOutput{refs}, nil +} + +func addRefs(n *core.IpfsNode, object *dag.Node, refs []string, refsSeen map[u.Key]bool, recursive bool) ([]string, error) { + for _, link := range object.Links { + var found bool + found, refs = addRef(link.Hash, refs, refsSeen) + + if recursive && !found { + child, err := n.DAG.Get(u.Key(link.Hash)) + if err != nil { + return nil, fmt.Errorf("cannot retrieve %s (%s)", link.Hash.B58String(), err) + } + + refs, err = addRefs(n, child, refs, refsSeen, recursive) + if err != nil { + return nil, err + } + } + } + + return refs, nil +} + +func addRef(h mh.Multihash, refs []string, refsSeen map[u.Key]bool) (bool, []string) { + if refsSeen != nil { + _, found := refsSeen[u.Key(h)] + if found { + return true, refs + } + refsSeen[u.Key(h)] = true + } + + refs = append(refs, h.B58String()) + return false, refs +} diff --git a/core/commands2/root.go b/core/commands2/root.go index e66c71cf6..29fb82a57 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -71,6 +71,7 @@ var rootSubcommands = map[string]*cmds.Command{ "block": blockCmd, "update": updateCmd, "object": objectCmd, + "refs": refsCmd, } func init() { From 2d756473f6c24d16f41ff5ed6393901f4f6ecbaf Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sun, 9 Nov 2014 20:48:40 -0800 Subject: [PATCH 201/383] core/command2: Make help text match old commands --- core/commands2/block.go | 10 ++++++---- core/commands2/bootstrap.go | 2 +- core/commands2/cat.go | 2 +- core/commands2/ls.go | 5 ++++- core/commands2/pin.go | 6 +++--- core/commands2/publish.go | 3 ++- core/commands2/refs.go | 6 ++++-- core/commands2/resolve.go | 3 ++- core/commands2/version.go | 2 +- 9 files changed, 24 insertions(+), 15 deletions(-) diff --git a/core/commands2/block.go b/core/commands2/block.go index 4e9df7e7d..3368eeaf7 100644 --- a/core/commands2/block.go +++ b/core/commands2/block.go @@ -24,7 +24,8 @@ type Block struct { var blockCmd = &cmds.Command{ Description: "Manipulate raw IPFS blocks", Help: `'ipfs block' is a plumbing command used to manipulate raw ipfs blocks. -Reads from stdin or writes to stdout.`, +Reads from stdin or writes to stdout, and is a base58 encoded +multihash.`, Subcommands: map[string]*cmds.Command{ "get": blockGetCmd, "put": blockPutCmd, @@ -33,7 +34,8 @@ Reads from stdin or writes to stdout.`, var blockGetCmd = &cmds.Command{ Description: "Get a raw IPFS block", - Help: `'ipfs block get' is a plumbing command for retreiving raw ipfs blocks.`, + Help: `ipfs block get is a plumbing command for retreiving raw ipfs blocks. +It outputs to stdout, and is a base58 encoded multihash.`, Arguments: []cmds.Argument{ cmds.Argument{"key", cmds.ArgString, true, false, "The base58 multihash of an existing block to get"}, @@ -72,8 +74,8 @@ var blockGetCmd = &cmds.Command{ var blockPutCmd = &cmds.Command{ Description: "Stores input as an IPFS block", - Help: `'ipfs block put' is a plumbing command for storing raw ipfs blocks. -It outputs the key of the stored block.`, + Help: `ipfs block put is a plumbing command for storing raw ipfs blocks. +It reads from stdin, and is a base58 encoded multihash.`, Arguments: []cmds.Argument{ cmds.Argument{"data", cmds.ArgFile, true, false, "The data to be stored as an IPFS block"}, diff --git a/core/commands2/bootstrap.go b/core/commands2/bootstrap.go index 26b15e809..99eac9228 100644 --- a/core/commands2/bootstrap.go +++ b/core/commands2/bootstrap.go @@ -120,7 +120,7 @@ var bootstrapRemoveCmd = &cmds.Command{ } var bootstrapListCmd = &cmds.Command{ - Description: "Lists peers in the bootstrap list", + Description: "Show peers in the bootstrap list", Help: `Peers are output in the format '/'. `, diff --git a/core/commands2/cat.go b/core/commands2/cat.go index b1c8ed336..25327e436 100644 --- a/core/commands2/cat.go +++ b/core/commands2/cat.go @@ -12,7 +12,7 @@ import ( var catCmd = &cmds.Command{ Description: "Show IPFS object data", Help: `Retrieves the object named by and outputs the data - it contains. +it contains. `, Arguments: []cmds.Argument{ diff --git a/core/commands2/ls.go b/core/commands2/ls.go index 071209437..506b898cf 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -25,7 +25,9 @@ type LsOutput struct { var lsCmd = &cmds.Command{ Description: "List links from an object.", Help: `Retrieves the object named by and displays the links -it contains. +it contains, with the following format: + + `, Arguments: []cmds.Argument{ @@ -70,6 +72,7 @@ it contains. }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ cmds.Text: func(res cmds.Response) ([]byte, error) { + // TODO: revert format to match old command s := "" output := res.Output().(*LsOutput).Objects diff --git a/core/commands2/pin.go b/core/commands2/pin.go index 7f8a34618..28d965539 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -20,8 +20,8 @@ var pinCmd = &cmds.Command{ var addPinCmd = &cmds.Command{ Description: "Pins objects to local storage", - Help: `Keeps the object(s) named by in local storage. If the object -isn't already being stored, IPFS retrieves it. + Help: `Retrieves the object named by and stores it locally +on disk. `, Arguments: []cmds.Argument{ @@ -57,7 +57,7 @@ isn't already being stored, IPFS retrieves it. var rmPinCmd = &cmds.Command{ Description: "Unpin an object from local storage", Help: `Removes the pin from the given object allowing it to be garbage - collected if needed. +collected if needed. `, Arguments: []cmds.Argument{ diff --git a/core/commands2/publish.go b/core/commands2/publish.go index 37ba749b9..04a5684a0 100644 --- a/core/commands2/publish.go +++ b/core/commands2/publish.go @@ -16,7 +16,8 @@ var errNotOnline = errors.New("This command must be run in online mode. Try runn var publishCmd = &cmds.Command{ Description: "Publish an object to IPNS", Help: `IPNS is a PKI namespace, where names are the hashes of public keys, and -the private key enables publishing new (signed) values. +the private key enables publishing new (signed) values. In publish, the +default value of is your own identity public key. Examples: diff --git a/core/commands2/refs.go b/core/commands2/refs.go index f66746ff8..f4b34e9f1 100644 --- a/core/commands2/refs.go +++ b/core/commands2/refs.go @@ -18,9 +18,11 @@ type RefsOutput struct { var refsCmd = &cmds.Command{ Description: "Lists link hashes from an object", Help: `Retrieves the object named by and displays the link - hashes it contains, with the following format: +hashes it contains, with the following format: - `, + + +Note: list all refs recursively with -r.`, Arguments: []cmds.Argument{ cmds.Argument{"ipfs-path", cmds.ArgString, true, true, diff --git a/core/commands2/resolve.go b/core/commands2/resolve.go index ecb6e276e..f6bf8f5b8 100644 --- a/core/commands2/resolve.go +++ b/core/commands2/resolve.go @@ -14,7 +14,8 @@ type ResolveOutput struct { var resolveCmd = &cmds.Command{ Description: "Gets the value currently published at an IPNS name", Help: `IPNS is a PKI namespace, where names are the hashes of public keys, and -the private key enables publishing new (signed) values. +the private key enables publishing new (signed) values. In resolve, the +default value of is your own identity public key. Examples: diff --git a/core/commands2/version.go b/core/commands2/version.go index 9d085dd61..22b7ceb9c 100644 --- a/core/commands2/version.go +++ b/core/commands2/version.go @@ -13,7 +13,7 @@ type VersionOutput struct { var versionCmd = &cmds.Command{ Description: "Outputs the current version of IPFS", - Help: `Returns the version number of IPFS. + Help: `Returns the version number of IPFS and exits. `, Options: []cmds.Option{ From 68693783fef1fb1ec524bcdb93c12cf2b201a5f1 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sun, 9 Nov 2014 21:06:15 -0800 Subject: [PATCH 202/383] core/commands2: Made 'resolve' output match old command --- core/commands2/resolve.go | 50 +++++++++++++-------------------------- 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/core/commands2/resolve.go b/core/commands2/resolve.go index f6bf8f5b8..07d4a8f53 100644 --- a/core/commands2/resolve.go +++ b/core/commands2/resolve.go @@ -4,13 +4,8 @@ import ( "errors" cmds "github.com/jbenet/go-ipfs/commands" - core "github.com/jbenet/go-ipfs/core" ) -type ResolveOutput struct { - Entries []IpnsEntry -} - var resolveCmd = &cmds.Command{ Description: "Gets the value currently published at an IPNS name", Help: `IPNS is a PKI namespace, where names are the hashes of public keys, and @@ -33,13 +28,13 @@ Resolve te value of another name: `, Arguments: []cmds.Argument{ - cmds.Argument{"name", cmds.ArgString, false, true, + cmds.Argument{"name", cmds.ArgString, false, false, "The IPNS name to resolve. Defaults to your node's peerID."}, }, Run: func(res cmds.Response, req cmds.Request) { n := req.Context().Node - var names []string + var name string if n.Network == nil { res.SetError(errNotOnline, cmds.ErrNormal) @@ -51,40 +46,29 @@ Resolve te value of another name: res.SetError(errors.New("Identity not loaded!"), cmds.ErrNormal) return } - names = append(names, n.Identity.ID().String()) + name = n.Identity.ID().String() + } else { - for _, arg := range req.Arguments() { - name, ok := arg.(string) - if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return - } - names = append(names, name) + var ok bool + name, ok = req.Arguments()[0].(string) + if !ok { + res.SetError(errors.New("cast error"), cmds.ErrNormal) + return } } - entries, err := resolve(n, names) + output, err := n.Namesys.Resolve(name) if err != nil { res.SetError(err, cmds.ErrNormal) return } - res.SetOutput(&ResolveOutput{entries}) + res.SetOutput(output) + }, + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + output := res.Output().(string) + return []byte(output), nil + }, }, - Type: &ResolveOutput{}, -} - -func resolve(n *core.IpfsNode, names []string) ([]IpnsEntry, error) { - var entries []IpnsEntry - for _, name := range names { - resolved, err := n.Namesys.Resolve(name) - if err != nil { - return nil, err - } - entries = append(entries, IpnsEntry{ - Name: name, - Value: resolved, - }) - } - return entries, nil } From 352c29418f1232275a32a21a5001415a90b852f8 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Sun, 9 Nov 2014 21:07:43 -0800 Subject: [PATCH 203/383] core/commands2: Made 'ls' output match old command --- core/commands2/ls.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/commands2/ls.go b/core/commands2/ls.go index 506b898cf..04cfdea4d 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -72,7 +72,6 @@ it contains, with the following format: }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ cmds.Text: func(res cmds.Response) ([]byte, error) { - // TODO: revert format to match old command s := "" output := res.Output().(*LsOutput).Objects @@ -82,7 +81,7 @@ it contains, with the following format: } for _, link := range object.Links { - s += fmt.Sprintf("-> %s %s (%v bytes)\n", link.Name, link.Hash, link.Size) + s += fmt.Sprintf("%s %v %s\n", link.Hash, link.Size, link.Name) } if len(output) > 1 { From 2029168e98ca24bf2cd7efc1fd443ffa13974e01 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 10 Nov 2014 16:32:50 -0800 Subject: [PATCH 204/383] commands: Made Command run functions return (interface{}, error) instead of setting the values in the response --- commands/command.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/commands/command.go b/commands/command.go index 5d9f584cd..1f531be85 100644 --- a/commands/command.go +++ b/commands/command.go @@ -13,7 +13,7 @@ var log = u.Logger("command") // Function is the type of function that Commands use. // It reads from the Request, and writes results to the Response. -type Function func(Response, Request) +type Function func(Request) (interface{}, error) // Marshaller is a function that takes in a Response, and returns a marshalled []byte // (or an error on failure) @@ -78,8 +78,21 @@ func (c *Command) Call(req Request) Response { return res } - cmd.Run(res, req) + output, err := cmd.Run(req) + if err != nil { + // if returned error is a commands.Error, use its error code + // otherwise, just default the code to ErrNormal + var e Error + e, ok := err.(Error) + if ok { + res.SetError(e, e.Code) + } else { + res.SetError(err, ErrNormal) + } + return res + } + res.SetOutput(output) return res } From 3e30093c2b4207fbd035144360c8f179fafd07a9 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 10 Nov 2014 16:33:07 -0800 Subject: [PATCH 205/383] core/commands2: Updated commands to new Run function API --- cmd/ipfs2/daemon.go | 19 +++++------- cmd/ipfs2/init.go | 17 ++++------- cmd/ipfs2/tour.go | 27 ++++++++--------- core/commands2/add.go | 13 ++++---- core/commands2/block.go | 31 ++++++++----------- core/commands2/bootstrap.go | 30 ++++++++----------- core/commands2/cat.go | 10 +++---- core/commands2/commands.go | 4 +-- core/commands2/config.go | 53 ++++++++------------------------- core/commands2/diag.go | 11 +++---- core/commands2/log.go | 7 ++--- core/commands2/ls.go | 10 +++---- core/commands2/mount_unix.go | 13 ++++---- core/commands2/mount_windows.go | 4 +-- core/commands2/object.go | 49 ++++++++++-------------------- core/commands2/pin.go | 16 +++++----- core/commands2/publish.go | 19 ++++-------- core/commands2/refs.go | 13 ++------ core/commands2/resolve.go | 16 ++++------ core/commands2/update.go | 33 ++++---------------- core/commands2/version.go | 6 ++-- 21 files changed, 137 insertions(+), 264 deletions(-) diff --git a/cmd/ipfs2/daemon.go b/cmd/ipfs2/daemon.go index 50d605e7c..4b7d0b95d 100644 --- a/cmd/ipfs2/daemon.go +++ b/cmd/ipfs2/daemon.go @@ -21,33 +21,29 @@ var daemonCmd = &cmds.Command{ Run: daemonFunc, } -func daemonFunc(res cmds.Response, req cmds.Request) { +func daemonFunc(req cmds.Request) (interface{}, error) { ctx := req.Context() lock, err := daemon.Lock(ctx.ConfigRoot) if err != nil { - res.SetError(fmt.Errorf("Couldn't obtain lock. Is another daemon already running?"), cmds.ErrNormal) - return + return nil, fmt.Errorf("Couldn't obtain lock. Is another daemon already running?") } defer lock.Close() node, err := core.NewIpfsNode(ctx.Config, true) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } ctx.Node = node addr, err := ma.NewMultiaddr(ctx.Config.Addresses.API) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } _, host, err := manet.DialArgs(addr) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } cmdHandler := cmdsHttp.NewHandler(*ctx, commands.Root) @@ -60,7 +56,8 @@ func daemonFunc(res cmds.Response, req cmds.Request) { err = http.ListenAndServe(host, nil) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } + + return nil, nil } diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 7f7cc05cb..d1938db87 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -29,36 +29,29 @@ var initCmd = &cmds.Command{ cmds.Option{[]string{"datastore", "d"}, cmds.String, "Location for the IPFS data store"}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { arg, found := req.Option("d") dspath, ok := arg.(string) if found && !ok { - res.SetError(errors.New("failed to parse datastore flag"), cmds.ErrNormal) - return + return nil, errors.New("failed to parse datastore flag") } arg, found = req.Option("f") force, ok := arg.(bool) // TODO param if found && !ok { - res.SetError(errors.New("failed to parse force flag"), cmds.ErrNormal) - return + return nil, errors.New("failed to parse force flag") } arg, found = req.Option("b") nBitsForKeypair, ok := arg.(int) // TODO param if found && !ok { - res.SetError(errors.New("failed to get bits flag"), cmds.ErrNormal) - return + return nil, errors.New("failed to get bits flag") } else if !found { nBitsForKeypair = 4096 } - err := doInit(req.Context().ConfigRoot, dspath, force, nBitsForKeypair) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } + return nil, doInit(req.Context().ConfigRoot, dspath, force, nBitsForKeypair) }, } diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index 0c0928479..00b845601 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -30,14 +30,13 @@ IPFS very quickly. To start, run: "next": cmdIpfsTourNext, "restart": cmdIpfsTourRestart, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { out := new(bytes.Buffer) cfg := req.Context().Config strs, err := internal.CastToStrings(req.Arguments()) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } topic := tour.TopicID(cfg.Tour.Last) @@ -47,26 +46,24 @@ IPFS very quickly. To start, run: err = tourShow(out, topic) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } - res.SetOutput(out) + return out, nil }, } var cmdIpfsTourNext = &cmds.Command{ Description: "Show the next IPFS Tour topic", - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { var w bytes.Buffer cfg := req.Context().Config path := req.Context().ConfigRoot topic := tour.NextTopic(tour.TopicID(cfg.Tour.Last)) if err := tourShow(&w, topic); err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } // topic changed, not last. write it out. @@ -74,37 +71,39 @@ var cmdIpfsTourNext = &cmds.Command{ cfg.Tour.Last = string(topic) err := writeConfig(path, cfg) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } } w.WriteTo(os.Stdout) // TODO write to res.SetValue + return nil, nil }, } var cmdIpfsTourRestart = &cmds.Command{ Description: "Restart the IPFS Tour", - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { path := req.Context().ConfigRoot cfg := req.Context().Config cfg.Tour.Last = "" err := writeConfig(path, cfg) if err != nil { - res.SetError(err, cmds.ErrNormal) + return nil, err } + return nil, nil }, } var cmdIpfsTourList = &cmds.Command{ Description: "Show a list of IPFS Tour topics", - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { var w bytes.Buffer tourListCmd(&w, req.Context().Config) w.WriteTo(os.Stdout) // TODO use res.SetOutput(output) + return nil, nil }, } diff --git a/core/commands2/add.go b/core/commands2/add.go index 294b9626f..274ec524b 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -34,19 +34,17 @@ var addCmd = &cmds.Command{ MerkleDAG. A smarter partial add with a staging area (like git) remains to be implemented. `, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node readers, err := internal.CastToReaders(req.Arguments()) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } dagnodes, err := add(n, readers) if err != nil { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return + return nil, errors.New("cast error") } // TODO: include fs paths in output (will need a way to specify paths in underlying filearg system) @@ -54,14 +52,13 @@ var addCmd = &cmds.Command{ for _, dagnode := range dagnodes { object, err := getOutput(dagnode) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } added = append(added, object) } - res.SetOutput(&AddOutput{added}) + return &AddOutput{added}, nil }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ cmds.Text: func(res cmds.Response) ([]byte, error) { diff --git a/core/commands2/block.go b/core/commands2/block.go index 3368eeaf7..ef9e554a8 100644 --- a/core/commands2/block.go +++ b/core/commands2/block.go @@ -40,35 +40,31 @@ It outputs to stdout, and is a base58 encoded multihash.`, Arguments: []cmds.Argument{ cmds.Argument{"key", cmds.ArgString, true, false, "The base58 multihash of an existing block to get"}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node key, ok := req.Arguments()[0].(string) if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return + return nil, errors.New("cast error") } if !u.IsValidHash(key) { - res.SetError(errors.New("Not a valid hash"), cmds.ErrClient) - return + return nil, cmds.Error{"Not a valid hash", cmds.ErrClient} } h, err := mh.FromB58String(key) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } k := u.Key(h) ctx, _ := context.WithTimeout(context.TODO(), time.Second*5) b, err := n.Blocks.GetBlock(ctx, k) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } - res.SetOutput(bytes.NewReader(b.Data)) + return bytes.NewReader(b.Data), nil }, } @@ -80,19 +76,17 @@ It reads from stdin, and is a base58 encoded multihash.`, Arguments: []cmds.Argument{ cmds.Argument{"data", cmds.ArgFile, true, false, "The data to be stored as an IPFS block"}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node in, ok := req.Arguments()[0].(io.Reader) if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return + return nil, errors.New("cast error") } data, err := ioutil.ReadAll(in) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } b := blocks.NewBlock(data) @@ -100,14 +94,13 @@ It reads from stdin, and is a base58 encoded multihash.`, k, err := n.Blocks.AddBlock(b) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } - res.SetOutput(&Block{ + return &Block{ Key: k.String(), Length: len(data), - }) + }, nil }, Type: &Block{}, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ diff --git a/core/commands2/bootstrap.go b/core/commands2/bootstrap.go index 99eac9228..9c6de75bb 100644 --- a/core/commands2/bootstrap.go +++ b/core/commands2/bootstrap.go @@ -41,26 +41,23 @@ in the bootstrap list). Arguments: []cmds.Argument{ cmds.Argument{"peer", cmds.ArgString, true, true, peerOptionDesc}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { input, err := bootstrapInputToPeers(req.Arguments()) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } filename, err := config.Filename(req.Context().ConfigRoot) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } added, err := bootstrapAdd(filename, req.Context().Config, input) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } - res.SetOutput(&BootstrapOutput{added}) + return &BootstrapOutput{added}, nil }, Type: &BootstrapOutput{}, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ @@ -84,26 +81,23 @@ var bootstrapRemoveCmd = &cmds.Command{ Arguments: []cmds.Argument{ cmds.Argument{"peer", cmds.ArgString, true, true, peerOptionDesc}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { input, err := bootstrapInputToPeers(req.Arguments()) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } filename, err := config.Filename(req.Context().ConfigRoot) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } removed, err := bootstrapRemove(filename, req.Context().Config, input) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } - res.SetOutput(&BootstrapOutput{removed}) + return &BootstrapOutput{removed}, nil }, Type: &BootstrapOutput{}, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ @@ -124,9 +118,9 @@ var bootstrapListCmd = &cmds.Command{ Help: `Peers are output in the format '/'. `, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { peers := req.Context().Config.Bootstrap - res.SetOutput(&BootstrapOutput{peers}) + return &BootstrapOutput{peers}, nil }, Type: &BootstrapOutput{}, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ diff --git a/core/commands2/cat.go b/core/commands2/cat.go index 25327e436..e8c39a533 100644 --- a/core/commands2/cat.go +++ b/core/commands2/cat.go @@ -19,24 +19,22 @@ it contains. cmds.Argument{"ipfs-path", cmds.ArgString, true, true, "The path to the IPFS object(s) to be outputted"}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { node := req.Context().Node readers := make([]io.Reader, 0, len(req.Arguments())) paths, err := internal.CastToStrings(req.Arguments()) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } readers, err = cat(node, paths) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } reader := io.MultiReader(readers...) - res.SetOutput(reader) + return reader, nil }, } diff --git a/core/commands2/commands.go b/core/commands2/commands.go index 6a5ab653d..6df3cd075 100644 --- a/core/commands2/commands.go +++ b/core/commands2/commands.go @@ -17,9 +17,9 @@ var commandsCmd = &cmds.Command{ Help: `Lists all available commands (and subcommands) and exits. `, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { root := outputCommand("ipfs", Root) - res.SetOutput(&root) + return &root, nil }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ cmds.Text: func(res cmds.Response) ([]byte, error) { diff --git a/core/commands2/config.go b/core/commands2/config.go index e0583b4ca..dd15653c7 100644 --- a/core/commands2/config.go +++ b/core/commands2/config.go @@ -36,19 +36,17 @@ var configCmd = &cmds.Command{ cmds.Argument{"value", cmds.ArgString, false, false, "The value to set the config entry to"}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { args := req.Arguments() key, ok := args[0].(string) if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return + return nil, errors.New("cast error") } filename, err := config.Filename(req.Context().ConfigRoot) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } var value string @@ -56,28 +54,13 @@ var configCmd = &cmds.Command{ var ok bool value, ok = args[1].(string) if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return + return nil, errors.New("cast error") } - field, err := setConfig(filename, key, value) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - res.SetOutput(field) - return + return setConfig(filename, key, value) } else { - field, err := getConfig(filename, key) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - res.SetOutput(field) - return + return getConfig(filename, key) } }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ @@ -111,20 +94,13 @@ var configShowCmd = &cmds.Command{ included in the output of this command. `, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { filename, err := config.Filename(req.Context().ConfigRoot) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } - reader, err := showConfig(filename) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - res.SetOutput(reader) + return showConfig(filename) }, } @@ -134,18 +110,13 @@ var configEditCmd = &cmds.Command{ variable set to your preferred text editor. `, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { filename, err := config.Filename(req.Context().ConfigRoot) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } - err = editConfig(filename) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } + return nil, editConfig(filename) }, } diff --git a/core/commands2/diag.go b/core/commands2/diag.go index ef1abf150..82da78891 100644 --- a/core/commands2/diag.go +++ b/core/commands2/diag.go @@ -1,7 +1,6 @@ package commands import ( - "errors" "fmt" "io" "time" @@ -42,18 +41,16 @@ requesting a listing of data about them including number of connected peers and latencies between them. `, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node if !n.OnlineMode() { - res.SetError(errors.New("Cannot run diagnostic in offline mode!"), cmds.ErrNormal) - return + return nil, errNotOnline } info, err := n.Diagnostics.GetDiagnostic(time.Second * 20) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } output := make([]DiagnosticPeer, len(info)) @@ -75,7 +72,7 @@ connected peers and latencies between them. } } - res.SetOutput(&DiagnosticOutput{output}) + return &DiagnosticOutput{output}, nil }, Type: &DiagnosticOutput{}, } diff --git a/core/commands2/log.go b/core/commands2/log.go index 28d4ab0b5..12a8a28f9 100644 --- a/core/commands2/log.go +++ b/core/commands2/log.go @@ -19,16 +19,15 @@ output of a running daemon. cmds.Argument{"level", cmds.ArgString, true, false, "one of: debug, info, notice, warning, error, critical"}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { args := req.Arguments() if err := u.SetLogLevel(args[0].(string), args[1].(string)); err != nil { - res.SetError(err, cmds.ErrClient) - return + return nil, err } s := fmt.Sprintf("Changed log level of '%s' to '%s'", args[0], args[1]) log.Info(s) - res.SetOutput(&MessageOutput{s}) + return &MessageOutput{s}, nil }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ cmds.Text: MessageTextMarshaller, diff --git a/core/commands2/ls.go b/core/commands2/ls.go index 04cfdea4d..1849740b3 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -34,21 +34,19 @@ it contains, with the following format: cmds.Argument{"ipfs-path", cmds.ArgString, false, true, "The path to the IPFS object(s) to list links from"}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { node := req.Context().Node paths, err := internal.CastToStrings(req.Arguments()) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } dagnodes := make([]*merkledag.Node, 0) for _, path := range paths { dagnode, err := node.Resolver.ResolvePath(path) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } dagnodes = append(dagnodes, dagnode) } @@ -68,7 +66,7 @@ it contains, with the following format: } } - res.SetOutput(&LsOutput{output}) + return &LsOutput{output}, nil }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ cmds.Text: func(res cmds.Response) ([]byte, error) { diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go index e82240380..14fdcb63d 100644 --- a/core/commands2/mount_unix.go +++ b/core/commands2/mount_unix.go @@ -35,18 +35,16 @@ not be listable, as it is virtual. Accessing known paths directly. cmds.Option{[]string{"n"}, cmds.String, "The path where IPNS should be mounted\n(default is '/ipns')"}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { ctx := req.Context() // error if we aren't running node in online mode if ctx.Node.Network == nil { - res.SetError(errNotOnline, cmds.ErrNormal) - return + return nil, errNotOnline } if err := platformFuseChecks(); err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } // update fsdir with flag. @@ -74,11 +72,10 @@ not be listable, as it is virtual. Accessing known paths directly. // mounted successfully, we timed out with no errors case <-time.After(mountTimeout): output := ctx.Config.Mounts - res.SetOutput(&output) - return + return &output, nil } - res.SetError(err, cmds.ErrNormal) + return nil, err }, Type: &config.Mounts{}, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ diff --git a/core/commands2/mount_windows.go b/core/commands2/mount_windows.go index 278192c6d..29e105c80 100644 --- a/core/commands2/mount_windows.go +++ b/core/commands2/mount_windows.go @@ -10,7 +10,7 @@ var ipfsMount = &cmds.Command{ Description: "Not yet implemented on Windows", Help: `Not yet implemented on Windows. :(`, - Run: func(res cmds.Response, req cmds.Request) { - res.SetError(errors.New("Mount isn't compatible with Windows yet"), cmds.ErrNormal) + Run: func(req cmds.Request) (interface{}, error) { + return errors.New("Mount isn't compatible with Windows yet"), nil }, } diff --git a/core/commands2/object.go b/core/commands2/object.go index bc08dcebe..48908334f 100644 --- a/core/commands2/object.go +++ b/core/commands2/object.go @@ -47,22 +47,15 @@ output is the raw data of the object. cmds.Argument{"key", cmds.ArgString, true, false, "Key of the object to retrieve, in base58-encoded multihash format"}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node key, ok := req.Arguments()[0].(string) if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return + return nil, errors.New("cast error") } - reader, err := objectData(n, key) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - res.SetOutput(reader) + return objectData(n, key) }, } @@ -75,22 +68,15 @@ It outputs to stdout, and is a base58 encoded multihash.`, cmds.Argument{"key", cmds.ArgString, true, false, "Key of the object to retrieve, in base58-encoded multihash format"}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node key, ok := req.Arguments()[0].(string) if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return + return nil, errors.New("cast error") } - output, err := objectLinks(n, key) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - res.SetOutput(output) + return objectLinks(n, key) }, Type: &Object{}, } @@ -111,19 +97,17 @@ This command outputs data in the following encodings: cmds.Argument{"key", cmds.ArgString, true, false, "Key of the object to retrieve\n(in base58-encoded multihash format)"}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node key, ok := req.Arguments()[0].(string) if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return + return nil, errors.New("cast error") } object, err := objectGet(n, key) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } node := &Node{ @@ -139,7 +123,7 @@ This command outputs data in the following encodings: } } - res.SetOutput(node) + return node, nil }, Type: &Node{}, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ @@ -167,19 +151,17 @@ Data should be in the format specified by . cmds.Argument{"encoding", cmds.ArgString, true, false, "Encoding type of , either \"protobuf\" or \"json\""}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node input, ok := req.Arguments()[0].(io.Reader) if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return + return nil, errors.New("cast error") } encoding, ok := req.Arguments()[1].(string) if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return + return nil, errors.New("cast error") } output, err := objectPut(n, input, encoding) @@ -188,11 +170,10 @@ Data should be in the format specified by . if err == ErrUnknownObjectEnc { errType = cmds.ErrClient } - res.SetError(err, errType) - return + return nil, cmds.Error{err.Error(), errType} } - res.SetOutput(output) + return output, nil }, Type: &Object{}, } diff --git a/core/commands2/pin.go b/core/commands2/pin.go index 28d965539..fac0aa859 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -32,7 +32,7 @@ on disk. cmds.Option{[]string{"recursive", "r"}, cmds.Bool, "Recursively pin the object linked to by the specified object(s)"}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node // set recursive flag @@ -41,16 +41,16 @@ on disk. paths, err := internal.CastToStrings(req.Arguments()) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } _, err = pin(n, paths, recursive) if err != nil { - res.SetError(err, cmds.ErrNormal) + return nil, err } // TODO: create some output to show what got pinned + return nil, nil }, } @@ -68,7 +68,7 @@ collected if needed. cmds.Option{[]string{"recursive", "r"}, cmds.Bool, "Recursively unpin the object linked to by the specified object(s)"}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node // set recursive flag @@ -77,16 +77,16 @@ collected if needed. paths, err := internal.CastToStrings(req.Arguments()) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } _, err = unpin(n, paths, recursive) if err != nil { - res.SetError(err, cmds.ErrNormal) + return nil, err } // TODO: create some output to show what got unpinned + return nil, nil }, } diff --git a/core/commands2/publish.go b/core/commands2/publish.go index 04a5684a0..e5c8c89bf 100644 --- a/core/commands2/publish.go +++ b/core/commands2/publish.go @@ -39,20 +39,18 @@ Publish a to another public key: cmds.Argument{"ipfs-path", cmds.ArgString, true, false, "IPFS path of the obejct to be published at "}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { log.Debug("Begin Publish") n := req.Context().Node args := req.Arguments() if n.Network == nil { - res.SetError(errNotOnline, cmds.ErrNormal) - return + return nil, errNotOnline } if n.Identity == nil { - res.SetError(errors.New("Identity not loaded!"), cmds.ErrNormal) - return + return nil, errors.New("Identity not loaded!") } // name := "" @@ -62,8 +60,7 @@ Publish a to another public key: case 2: // name = args[0] ref = args[1].(string) - res.SetError(errors.New("keychains not yet implemented"), cmds.ErrNormal) - return + return nil, errors.New("keychains not yet implemented") case 1: // name = n.Identity.ID.String() ref = args[0].(string) @@ -71,13 +68,7 @@ Publish a to another public key: // TODO n.Keychain.Get(name).PrivKey k := n.Identity.PrivKey() - publishOutput, err := publish(n, k, ref) - - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - res.SetOutput(publishOutput) + return publish(n, k, ref) }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ cmds.Text: func(res cmds.Response) ([]byte, error) { diff --git a/core/commands2/refs.go b/core/commands2/refs.go index f4b34e9f1..41354d688 100644 --- a/core/commands2/refs.go +++ b/core/commands2/refs.go @@ -34,7 +34,7 @@ Note: list all refs recursively with -r.`, cmds.Option{[]string{"recursive", "r"}, cmds.Bool, "Recursively list links of child nodes"}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node opt, found := req.Option("unique") @@ -51,17 +51,10 @@ Note: list all refs recursively with -r.`, paths, err := internal.CastToStrings(req.Arguments()) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } - output, err := getRefs(n, paths, unique, recursive) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - res.SetOutput(output) + return getRefs(n, paths, unique, recursive) }, Type: &RefsOutput{}, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ diff --git a/core/commands2/resolve.go b/core/commands2/resolve.go index 07d4a8f53..f41c0c1d3 100644 --- a/core/commands2/resolve.go +++ b/core/commands2/resolve.go @@ -31,20 +31,18 @@ Resolve te value of another name: cmds.Argument{"name", cmds.ArgString, false, false, "The IPNS name to resolve. Defaults to your node's peerID."}, }, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node var name string if n.Network == nil { - res.SetError(errNotOnline, cmds.ErrNormal) - return + return nil, errNotOnline } if len(req.Arguments()) == 0 { if n.Identity == nil { - res.SetError(errors.New("Identity not loaded!"), cmds.ErrNormal) - return + return nil, errors.New("Identity not loaded!") } name = n.Identity.ID().String() @@ -52,18 +50,16 @@ Resolve te value of another name: var ok bool name, ok = req.Arguments()[0].(string) if !ok { - res.SetError(errors.New("cast error"), cmds.ErrNormal) - return + return nil, errors.New("cast error") } } output, err := n.Namesys.Resolve(name) if err != nil { - res.SetError(err, cmds.ErrNormal) - return + return nil, err } - res.SetOutput(output) + return output, nil }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ cmds.Text: func(res cmds.Response) ([]byte, error) { diff --git a/core/commands2/update.go b/core/commands2/update.go index 8702e0154..881cddd89 100644 --- a/core/commands2/update.go +++ b/core/commands2/update.go @@ -19,16 +19,9 @@ var updateCmd = &cmds.Command{ Help: `ipfs update is a utility command used to check for updates and apply them. `, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node - - output, err := updateApply(n) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - res.SetOutput(output) + return updateApply(n) }, Type: &UpdateOutput{}, Subcommands: map[string]*cmds.Command{ @@ -57,16 +50,9 @@ var updateCheckCmd = &cmds.Command{ Nothing will be downloaded or installed. `, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node - - output, err := updateCheck(n) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - res.SetOutput(output) + return updateCheck(n) }, Type: &UpdateOutput{}, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ @@ -89,16 +75,9 @@ var updateLogCmd = &cmds.Command{ Help: `This command is not yet implemented. `, - Run: func(res cmds.Response, req cmds.Request) { + Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node - - output, err := updateLog(n) - if err != nil { - res.SetError(err, cmds.ErrNormal) - return - } - - res.SetOutput(output) + return updateLog(n) }, } diff --git a/core/commands2/version.go b/core/commands2/version.go index 22b7ceb9c..3777ae075 100644 --- a/core/commands2/version.go +++ b/core/commands2/version.go @@ -20,10 +20,10 @@ var versionCmd = &cmds.Command{ cmds.Option{[]string{"number", "n"}, cmds.Bool, "Only output the version number"}, }, - Run: func(res cmds.Response, req cmds.Request) { - res.SetOutput(&VersionOutput{ + Run: func(req cmds.Request) (interface{}, error) { + return &VersionOutput{ Version: config.CurrentVersionNumber, - }) + }, nil }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ cmds.Text: func(res cmds.Response) ([]byte, error) { From 38b31c65f8d4b85df739905d063f431cfa8b43dc Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 10 Nov 2014 16:59:07 -0800 Subject: [PATCH 206/383] commands: Added Option helper constructors --- commands/option.go | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/commands/option.go b/commands/option.go index f068ec228..8f11ace49 100644 --- a/commands/option.go +++ b/commands/option.go @@ -18,11 +18,43 @@ type Option struct { Type reflect.Kind // value must be this type Description string // a short string to describe this option - // TODO: add more features(?): + // MAYBE_TODO: add more features(?): //Default interface{} // the default value (ignored if `Required` is true) //Required bool // whether or not the option must be provided } +// constructor helper functions +func NewOption(kind reflect.Kind, names ...string) Option { + if len(names) < 2 { + panic("Options require at least two string values (name and description)") + } + + desc := names[len(names)-1] + names = names[:len(names)-2] + + return Option{ + Names: names, + Type: kind, + Description: desc, + } +} + +func BoolOption(names ...string) Option { + return NewOption(Bool, names...) +} +func IntOption(names ...string) Option { + return NewOption(Int, names...) +} +func UintOption(names ...string) Option { + return NewOption(Uint, names...) +} +func FloatOption(names ...string) Option { + return NewOption(Float, names...) +} +func StringOption(names ...string) Option { + return NewOption(String, names...) +} + // Flag names const ( EncShort = "enc" From 8ee3e996ccfcdc4741fa737738ea692e18e1c4c0 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 10 Nov 2014 17:10:55 -0800 Subject: [PATCH 207/383] core/commands2: Use Option constructors in commands --- cmd/ipfs2/init.go | 12 ++++-------- core/commands2/add.go | 2 +- core/commands2/mount_unix.go | 9 ++------- core/commands2/pin.go | 6 ++---- core/commands2/refs.go | 6 ++---- core/commands2/root.go | 12 ++++-------- core/commands2/version.go | 3 +-- 7 files changed, 16 insertions(+), 34 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index d1938db87..009412613 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -20,14 +20,10 @@ var initCmd = &cmds.Command{ `, Options: []cmds.Option{ - cmds.Option{[]string{"bits", "b"}, cmds.Int, - "Number of bits to use in the generated RSA private key (defaults to 4096)"}, - cmds.Option{[]string{"passphrase", "p"}, cmds.String, - "Passphrase for encrypting the private key"}, - cmds.Option{[]string{"force", "f"}, cmds.Bool, - "Overwrite existing config (if it exists)"}, - cmds.Option{[]string{"datastore", "d"}, cmds.String, - "Location for the IPFS data store"}, + cmds.IntOption("bits", "b", "Number of bits to use in the generated RSA private key (defaults to 4096)"), + cmds.StringOption("passphrase", "p", "Passphrase for encrypting the private key"), + cmds.BoolOption("force", "f", "Overwrite existing config (if it exists)"), + cmds.StringOption("datastore", "d", "Location for the IPFS data store"), }, Run: func(req cmds.Request) (interface{}, error) { diff --git a/core/commands2/add.go b/core/commands2/add.go index 274ec524b..043ac32cb 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -23,7 +23,7 @@ type AddOutput struct { var addCmd = &cmds.Command{ Options: []cmds.Option{ - cmds.Option{[]string{"recursive", "r"}, cmds.Bool, "Must be specified when adding directories"}, + cmds.BoolOption("recursive", "r", "Must be specified when adding directories"), }, Arguments: []cmds.Argument{ cmds.Argument{"file", cmds.ArgFile, true, true, "The path to a file to be added to IPFS"}, diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go index 14fdcb63d..7bd7128f9 100644 --- a/core/commands2/mount_unix.go +++ b/core/commands2/mount_unix.go @@ -24,16 +24,11 @@ not be listable, as it is virtual. Accessing known paths directly. `, Options: []cmds.Option{ - - // TODO text: specify a mountpoint for ipfs // TODO longform - cmds.Option{[]string{"f"}, cmds.String, - "The path where IPFS should be mounted\n(default is '/ipfs')"}, + cmds.StringOption("f", "The path where IPFS should be mounted\n(default is '/ipfs')"), - // TODO text: specify a mountpoint for ipns // TODO longform - cmds.Option{[]string{"n"}, cmds.String, - "The path where IPNS should be mounted\n(default is '/ipns')"}, + cmds.StringOption("n", "The path where IPNS should be mounted\n(default is '/ipns')"), }, Run: func(req cmds.Request) (interface{}, error) { ctx := req.Context() diff --git a/core/commands2/pin.go b/core/commands2/pin.go index fac0aa859..77d382c9a 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -29,8 +29,7 @@ on disk. "Path to object(s) to be pinned"}, }, Options: []cmds.Option{ - cmds.Option{[]string{"recursive", "r"}, cmds.Bool, - "Recursively pin the object linked to by the specified object(s)"}, + cmds.BoolOption("recursive", "r", "Recursively pin the object linked to by the specified object(s)"), }, Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node @@ -65,8 +64,7 @@ collected if needed. "Path to object(s) to be unpinned"}, }, Options: []cmds.Option{ - cmds.Option{[]string{"recursive", "r"}, cmds.Bool, - "Recursively unpin the object linked to by the specified object(s)"}, + cmds.BoolOption("recursive", "r", "Recursively unpin the object linked to by the specified object(s)"), }, Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node diff --git a/core/commands2/refs.go b/core/commands2/refs.go index 41354d688..f4eb4d2e2 100644 --- a/core/commands2/refs.go +++ b/core/commands2/refs.go @@ -29,10 +29,8 @@ Note: list all refs recursively with -r.`, "Path to the object(s) to list refs from"}, }, Options: []cmds.Option{ - cmds.Option{[]string{"unique", "u"}, cmds.Bool, - "Omit duplicate refs from output"}, - cmds.Option{[]string{"recursive", "r"}, cmds.Bool, - "Recursively list links of child nodes"}, + cmds.BoolOption("unique", "u", "Omit duplicate refs from output"), + cmds.BoolOption("recursive", "r", "Recursively list links of child nodes"), }, Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node diff --git a/core/commands2/root.go b/core/commands2/root.go index 29fb82a57..187592ceb 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -44,14 +44,10 @@ Use "ipfs --help" for more information about a command. `, Options: []cmds.Option{ - cmds.Option{[]string{"config", "c"}, cmds.String, - "Path to the configuration file to use"}, - cmds.Option{[]string{"debug", "D"}, cmds.Bool, - "Operate in debug mode"}, - cmds.Option{[]string{"help", "h"}, cmds.Bool, - "Show the command help text"}, - cmds.Option{[]string{"local", "L"}, cmds.Bool, - "Run the command locally, instead of using the daemon"}, + cmds.StringOption("config", "c", "Path to the configuration file to use"), + cmds.BoolOption("debug", "D", "Operate in debug mode"), + cmds.BoolOption("help", "h", "Show the command help text"), + cmds.BoolOption("local", "L", "Run the command locally, instead of using the daemon"), }, } diff --git a/core/commands2/version.go b/core/commands2/version.go index 3777ae075..ca80f538f 100644 --- a/core/commands2/version.go +++ b/core/commands2/version.go @@ -17,8 +17,7 @@ var versionCmd = &cmds.Command{ `, Options: []cmds.Option{ - cmds.Option{[]string{"number", "n"}, cmds.Bool, - "Only output the version number"}, + cmds.BoolOption("number", "n", "Only output the version number"), }, Run: func(req cmds.Request) (interface{}, error) { return &VersionOutput{ From ce28fa363b7580ae28a7267dbd862f7b885a74a0 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 10 Nov 2014 17:14:35 -0800 Subject: [PATCH 208/383] commands: Fixed tests --- commands/command_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/commands/command_test.go b/commands/command_test.go index c113a324b..f05c663ee 100644 --- a/commands/command_test.go +++ b/commands/command_test.go @@ -2,13 +2,17 @@ package commands import "testing" +func noop(req Request) (interface{}, error) { + return nil, nil +} + func TestOptionValidation(t *testing.T) { cmd := Command{ Options: []Option{ Option{[]string{"b", "beep"}, Int, "enables beeper"}, Option{[]string{"B", "boop"}, String, "password for booper"}, }, - Run: func(res Response, req Request) {}, + Run: noop, } req := NewEmptyRequest() @@ -79,8 +83,6 @@ func TestOptionValidation(t *testing.T) { } func TestRegistration(t *testing.T) { - noop := func(res Response, req Request) {} - cmdA := &Command{ Options: []Option{ Option{[]string{"beep"}, Int, "number of beeps"}, From eebb43753b0eb5ad2585548fbb27a03870cad482 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 10 Nov 2014 19:23:21 -0800 Subject: [PATCH 209/383] commands: Added list of option definitions to Request, so Option can lookup values by all aliases --- commands/cli/parse.go | 7 ++++++- commands/http/parse.go | 7 ++++++- commands/request.go | 42 ++++++++++++++++++++++++++++++++---------- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index e9e91fbfe..982691a80 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -45,7 +45,12 @@ func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, return nil, root, cmd, path, err } - req := cmds.NewRequest(path, opts, args, cmd) + optDefs, err := root.GetOptions(path) + if err != nil { + return nil, root, cmd, path, err + } + + req := cmds.NewRequest(path, opts, args, cmd, optDefs) err = cmd.CheckArguments(req) if err != nil { diff --git a/commands/http/parse.go b/commands/http/parse.go index 979bacb78..7c1a0e1ff 100644 --- a/commands/http/parse.go +++ b/commands/http/parse.go @@ -90,7 +90,12 @@ func Parse(r *http.Request, root *cmds.Command) (cmds.Request, error) { args = append(args, make([]interface{}, valCount-1)) } - req := cmds.NewRequest(path, opts, args, cmd) + optDefs, err := root.GetOptions(path) + if err != nil { + return nil, err + } + + req := cmds.NewRequest(path, opts, args, cmd, optDefs) err = cmd.CheckArguments(req) if err != nil { diff --git a/commands/request.go b/commands/request.go index 40eda9fc6..9e32302c1 100644 --- a/commands/request.go +++ b/commands/request.go @@ -33,11 +33,12 @@ type Request interface { } type request struct { - path []string - options optMap - arguments []interface{} - cmd *Command - ctx Context + path []string + options optMap + arguments []interface{} + cmd *Command + ctx Context + optionDefs map[string]Option } // Path returns the command path of this request @@ -47,8 +48,26 @@ func (r *request) Path() []string { // Option returns the value of the option for given name. func (r *request) Option(name string) (interface{}, bool) { - val, err := r.options[name] - return val, err + val, found := r.options[name] + if found { + return val, found + } + + // if a value isn't defined for that name, we will try to look it up by its aliases + + // find the option with the specified name + option, found := r.optionDefs[name] + if found { + // try all the possible names, break if we find a value + for _, n := range option.Names { + val, found := r.options[n] + if found { + return val, found + } + } + } + + return nil, false } // Options returns a copy of the option map @@ -152,11 +171,11 @@ func (r *request) ConvertOptions(options map[string]Option) error { // NewEmptyRequest initializes an empty request func NewEmptyRequest() Request { - return NewRequest(nil, nil, nil, nil) + return NewRequest(nil, nil, nil, nil, nil) } // NewRequest returns a request initialized with given arguments -func NewRequest(path []string, opts optMap, args []interface{}, cmd *Command) Request { +func NewRequest(path []string, opts optMap, args []interface{}, cmd *Command, optDefs map[string]Option) Request { if path == nil { path = make([]string, 0) } @@ -166,5 +185,8 @@ func NewRequest(path []string, opts optMap, args []interface{}, cmd *Command) Re if args == nil { args = make([]interface{}, 0) } - return &request{path, opts, args, cmd, Context{}} + if optDefs == nil { + optDefs = make(map[string]Option) + } + return &request{path, opts, args, cmd, Context{}, optDefs} } From d700a2ce879b5f0e65ba5fb2e4845d8e9d7b0d4a Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 10 Nov 2014 19:23:59 -0800 Subject: [PATCH 210/383] cmd/ipfs2: Cleaned up main option checking (no longer need temporary Request) --- cmd/ipfs2/main.go | 56 +++++++---------------------------------------- 1 file changed, 8 insertions(+), 48 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index fafba34e7..40d25c09a 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -59,28 +59,16 @@ func main() { func createRequest(args []string) (cmds.Request, *cmds.Command) { req, root, cmd, path, err := cmdsCli.Parse(args, Root, commands.Root) - var options cmds.Request - if req != nil && root != nil { - var err2 error - options, err2 = getOptions(req, root) - if err2 != nil { - fmt.Println(err2) - exit(1) - } - } - // handle parse error (which means the commandline input was wrong, // e.g. incorrect number of args, or nonexistent subcommand) if err != nil { // if the -help flag wasn't specified, show the error message // or if a path was returned (user specified a valid subcommand), show the error message // (this means there was an option or argument error) - if options != nil || path != nil && len(path) > 0 { + if path != nil && len(path) > 0 { help := false - if options != nil { - opt, _ := options.Option("help") - help, _ = opt.(bool) - } + opt, _ := req.Option("help") + help, _ = opt.(bool) if !help { fmt.Printf(errorFormat, err) @@ -104,7 +92,7 @@ func createRequest(args []string) (cmds.Request, *cmds.Command) { exit(1) } - configPath, err := getConfigRoot(options) + configPath, err := getConfigRoot(req) if err != nil { fmt.Println(err) exit(1) @@ -120,7 +108,7 @@ func createRequest(args []string) (cmds.Request, *cmds.Command) { ctx.ConfigRoot = configPath ctx.Config = conf - if _, found := options.Option("encoding"); !found { + if _, found := req.Option("encoding"); !found { if req.Command().Marshallers != nil && req.Command().Marshallers[cmds.Text] != nil { req.SetOption("encoding", cmds.Text) } else { @@ -132,13 +120,7 @@ func createRequest(args []string) (cmds.Request, *cmds.Command) { } func handleOptions(req cmds.Request, root *cmds.Command) { - options, err := getOptions(req, root) - if err != nil { - fmt.Println(err) - exit(1) - } - - if help, found := options.Option("help"); found { + if help, found := req.Option("help"); found { if helpBool, ok := help.(bool); helpBool && ok { helpText, err := cmdsCli.HelpText("ipfs", root, req.Path()) if err != nil { @@ -153,7 +135,7 @@ func handleOptions(req cmds.Request, root *cmds.Command) { } } - if debug, found := options.Option("debug"); found { + if debug, found := req.Option("debug"); found { if debugBool, ok := debug.(bool); debugBool && ok { u.Debug = true @@ -172,16 +154,10 @@ func callCommand(req cmds.Request, root *cmds.Command) cmds.Response { res = root.Call(req) } else { - options, err := getOptions(req, root) - if err != nil { - fmt.Println(err) - exit(1) - } - var found bool var local interface{} localBool := false - if local, found = options.Option("local"); found { + if local, found = req.Option("local"); found { var ok bool localBool, ok = local.(bool) if !ok { @@ -252,22 +228,6 @@ func outputResponse(res cmds.Response, root *cmds.Command) { io.Copy(os.Stdout, out) } -func getOptions(req cmds.Request, root *cmds.Command) (cmds.Request, error) { - tempReq := cmds.NewRequest(req.Path(), req.Options(), nil, nil) - - options, err := root.GetOptions(tempReq.Path()) - if err != nil { - return nil, err - } - - err = tempReq.ConvertOptions(options) - if err != nil { - return nil, err - } - - return tempReq, nil -} - func getConfigRoot(req cmds.Request) (string, error) { if opt, found := req.Option("config"); found { if optStr, ok := opt.(string); ok { From e34a0fd65831a77672b261193a1af21342c4b8ae Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 10 Nov 2014 19:24:13 -0800 Subject: [PATCH 211/383] commands: Updated tests --- commands/command_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/command_test.go b/commands/command_test.go index f05c663ee..2186678b7 100644 --- a/commands/command_test.go +++ b/commands/command_test.go @@ -107,7 +107,7 @@ func TestRegistration(t *testing.T) { Run: noop, } - res := cmdB.Call(NewRequest([]string{"a"}, nil, nil, nil)) + res := cmdB.Call(NewRequest([]string{"a"}, nil, nil, nil, nil)) if res.Error() == nil { t.Error("Should have failed (option name collision)") } From 20591c7e64917dd8970fecd301501623c14ed3f7 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 10 Nov 2014 21:11:49 -0800 Subject: [PATCH 212/383] commands: Made SetOption override existing values (even if they used a different alias) --- commands/request.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/commands/request.go b/commands/request.go index 9e32302c1..c08c0b22a 100644 --- a/commands/request.go +++ b/commands/request.go @@ -81,6 +81,21 @@ func (r *request) Options() map[string]interface{} { // SetOption sets the value of the option for given name. func (r *request) SetOption(name string, val interface{}) { + // find the option with the specified name + option, found := r.optionDefs[name] + if !found { + return + } + + // try all the possible names, if we already have a value then set over it + for _, n := range option.Names { + val, found := r.options[n] + if found { + r.options[n] = val + return + } + } + r.options[name] = val } From 3e507f7c9f5488e69aafb9be7f5da00702bfeafb Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 10 Nov 2014 21:30:42 -0800 Subject: [PATCH 213/383] commands: Changed option accessor API (Request#Option now returns an OptionValue) --- cmd/ipfs2/init.go | 26 ++++++------- cmd/ipfs2/main.go | 72 ++++++++++++++--------------------- commands/command.go | 8 +--- commands/command_test.go | 39 +++++++++---------- commands/http/client.go | 20 +--------- commands/http/handler.go | 7 ++-- commands/option.go | 74 +++++++++++++++++++++++++++++++++++- commands/request.go | 41 +++++++++++--------- commands/response.go | 12 ++++-- commands/response_test.go | 15 +++----- core/commands2/mount_unix.go | 10 ++--- core/commands2/pin.go | 6 +-- core/commands2/refs.go | 13 +------ core/commands2/version.go | 8 +--- 14 files changed, 182 insertions(+), 169 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index 009412613..d06525aec 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -20,30 +20,28 @@ var initCmd = &cmds.Command{ `, Options: []cmds.Option{ - cmds.IntOption("bits", "b", "Number of bits to use in the generated RSA private key (defaults to 4096)"), + cmds.UintOption("bits", "b", "Number of bits to use in the generated RSA private key (defaults to 4096)"), cmds.StringOption("passphrase", "p", "Passphrase for encrypting the private key"), cmds.BoolOption("force", "f", "Overwrite existing config (if it exists)"), cmds.StringOption("datastore", "d", "Location for the IPFS data store"), }, Run: func(req cmds.Request) (interface{}, error) { - arg, found := req.Option("d") - dspath, ok := arg.(string) - if found && !ok { - return nil, errors.New("failed to parse datastore flag") + dspath, err := req.Option("d").String() + if err != nil { + return nil, err } - arg, found = req.Option("f") - force, ok := arg.(bool) // TODO param - if found && !ok { - return nil, errors.New("failed to parse force flag") + force, err := req.Option("f").Bool() + if err != nil { + return nil, err } - arg, found = req.Option("b") - nBitsForKeypair, ok := arg.(int) // TODO param - if found && !ok { - return nil, errors.New("failed to get bits flag") - } else if !found { + nBitsForKeypair, err := req.Option("b").Int() + if err != nil { + return nil, err + } + if !req.Option("b").Found() { nBitsForKeypair = 4096 } diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 40d25c09a..e5cdc3465 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -66,10 +66,7 @@ func createRequest(args []string) (cmds.Request, *cmds.Command) { // or if a path was returned (user specified a valid subcommand), show the error message // (this means there was an option or argument error) if path != nil && len(path) > 0 { - help := false - opt, _ := req.Option("help") - help, _ = opt.(bool) - + help, _ := req.Option("help").Bool() if !help { fmt.Printf(errorFormat, err) } @@ -108,7 +105,7 @@ func createRequest(args []string) (cmds.Request, *cmds.Command) { ctx.ConfigRoot = configPath ctx.Config = conf - if _, found := req.Option("encoding"); !found { + if !req.Option("encoding").Found() { if req.Command().Marshallers != nil && req.Command().Marshallers[cmds.Text] != nil { req.SetOption("encoding", cmds.Text) } else { @@ -120,30 +117,25 @@ func createRequest(args []string) (cmds.Request, *cmds.Command) { } func handleOptions(req cmds.Request, root *cmds.Command) { - if help, found := req.Option("help"); found { - if helpBool, ok := help.(bool); helpBool && ok { - helpText, err := cmdsCli.HelpText("ipfs", root, req.Path()) - if err != nil { - fmt.Println(err.Error()) - } else { - fmt.Println(helpText) - } - exit(0) - } else if !ok { - fmt.Println("error: expected 'help' option to be a bool") - exit(1) + if help, err := req.Option("help").Bool(); help && err == nil { + helpText, err := cmdsCli.HelpText("ipfs", root, req.Path()) + if err != nil { + fmt.Println(err.Error()) + } else { + fmt.Println(helpText) } + exit(0) + } else if err != nil { + fmt.Println(err) + exit(1) } - if debug, found := req.Option("debug"); found { - if debugBool, ok := debug.(bool); debugBool && ok { - u.Debug = true - - u.SetAllLoggers(logging.DEBUG) - } else if !ok { - fmt.Println("error: expected 'debug' option to be a bool") - exit(1) - } + if debug, err := req.Option("debug").Bool(); debug && err == nil { + u.Debug = true + u.SetAllLoggers(logging.DEBUG) + } else if err != nil { + fmt.Println(err) + exit(1) } } @@ -154,19 +146,13 @@ func callCommand(req cmds.Request, root *cmds.Command) cmds.Response { res = root.Call(req) } else { - var found bool - var local interface{} - localBool := false - if local, found = req.Option("local"); found { - var ok bool - localBool, ok = local.(bool) - if !ok { - fmt.Println("error: expected 'local' option to be a bool") - exit(1) - } + local, err := req.Option("local").Bool() + if err != nil { + fmt.Println(err) + exit(1) } - if (!found || !localBool) && daemon.Locked(req.Context().ConfigRoot) { + if (!req.Option("local").Found() || !local) && daemon.Locked(req.Context().ConfigRoot) { addr, err := ma.NewMultiaddr(req.Context().Config.Addresses.API) if err != nil { fmt.Println(err) @@ -229,12 +215,12 @@ func outputResponse(res cmds.Response, root *cmds.Command) { } func getConfigRoot(req cmds.Request) (string, error) { - if opt, found := req.Option("config"); found { - if optStr, ok := opt.(string); ok { - return optStr, nil - } else { - return "", fmt.Errorf("Expected 'config' option to be a string") - } + configOpt, err := req.Option("config").String() + if err != nil { + return "", err + } + if configOpt != "" { + return configOpt, nil } configPath, err := config.PathRoot() diff --git a/commands/command.go b/commands/command.go index 1f531be85..fc8092d7b 100644 --- a/commands/command.go +++ b/commands/command.go @@ -66,13 +66,7 @@ func (c *Command) Call(req Request) Response { return res } - options, err := c.GetOptions(req.Path()) - if err != nil { - res.SetError(err, ErrClient) - return res - } - - err = req.ConvertOptions(options) + err = req.ConvertOptions() if err != nil { res.SetError(err, ErrClient) return res diff --git a/commands/command_test.go b/commands/command_test.go index 2186678b7..44a39d0c1 100644 --- a/commands/command_test.go +++ b/commands/command_test.go @@ -15,29 +15,23 @@ func TestOptionValidation(t *testing.T) { Run: noop, } - req := NewEmptyRequest() - req.SetOption("beep", 5) - req.SetOption("b", 10) - res := cmd.Call(req) - if res.Error() == nil { - t.Error("Should have failed (duplicate options)") - } + opts, _ := cmd.GetOptions(nil) - req = NewEmptyRequest() - req.SetOption("beep", "foo") - res = cmd.Call(req) + req := NewRequest(nil, nil, nil, nil, opts) + req.SetOption("beep", true) + res := cmd.Call(req) if res.Error() == nil { t.Error("Should have failed (incorrect type)") } - req = NewEmptyRequest() + req = NewRequest(nil, nil, nil, nil, opts) req.SetOption("beep", 5) res = cmd.Call(req) if res.Error() != nil { t.Error(res.Error(), "Should have passed") } - req = NewEmptyRequest() + req = NewRequest(nil, nil, nil, nil, opts) req.SetOption("beep", 5) req.SetOption("boop", "test") res = cmd.Call(req) @@ -45,7 +39,7 @@ func TestOptionValidation(t *testing.T) { t.Error("Should have passed") } - req = NewEmptyRequest() + req = NewRequest(nil, nil, nil, nil, opts) req.SetOption("b", 5) req.SetOption("B", "test") res = cmd.Call(req) @@ -53,32 +47,32 @@ func TestOptionValidation(t *testing.T) { t.Error("Should have passed") } - req = NewEmptyRequest() + req = NewRequest(nil, nil, nil, nil, opts) req.SetOption("foo", 5) res = cmd.Call(req) if res.Error() != nil { t.Error("Should have passed") } - req = NewEmptyRequest() + req = NewRequest(nil, nil, nil, nil, opts) req.SetOption(EncShort, "json") res = cmd.Call(req) if res.Error() != nil { t.Error("Should have passed") } - req = NewEmptyRequest() + req = NewRequest(nil, nil, nil, nil, opts) req.SetOption("b", "100") res = cmd.Call(req) if res.Error() != nil { t.Error("Should have passed") } - req = NewEmptyRequest() + req = NewRequest(nil, nil, nil, nil, opts) req.SetOption("b", ":)") res = cmd.Call(req) if res.Error() == nil { - t.Error(res.Error(), "Should have failed (string value not convertible to int)") + t.Error("Should have failed (string value not convertible to int)") } } @@ -107,13 +101,14 @@ func TestRegistration(t *testing.T) { Run: noop, } - res := cmdB.Call(NewRequest([]string{"a"}, nil, nil, nil, nil)) - if res.Error() == nil { + path := []string{"a"} + _, err := cmdB.GetOptions(path) + if err == nil { t.Error("Should have failed (option name collision)") } - res = cmdC.Call(NewEmptyRequest()) - if res.Error() == nil { + _, err = cmdC.GetOptions(nil) + if err == nil { t.Error("Should have failed (option name collision with global options)") } } diff --git a/commands/http/client.go b/commands/http/client.go index 9e8c1d3a7..55d6f11a7 100644 --- a/commands/http/client.go +++ b/commands/http/client.go @@ -34,23 +34,8 @@ func NewClient(address string) Client { } func (c *client) Send(req cmds.Request) (cmds.Response, error) { - var userEncoding string - if enc, found := req.Option(cmds.EncShort); found { - var ok bool - userEncoding, ok = enc.(string) - if !ok { - return nil, castError - } - req.SetOption(cmds.EncShort, cmds.JSON) - } else { - var ok bool - enc, _ := req.Option(cmds.EncLong) - userEncoding, ok = enc.(string) - if !ok { - return nil, castError - } - req.SetOption(cmds.EncLong, cmds.JSON) - } + userEncoding, _ := req.Option(cmds.EncShort).String() + req.SetOption(cmds.EncShort, cmds.JSON) query, inputStream, err := getQuery(req) if err != nil { @@ -72,7 +57,6 @@ func (c *client) Send(req cmds.Request) (cmds.Response, error) { if len(userEncoding) > 0 { req.SetOption(cmds.EncShort, userEncoding) - req.SetOption(cmds.EncLong, userEncoding) } return res, nil diff --git a/commands/http/handler.go b/commands/http/handler.go index a8fec766e..79d862fad 100644 --- a/commands/http/handler.go +++ b/commands/http/handler.go @@ -57,13 +57,12 @@ func (i Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Header().Set(streamHeader, "1") } else { - enc, _ := req.Option(cmds.EncShort) - encStr, ok := enc.(string) - if !ok { + enc, err := req.Option(cmds.EncShort).String() + if err != nil || len(enc) == 0 { w.WriteHeader(http.StatusInternalServerError) return } - mime := mimeTypes[encStr] + mime := mimeTypes[enc] w.Header().Set("Content-Type", mime) } diff --git a/commands/option.go b/commands/option.go index 8f11ace49..f8b3c3b3c 100644 --- a/commands/option.go +++ b/commands/option.go @@ -1,6 +1,9 @@ package commands -import "reflect" +import ( + "errors" + "reflect" +) // Types of Command options const ( @@ -30,7 +33,7 @@ func NewOption(kind reflect.Kind, names ...string) Option { } desc := names[len(names)-1] - names = names[:len(names)-2] + names = names[:len(names)-1] return Option{ Names: names, @@ -55,6 +58,73 @@ func StringOption(names ...string) Option { return NewOption(String, names...) } +type OptionValue struct { + value interface{} + found bool +} + +// Found returns true if the option value was provided by the user (not a default value) +func (ov OptionValue) Found() bool { + return ov.found +} + +// value accessor methods, gets the value as a certain type +func (ov OptionValue) Bool() (bool, error) { + val, ok := ov.value.(bool) + if !ok { + var err error + if ov.value != nil { + err = errors.New("error casting to bool") + } + return false, err + } + return val, nil +} +func (ov OptionValue) Int() (int, error) { + val, ok := ov.value.(int) + if !ok { + var err error + if ov.value != nil { + err = errors.New("error casting to int") + } + return 0, err + } + return val, nil +} +func (ov OptionValue) Uint() (uint, error) { + val, ok := ov.value.(uint) + if !ok { + var err error + if ov.value != nil { + err = errors.New("error casting to uint") + } + return 0, err + } + return val, nil +} +func (ov OptionValue) Float() (float64, error) { + val, ok := ov.value.(float64) + if !ok { + var err error + if ov.value != nil { + err = errors.New("error casting to float64") + } + return 0.0, err + } + return val, nil +} +func (ov OptionValue) String() (string, error) { + val, ok := ov.value.(string) + if !ok { + var err error + if ov.value != nil { + err = errors.New("error casting to string") + } + return "", err + } + return val, nil +} + // Flag names const ( EncShort = "enc" diff --git a/commands/request.go b/commands/request.go index c08c0b22a..25f59ad8c 100644 --- a/commands/request.go +++ b/commands/request.go @@ -21,15 +21,15 @@ type Context struct { // Request represents a call to a command from a consumer type Request interface { Path() []string - Option(name string) (interface{}, bool) - Options() map[string]interface{} + Option(name string) *OptionValue + Options() optMap SetOption(name string, val interface{}) Arguments() []interface{} // TODO: make argument value type instead of using interface{} Context() *Context SetContext(Context) Command() *Command - ConvertOptions(options map[string]Option) error + ConvertOptions() error } type request struct { @@ -47,31 +47,34 @@ func (r *request) Path() []string { } // Option returns the value of the option for given name. -func (r *request) Option(name string) (interface{}, bool) { +func (r *request) Option(name string) *OptionValue { val, found := r.options[name] if found { - return val, found + return &OptionValue{val, found} } // if a value isn't defined for that name, we will try to look it up by its aliases // find the option with the specified name option, found := r.optionDefs[name] - if found { - // try all the possible names, break if we find a value - for _, n := range option.Names { - val, found := r.options[n] - if found { - return val, found - } + if !found { + return nil + } + + // try all the possible names, break if we find a value + for _, n := range option.Names { + val, found = r.options[n] + if found { + return &OptionValue{val, found} } } - return nil, false + // MAYBE_TODO: use default value instead of nil + return &OptionValue{nil, false} } // Options returns a copy of the option map -func (r *request) Options() map[string]interface{} { +func (r *request) Options() optMap { output := make(optMap) for k, v := range r.options { output[k] = v @@ -136,11 +139,11 @@ var converters = map[reflect.Kind]converter{ }, } -func (r *request) ConvertOptions(options map[string]Option) error { +func (r *request) ConvertOptions() error { converted := make(map[string]interface{}) for k, v := range r.options { - opt, ok := options[k] + opt, ok := r.optionDefs[k] if !ok { continue } @@ -203,5 +206,9 @@ func NewRequest(path []string, opts optMap, args []interface{}, cmd *Command, op if optDefs == nil { optDefs = make(map[string]Option) } - return &request{path, opts, args, cmd, Context{}, optDefs} + + req := &request{path, opts, args, cmd, Context{}, optDefs} + req.ConvertOptions() + + return req } diff --git a/commands/response.go b/commands/response.go index 912eddb0a..ac5b6b293 100644 --- a/commands/response.go +++ b/commands/response.go @@ -108,18 +108,22 @@ func (r *response) Marshal() ([]byte, error) { return []byte{}, nil } - enc, found := r.req.Option(EncShort) - encStr, ok := enc.(string) - if !found || !ok || encStr == "" { + fmt.Println(r.req, r.req.Option(EncShort)) + if !r.req.Option(EncShort).Found() { return nil, fmt.Errorf("No encoding type was specified") } - encType := EncodingType(strings.ToLower(encStr)) + enc, err := r.req.Option(EncShort).String() + if err != nil { + return nil, err + } + encType := EncodingType(strings.ToLower(enc)) var marshaller Marshaller if r.req.Command() != nil && r.req.Command().Marshallers != nil { marshaller = r.req.Command().Marshallers[encType] } if marshaller == nil { + var ok bool marshaller, ok = marshallers[encType] if !ok { return nil, fmt.Errorf("No marshaller found for encoding type '%s'", enc) diff --git a/commands/response_test.go b/commands/response_test.go index c8033f2f9..6bc7417ea 100644 --- a/commands/response_test.go +++ b/commands/response_test.go @@ -12,25 +12,20 @@ type TestOutput struct { } func TestMarshalling(t *testing.T) { - req := NewEmptyRequest() + cmd := &Command{} + opts, _ := cmd.GetOptions(nil) + + req := NewRequest(nil, nil, nil, nil, opts) res := NewResponse(req) res.SetOutput(TestOutput{"beep", "boop", 1337}) - // get command global options so we can set the encoding option - cmd := Command{} - options, err := cmd.GetOptions(nil) - if err != nil { - t.Error(err) - } - - _, err = res.Marshal() + _, err := res.Marshal() if err == nil { t.Error("Should have failed (no encoding type specified in request)") } req.SetOption(EncShort, JSON) - req.ConvertOptions(options) bytes, err := res.Marshal() if err != nil { diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go index 7bd7128f9..792f8dec4 100644 --- a/core/commands2/mount_unix.go +++ b/core/commands2/mount_unix.go @@ -44,17 +44,15 @@ not be listable, as it is virtual. Accessing known paths directly. // update fsdir with flag. fsdir := ctx.Config.Mounts.IPFS - opt, _ := req.Option("f") - if val, ok := opt.(string); ok && val != "" { - fsdir = val + if req.Option("f").Found() { + fsdir, _ = req.Option("f").String() } fsdone := mountIpfs(ctx.Node, fsdir) // get default mount points nsdir := ctx.Config.Mounts.IPNS - opt, _ = req.Option("f") - if val, ok := opt.(string); ok && val != "" { - nsdir = val + if req.Option("n").Found() { + nsdir, _ = req.Option("n").String() } nsdone := mountIpns(ctx.Node, nsdir, fsdir) diff --git a/core/commands2/pin.go b/core/commands2/pin.go index 77d382c9a..1d7930823 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -35,8 +35,7 @@ on disk. n := req.Context().Node // set recursive flag - opt, _ := req.Option("recursive") - recursive, _ := opt.(bool) // false if cast fails. + recursive, _ := req.Option("recursive").Bool() // false if cast fails. paths, err := internal.CastToStrings(req.Arguments()) if err != nil { @@ -70,8 +69,7 @@ collected if needed. n := req.Context().Node // set recursive flag - opt, _ := req.Option("recursive") - recursive, _ := opt.(bool) // false if cast fails. + recursive, _ := req.Option("recursive").Bool() // false if cast fails. paths, err := internal.CastToStrings(req.Arguments()) if err != nil { diff --git a/core/commands2/refs.go b/core/commands2/refs.go index f4eb4d2e2..a72324fd4 100644 --- a/core/commands2/refs.go +++ b/core/commands2/refs.go @@ -35,17 +35,8 @@ Note: list all refs recursively with -r.`, Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node - opt, found := req.Option("unique") - unique, ok := opt.(bool) - if !ok && found { - unique = false - } - - opt, found = req.Option("recursive") - recursive, ok := opt.(bool) - if !ok && found { - recursive = false - } + unique, _ := req.Option("unique").Bool() + recursive, _ := req.Option("recursive").Bool() paths, err := internal.CastToStrings(req.Arguments()) if err != nil { diff --git a/core/commands2/version.go b/core/commands2/version.go index ca80f538f..1e7adee8c 100644 --- a/core/commands2/version.go +++ b/core/commands2/version.go @@ -1,8 +1,6 @@ package commands import ( - "errors" - cmds "github.com/jbenet/go-ipfs/commands" config "github.com/jbenet/go-ipfs/config" ) @@ -29,11 +27,7 @@ var versionCmd = &cmds.Command{ v := res.Output().(*VersionOutput) s := "" - opt, found := res.Request().Option("number") - number, ok := opt.(bool) - if found && !ok { - return nil, errors.New("cast error") - } + number, _ := res.Request().Option("number").Bool() if !number { s += "ipfs version " From 17c59231603066d656bfcfa76c7cee40b1e7f605 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 10 Nov 2014 22:32:22 -0800 Subject: [PATCH 214/383] core/commands2: Fixed init option definition --- cmd/ipfs2/init.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index d06525aec..eb4b0d423 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -20,7 +20,7 @@ var initCmd = &cmds.Command{ `, Options: []cmds.Option{ - cmds.UintOption("bits", "b", "Number of bits to use in the generated RSA private key (defaults to 4096)"), + cmds.IntOption("bits", "b", "Number of bits to use in the generated RSA private key (defaults to 4096)"), cmds.StringOption("passphrase", "p", "Passphrase for encrypting the private key"), cmds.BoolOption("force", "f", "Overwrite existing config (if it exists)"), cmds.StringOption("datastore", "d", "Location for the IPFS data store"), From a9bd172414c8553ba59b394b52aa25945a05fbc1 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 10 Nov 2014 22:41:01 -0800 Subject: [PATCH 215/383] commands: Fixed handling of int/uint option values --- commands/request.go | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/commands/request.go b/commands/request.go index 25f59ad8c..d2b018219 100644 --- a/commands/request.go +++ b/commands/request.go @@ -129,10 +129,18 @@ var converters = map[reflect.Kind]converter{ return strconv.ParseBool(v) }, Int: func(v string) (interface{}, error) { - return strconv.ParseInt(v, 0, 32) + val, err := strconv.ParseInt(v, 0, 32) + if err != nil { + return nil, err + } + return int(val), err }, Uint: func(v string) (interface{}, error) { - return strconv.ParseInt(v, 0, 32) + val, err := strconv.ParseUint(v, 0, 32) + if err != nil { + return nil, err + } + return int(val), err }, Float: func(v string) (interface{}, error) { return strconv.ParseFloat(v, 64) @@ -140,8 +148,6 @@ var converters = map[reflect.Kind]converter{ } func (r *request) ConvertOptions() error { - converted := make(map[string]interface{}) - for k, v := range r.options { opt, ok := r.optionDefs[k] if !ok { @@ -149,8 +155,6 @@ func (r *request) ConvertOptions() error { } kind := reflect.TypeOf(v).Kind() - var value interface{} - if kind != opt.Type { if kind == String { convert := converters[opt.Type] @@ -163,14 +167,14 @@ func (r *request) ConvertOptions() error { return fmt.Errorf("Could not convert string value '%s' to type '%s'", v, opt.Type.String()) } - value = val + r.options[k] = val } else { return fmt.Errorf("Option '%s' should be type '%s', but got type '%s'", k, opt.Type.String(), kind.String()) } } else { - value = v + r.options[k] = v } for _, name := range opt.Names { @@ -178,12 +182,9 @@ func (r *request) ConvertOptions() error { return fmt.Errorf("Duplicate command options were provided ('%s' and '%s')", k, name) } - - converted[name] = value } } - r.options = converted return nil } From f1fc26e70b8982ebe9e62478cc08cd45e50577af Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Mon, 10 Nov 2014 22:41:30 -0800 Subject: [PATCH 216/383] commands: Removed a fmt.Println --- commands/response.go | 1 - 1 file changed, 1 deletion(-) diff --git a/commands/response.go b/commands/response.go index ac5b6b293..8d8bdccda 100644 --- a/commands/response.go +++ b/commands/response.go @@ -108,7 +108,6 @@ func (r *response) Marshal() ([]byte, error) { return []byte{}, nil } - fmt.Println(r.req, r.req.Option(EncShort)) if !r.req.Option(EncShort).Found() { return nil, fmt.Errorf("No encoding type was specified") } From 203a0723ba89f8043a2fbfd153444aca2c452a8b Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 10 Nov 2014 22:19:57 -0800 Subject: [PATCH 217/383] refactor(ipfs2/main) return errors in main --- cmd/ipfs2/main.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index e5cdc3465..145864eeb 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -35,7 +35,11 @@ func main() { handleInterrupt() args := os.Args[1:] - req, root := createRequest(args) + req, root, err := createRequest(args) + if err != nil { + fmt.Println(err) + exit(1) + } handleOptions(req, root) // if debugging, setup profiling. @@ -56,7 +60,7 @@ func main() { exit(0) } -func createRequest(args []string) (cmds.Request, *cmds.Command) { +func createRequest(args []string) (cmds.Request, *cmds.Command, error) { req, root, cmd, path, err := cmdsCli.Parse(args, Root, commands.Root) // handle parse error (which means the commandline input was wrong, @@ -80,27 +84,24 @@ func createRequest(args []string) (cmds.Request, *cmds.Command) { } // generate the help text for the command the user was trying to call (or root) - helpText, err := cmdsCli.HelpText("ipfs", root, path) - if err != nil { - fmt.Println(err) + helpText, htErr := cmdsCli.HelpText("ipfs", root, path) + if htErr != nil { + fmt.Println(htErr) } else { fmt.Println(helpText) } - exit(1) + return nil, nil, err } configPath, err := getConfigRoot(req) if err != nil { - fmt.Println(err) - exit(1) + return nil, nil, err } conf, err := getConfig(configPath) if err != nil { - fmt.Println(err) - exit(1) + return nil, nil, err } - ctx := req.Context() ctx.ConfigRoot = configPath ctx.Config = conf @@ -113,7 +114,7 @@ func createRequest(args []string) (cmds.Request, *cmds.Command) { } } - return req, root + return req, root, nil } func handleOptions(req cmds.Request, root *cmds.Command) { From f79f1267f797350ed6ea23584f4d8c0caf6e2a30 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 10 Nov 2014 22:23:49 -0800 Subject: [PATCH 218/383] refactor(ipfs2/main) split lines --- cmd/ipfs2/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 145864eeb..23ec2b62c 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -118,7 +118,8 @@ func createRequest(args []string) (cmds.Request, *cmds.Command, error) { } func handleOptions(req cmds.Request, root *cmds.Command) { - if help, err := req.Option("help").Bool(); help && err == nil { + help, err := req.Option("help").Bool() + if help && err == nil { helpText, err := cmdsCli.HelpText("ipfs", root, req.Path()) if err != nil { fmt.Println(err.Error()) From 28be8a617122914c83642468cdf3370f6be2216d Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 10 Nov 2014 22:25:19 -0800 Subject: [PATCH 219/383] refactor(ipfs2/main) check err before --- cmd/ipfs2/main.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 23ec2b62c..3b1512e58 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -119,7 +119,12 @@ func createRequest(args []string) (cmds.Request, *cmds.Command, error) { func handleOptions(req cmds.Request, root *cmds.Command) { help, err := req.Option("help").Bool() - if help && err == nil { + if err != nil { + fmt.Println(err) + exit(1) + } + + if help { helpText, err := cmdsCli.HelpText("ipfs", root, req.Path()) if err != nil { fmt.Println(err.Error()) @@ -127,9 +132,6 @@ func handleOptions(req cmds.Request, root *cmds.Command) { fmt.Println(helpText) } exit(0) - } else if err != nil { - fmt.Println(err) - exit(1) } if debug, err := req.Option("debug").Bool(); debug && err == nil { From 2473d2d7206e062309196b6686cb4df90d7d72a2 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 10 Nov 2014 22:26:32 -0800 Subject: [PATCH 220/383] refactor(ipfs2/main) same for debug --- cmd/ipfs2/main.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 3b1512e58..1c3f6a3b3 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -134,13 +134,15 @@ func handleOptions(req cmds.Request, root *cmds.Command) { exit(0) } - if debug, err := req.Option("debug").Bool(); debug && err == nil { - u.Debug = true - u.SetAllLoggers(logging.DEBUG) - } else if err != nil { + debug, err := req.Option("debug").Bool() + if err != nil { fmt.Println(err) exit(1) } + if debug { + u.Debug = true + u.SetAllLoggers(logging.DEBUG) + } } func callCommand(req cmds.Request, root *cmds.Command) cmds.Response { From 13a90537d615cff1996cb5a78f58aa7cc2f15b0e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 10 Nov 2014 22:40:05 -0800 Subject: [PATCH 221/383] refactor(ipfs2/main) * bring debug checking back to top level so we have more control over CPU profiling. * bring help text up to top level so we can exit from the program at the top level instead of within an arbitrary function --- cmd/ipfs2/main.go | 56 ++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 1c3f6a3b3..7178bd351 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -32,32 +32,54 @@ const ( var ofi io.WriteCloser func main() { + err := run() + if err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +func run() error { handleInterrupt() args := os.Args[1:] req, root, err := createRequest(args) if err != nil { - fmt.Println(err) - exit(1) + return err + } + + debug, err := req.Option("debug").Bool() + if err != nil { + return err + } + if debug { + u.Debug = true + u.SetAllLoggers(logging.DEBUG) } - handleOptions(req, root) // if debugging, setup profiling. if u.Debug { var err error ofi, err = os.Create("cpu.prof") if err != nil { - fmt.Println(err) - return + return err } pprof.StartCPUProfile(ofi) } + helpTextDisplayed, err := handleHelpOption(req, root) + if err != nil { + return err + } + if helpTextDisplayed { + return nil + } + res := callCommand(req, root) outputResponse(res, root) - exit(0) + return nil } func createRequest(args []string) (cmds.Request, *cmds.Command, error) { @@ -117,32 +139,22 @@ func createRequest(args []string) (cmds.Request, *cmds.Command, error) { return req, root, nil } -func handleOptions(req cmds.Request, root *cmds.Command) { +func handleHelpOption(req cmds.Request, root *cmds.Command) (helpTextDisplayed bool, err error) { help, err := req.Option("help").Bool() if err != nil { - fmt.Println(err) - exit(1) + return false, err } if help { helpText, err := cmdsCli.HelpText("ipfs", root, req.Path()) if err != nil { - fmt.Println(err.Error()) - } else { - fmt.Println(helpText) + return false, err } - exit(0) + fmt.Println(helpText) + return true, nil } - debug, err := req.Option("debug").Bool() - if err != nil { - fmt.Println(err) - exit(1) - } - if debug { - u.Debug = true - u.SetAllLoggers(logging.DEBUG) - } + return false, nil } func callCommand(req cmds.Request, root *cmds.Command) cmds.Response { From cda68a19d01abf3b6eb91df0b02edf4a2672de47 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 10 Nov 2014 22:45:43 -0800 Subject: [PATCH 222/383] refactor(ipfs2/main) return err --- cmd/ipfs2/main.go | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 7178bd351..ab21894e8 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -76,7 +76,10 @@ func run() error { return nil } - res := callCommand(req, root) + res, err := callCommand(req, root) + if err != nil { + return err + } outputResponse(res, root) return nil @@ -157,45 +160,40 @@ func handleHelpOption(req cmds.Request, root *cmds.Command) (helpTextDisplayed b return false, nil } -func callCommand(req cmds.Request, root *cmds.Command) cmds.Response { +func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { var res cmds.Response - if root == Root { + if root == Root { // TODO explain what it means when root == Root res = root.Call(req) } else { local, err := req.Option("local").Bool() if err != nil { - fmt.Println(err) - exit(1) + return nil, err } if (!req.Option("local").Found() || !local) && daemon.Locked(req.Context().ConfigRoot) { addr, err := ma.NewMultiaddr(req.Context().Config.Addresses.API) if err != nil { - fmt.Println(err) - exit(1) + return nil, err } _, host, err := manet.DialArgs(addr) if err != nil { - fmt.Println(err) - exit(1) + return nil, err } client := cmdsHttp.NewClient(host) res, err = client.Send(req) if err != nil { - fmt.Println(err) - exit(1) + return nil, err } } else { node, err := core.NewIpfsNode(req.Context().Config, false) if err != nil { - fmt.Println(err) - exit(1) + return nil, err } defer node.Close() req.Context().Node = node @@ -204,7 +202,7 @@ func callCommand(req cmds.Request, root *cmds.Command) cmds.Response { } } - return res + return res, nil } func outputResponse(res cmds.Response, root *cmds.Command) { From fa5ca3f27fa93940aabeaae33c05f71907fa36c9 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 10 Nov 2014 22:58:47 -0800 Subject: [PATCH 223/383] refactor(ipfs2/main) output response --- cmd/ipfs2/main.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index ab21894e8..ff82d3f15 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -1,6 +1,7 @@ package main import ( + "errors" "fmt" "io" "os" @@ -80,7 +81,11 @@ func run() error { if err != nil { return err } - outputResponse(res, root) + + err = outputResponse(res, root) + if err != nil { + return err + } return nil } @@ -205,10 +210,15 @@ func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { return res, nil } -func outputResponse(res cmds.Response, root *cmds.Command) { +func outputResponse(res cmds.Response, root *cmds.Command) error { if res.Error() != nil { fmt.Printf(errorFormat, res.Error().Error()) + if res.Error().Code != cmds.ErrClient { + return res.Error() + } + + // if this is a client error, we try to display help text if res.Error().Code == cmds.ErrClient { helpText, err := cmdsCli.HelpText("ipfs", root, res.Request().Path()) if err != nil { @@ -218,16 +228,17 @@ func outputResponse(res cmds.Response, root *cmds.Command) { } } - exit(1) + emptyErr := errors.New("") // already displayed error text, but want to exit(1) + return emptyErr } out, err := res.Reader() if err != nil { - fmt.Println(err.Error()) - return + return err } io.Copy(os.Stdout, out) + return nil } func getConfigRoot(req cmds.Request) (string, error) { From 9268bdd56edb45a09ae4f142dd9085f25285593f Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 10 Nov 2014 23:08:01 -0800 Subject: [PATCH 224/383] refactor(ipfs2/main) replace with equivalent action --- cmd/ipfs2/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index ff82d3f15..af7ab635f 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -283,7 +283,7 @@ func handleInterrupt() { go func() { for _ = range c { log.Info("Received interrupt signal, terminating...") - exit(0) + os.Exit(0) } }() } From d72af9c910184a48ef1ee9df638ddc92f9b0cf10 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 10 Nov 2014 23:09:33 -0800 Subject: [PATCH 225/383] refactor(ipfs2/main) return err --- cmd/ipfs2/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index af7ab635f..dc1f97396 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -269,7 +269,7 @@ func getConfig(path string) (*config.Config, error) { func writeHeapProfileToFile() error { mprof, err := os.Create(heapProfile) if err != nil { - log.Fatal(err) + return err } defer mprof.Close() return pprof.WriteHeapProfile(mprof) From 74b38cb65fc0612c2679a8df0547ff88ffc57314 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 10 Nov 2014 23:25:25 -0800 Subject: [PATCH 226/383] refactor(ipfs2/main) change the way we handle profiling --- cmd/ipfs2/main.go | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index dc1f97396..6bb33c585 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -26,12 +26,11 @@ import ( var log = u.Logger("cmd/ipfs") const ( - heapProfile = "ipfs.mprof" + cpuProfile = "ipfs.cpuprof" + heapProfile = "ipfs.memprof" errorFormat = "ERROR: %v\n\n" ) -var ofi io.WriteCloser - func main() { err := run() if err != nil { @@ -58,15 +57,12 @@ func run() error { u.SetAllLoggers(logging.DEBUG) } - // if debugging, setup profiling. if u.Debug { - var err error - ofi, err = os.Create("cpu.prof") + stopProfilingFunc, err := startProfiling() if err != nil { return err } - - pprof.StartCPUProfile(ofi) + defer stopProfilingFunc() // to be executed as late as possible } helpTextDisplayed, err := handleHelpOption(req, root) @@ -228,7 +224,7 @@ func outputResponse(res cmds.Response, root *cmds.Command) error { } } - emptyErr := errors.New("") // already displayed error text, but want to exit(1) + emptyErr := errors.New("") // already displayed error text return emptyErr } @@ -266,12 +262,34 @@ func getConfig(path string) (*config.Config, error) { return config.Load(configFile) } +// startProfiling begins CPU profiling and returns a `stop` function to be +// executed as late as possible. The stop function captures the memprofile. +func startProfiling() (func(), error) { + + // start CPU profiling as early as possible + ofi, err := os.Create(cpuProfile) + if err != nil { + return nil, err + } + pprof.StartCPUProfile(ofi) + + stopProfiling := func() { + pprof.StopCPUProfile() + defer ofi.Close() // captured by the closure + err := writeHeapProfileToFile() + if err != nil { + log.Critical(err) + } + } + return stopProfiling, nil +} + func writeHeapProfileToFile() error { mprof, err := os.Create(heapProfile) if err != nil { return err } - defer mprof.Close() + defer mprof.Close() // _after_ writing the heap profile return pprof.WriteHeapProfile(mprof) } From ad5ad912ce95f79353eaf0d11e512704d5cc7760 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 10 Nov 2014 23:37:07 -0800 Subject: [PATCH 227/383] refactor(ipfs2/main) use guard --- cmd/ipfs2/main.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 6bb33c585..3a9cd28a4 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -148,17 +148,16 @@ func handleHelpOption(req cmds.Request, root *cmds.Command) (helpTextDisplayed b if err != nil { return false, err } - - if help { - helpText, err := cmdsCli.HelpText("ipfs", root, req.Path()) - if err != nil { - return false, err - } - fmt.Println(helpText) - return true, nil + if !help { + return false, nil } + helpText, err := cmdsCli.HelpText("ipfs", root, req.Path()) + if err != nil { + return false, err + } + fmt.Println(helpText) - return false, nil + return true, nil } func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { From 8b14012bba2210e5c07a7e3554c17881effbef27 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Mon, 10 Nov 2014 23:37:51 -0800 Subject: [PATCH 228/383] refactor(ipfs2/main) rm exit --- cmd/ipfs2/main.go | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 3a9cd28a4..83722bbf3 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -304,17 +304,3 @@ func handleInterrupt() { } }() } - -func exit(code int) { - if u.Debug { - pprof.StopCPUProfile() - ofi.Close() - - err := writeHeapProfileToFile() - if err != nil { - log.Critical(err) - } - } - - os.Exit(code) -} From 40e96a1fa6cd59ab3de290d4451359da8b5e0a10 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 15:54:53 -0800 Subject: [PATCH 229/383] commands: Added Argument helper constructors --- commands/argument.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/commands/argument.go b/commands/argument.go index 6c9c7a3d0..ac407aba6 100644 --- a/commands/argument.go +++ b/commands/argument.go @@ -14,3 +14,23 @@ type Argument struct { Variadic bool Description string } + +func StringArg(name string, required, variadic bool, description string) Argument { + return Argument{ + Name: name, + Type: ArgString, + Required: required, + Variadic: variadic, + Description: description, + } +} + +func FileArg(name string, required, variadic bool, description string) Argument { + return Argument{ + Name: name, + Type: ArgFile, + Required: required, + Variadic: variadic, + Description: description, + } +} From c468a4dbf5cc442713ed0eda5057bfdd8b8c74f9 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 15:55:20 -0800 Subject: [PATCH 230/383] core/commands2: Use Argument constructors in commands --- cmd/ipfs2/tour.go | 3 +-- core/commands2/add.go | 2 +- core/commands2/block.go | 4 ++-- core/commands2/bootstrap.go | 4 ++-- core/commands2/cat.go | 3 +-- core/commands2/config.go | 6 ++---- core/commands2/log.go | 6 ++---- core/commands2/ls.go | 3 +-- core/commands2/object.go | 15 +++++---------- core/commands2/pin.go | 6 ++---- core/commands2/publish.go | 6 ++---- core/commands2/refs.go | 3 +-- core/commands2/resolve.go | 3 +-- 13 files changed, 23 insertions(+), 41 deletions(-) diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index 00b845601..9d3e1247b 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -22,8 +22,7 @@ IPFS very quickly. To start, run: `, Arguments: []cmds.Argument{ - cmds.Argument{"number", cmds.ArgString, false, false, - "The number of the topic you would like to tour"}, + cmds.StringArg("number", false, false, "The number of the topic you would like to tour"), }, Subcommands: map[string]*cmds.Command{ "list": cmdIpfsTourList, diff --git a/core/commands2/add.go b/core/commands2/add.go index 043ac32cb..4913b0d56 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -26,7 +26,7 @@ var addCmd = &cmds.Command{ cmds.BoolOption("recursive", "r", "Must be specified when adding directories"), }, Arguments: []cmds.Argument{ - cmds.Argument{"file", cmds.ArgFile, true, true, "The path to a file to be added to IPFS"}, + cmds.FileArg("file", true, true, "The path to a file to be added to IPFS"), }, Description: "Add an object to ipfs.", Help: `Adds contents of to ipfs. Use -r to add directories. diff --git a/core/commands2/block.go b/core/commands2/block.go index ef9e554a8..65097ab17 100644 --- a/core/commands2/block.go +++ b/core/commands2/block.go @@ -38,7 +38,7 @@ var blockGetCmd = &cmds.Command{ It outputs to stdout, and is a base58 encoded multihash.`, Arguments: []cmds.Argument{ - cmds.Argument{"key", cmds.ArgString, true, false, "The base58 multihash of an existing block to get"}, + cmds.StringArg("key", true, false, "The base58 multihash of an existing block to get"), }, Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node @@ -74,7 +74,7 @@ var blockPutCmd = &cmds.Command{ It reads from stdin, and is a base58 encoded multihash.`, Arguments: []cmds.Argument{ - cmds.Argument{"data", cmds.ArgFile, true, false, "The data to be stored as an IPFS block"}, + cmds.FileArg("data", true, false, "The data to be stored as an IPFS block"), }, Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node diff --git a/core/commands2/bootstrap.go b/core/commands2/bootstrap.go index 9c6de75bb..dd0edb0b0 100644 --- a/core/commands2/bootstrap.go +++ b/core/commands2/bootstrap.go @@ -39,7 +39,7 @@ in the bootstrap list). ` + bootstrapSecurityWarning, Arguments: []cmds.Argument{ - cmds.Argument{"peer", cmds.ArgString, true, true, peerOptionDesc}, + cmds.StringArg("peer", true, true, peerOptionDesc), }, Run: func(req cmds.Request) (interface{}, error) { input, err := bootstrapInputToPeers(req.Arguments()) @@ -79,7 +79,7 @@ var bootstrapRemoveCmd = &cmds.Command{ ` + bootstrapSecurityWarning, Arguments: []cmds.Argument{ - cmds.Argument{"peer", cmds.ArgString, true, true, peerOptionDesc}, + cmds.StringArg("peer", true, true, peerOptionDesc), }, Run: func(req cmds.Request) (interface{}, error) { input, err := bootstrapInputToPeers(req.Arguments()) diff --git a/core/commands2/cat.go b/core/commands2/cat.go index e8c39a533..89384fe55 100644 --- a/core/commands2/cat.go +++ b/core/commands2/cat.go @@ -16,8 +16,7 @@ it contains. `, Arguments: []cmds.Argument{ - cmds.Argument{"ipfs-path", cmds.ArgString, true, true, - "The path to the IPFS object(s) to be outputted"}, + cmds.StringArg("ipfs-path", true, true, "The path to the IPFS object(s) to be outputted"), }, Run: func(req cmds.Request) (interface{}, error) { node := req.Context().Node diff --git a/core/commands2/config.go b/core/commands2/config.go index dd15653c7..59dcd77e1 100644 --- a/core/commands2/config.go +++ b/core/commands2/config.go @@ -31,10 +31,8 @@ var configCmd = &cmds.Command{ `, Arguments: []cmds.Argument{ - cmds.Argument{"key", cmds.ArgString, true, false, - "The key of the config entry (e.g. \"Addresses.API\")"}, - cmds.Argument{"value", cmds.ArgString, false, false, - "The value to set the config entry to"}, + cmds.StringArg("key", true, false, "The key of the config entry (e.g. \"Addresses.API\")"), + cmds.StringArg("value", false, false, "The value to set the config entry to"), }, Run: func(req cmds.Request) (interface{}, error) { args := req.Arguments() diff --git a/core/commands2/log.go b/core/commands2/log.go index 12a8a28f9..4ed3233ad 100644 --- a/core/commands2/log.go +++ b/core/commands2/log.go @@ -14,10 +14,8 @@ output of a running daemon. `, Arguments: []cmds.Argument{ - cmds.Argument{"subsystem", cmds.ArgString, true, false, - "the subsystem logging identifier. Use * for all subsystems."}, - cmds.Argument{"level", cmds.ArgString, true, false, - "one of: debug, info, notice, warning, error, critical"}, + cmds.StringArg("subsystem", true, false, "the subsystem logging identifier. Use * for all subsystems."), + cmds.StringArg("level", true, false, "one of: debug, info, notice, warning, error, critical"), }, Run: func(req cmds.Request) (interface{}, error) { args := req.Arguments() diff --git a/core/commands2/ls.go b/core/commands2/ls.go index 1849740b3..0850178a5 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -31,8 +31,7 @@ it contains, with the following format: `, Arguments: []cmds.Argument{ - cmds.Argument{"ipfs-path", cmds.ArgString, false, true, - "The path to the IPFS object(s) to list links from"}, + cmds.StringArg("ipfs-path", false, true, "The path to the IPFS object(s) to list links from"), }, Run: func(req cmds.Request) (interface{}, error) { node := req.Context().Node diff --git a/core/commands2/object.go b/core/commands2/object.go index 48908334f..48e1b27a3 100644 --- a/core/commands2/object.go +++ b/core/commands2/object.go @@ -44,8 +44,7 @@ output is the raw data of the object. `, Arguments: []cmds.Argument{ - cmds.Argument{"key", cmds.ArgString, true, false, - "Key of the object to retrieve, in base58-encoded multihash format"}, + cmds.StringArg("key", true, false, "Key of the object to retrieve, in base58-encoded multihash format"), }, Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node @@ -65,8 +64,7 @@ var objectLinksCmd = &cmds.Command{ It outputs to stdout, and is a base58 encoded multihash.`, Arguments: []cmds.Argument{ - cmds.Argument{"key", cmds.ArgString, true, false, - "Key of the object to retrieve, in base58-encoded multihash format"}, + cmds.StringArg("key", true, false, "Key of the object to retrieve, in base58-encoded multihash format"), }, Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node @@ -94,8 +92,7 @@ This command outputs data in the following encodings: (Specified by the "--encoding" or "-enc" flags)`, Arguments: []cmds.Argument{ - cmds.Argument{"key", cmds.ArgString, true, false, - "Key of the object to retrieve\n(in base58-encoded multihash format)"}, + cmds.StringArg("key", true, false, "Key of the object to retrieve\n(in base58-encoded multihash format)"), }, Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node @@ -146,10 +143,8 @@ Data should be in the format specified by . `, Arguments: []cmds.Argument{ - cmds.Argument{"data", cmds.ArgFile, true, false, - "Data to be stored as a DAG object\nMust be encoded as specified in "}, - cmds.Argument{"encoding", cmds.ArgString, true, false, - "Encoding type of , either \"protobuf\" or \"json\""}, + cmds.FileArg("data", true, false, "Data to be stored as a DAG object\nMust be encoded as specified in "), + cmds.StringArg("encoding", true, false, "Encoding type of , either \"protobuf\" or \"json\""), }, Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node diff --git a/core/commands2/pin.go b/core/commands2/pin.go index 1d7930823..84e8c3749 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -25,8 +25,7 @@ on disk. `, Arguments: []cmds.Argument{ - cmds.Argument{"ipfs-path", cmds.ArgString, true, true, - "Path to object(s) to be pinned"}, + cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be pinned"), }, Options: []cmds.Option{ cmds.BoolOption("recursive", "r", "Recursively pin the object linked to by the specified object(s)"), @@ -59,8 +58,7 @@ collected if needed. `, Arguments: []cmds.Argument{ - cmds.Argument{"ipfs-path", cmds.ArgString, true, true, - "Path to object(s) to be unpinned"}, + cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be unpinned"), }, Options: []cmds.Option{ cmds.BoolOption("recursive", "r", "Recursively unpin the object linked to by the specified object(s)"), diff --git a/core/commands2/publish.go b/core/commands2/publish.go index e5c8c89bf..68c5d71c2 100644 --- a/core/commands2/publish.go +++ b/core/commands2/publish.go @@ -34,10 +34,8 @@ Publish a to another public key: `, Arguments: []cmds.Argument{ - cmds.Argument{"name", cmds.ArgString, false, false, - "The IPNS name to publish to. Defaults to your node's peerID"}, - cmds.Argument{"ipfs-path", cmds.ArgString, true, false, - "IPFS path of the obejct to be published at "}, + cmds.StringArg("name", false, false, "The IPNS name to publish to. Defaults to your node's peerID"), + cmds.StringArg("ipfs-path", true, false, "IPFS path of the obejct to be published at "), }, Run: func(req cmds.Request) (interface{}, error) { log.Debug("Begin Publish") diff --git a/core/commands2/refs.go b/core/commands2/refs.go index a72324fd4..9efc06b54 100644 --- a/core/commands2/refs.go +++ b/core/commands2/refs.go @@ -25,8 +25,7 @@ hashes it contains, with the following format: Note: list all refs recursively with -r.`, Arguments: []cmds.Argument{ - cmds.Argument{"ipfs-path", cmds.ArgString, true, true, - "Path to the object(s) to list refs from"}, + cmds.StringArg("ipfs-path", true, true, "Path to the object(s) to list refs from"), }, Options: []cmds.Option{ cmds.BoolOption("unique", "u", "Omit duplicate refs from output"), diff --git a/core/commands2/resolve.go b/core/commands2/resolve.go index f41c0c1d3..422ee32ee 100644 --- a/core/commands2/resolve.go +++ b/core/commands2/resolve.go @@ -28,8 +28,7 @@ Resolve te value of another name: `, Arguments: []cmds.Argument{ - cmds.Argument{"name", cmds.ArgString, false, false, - "The IPNS name to resolve. Defaults to your node's peerID."}, + cmds.StringArg("name", false, false, "The IPNS name to resolve. Defaults to your node's peerID."), }, Run: func(req cmds.Request) (interface{}, error) { From 4902361fd58dd2d1b381cde1b04fc7635e6f450c Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 16:20:32 -0800 Subject: [PATCH 231/383] cmd/ipfs2: Updated readme --- cmd/ipfs2/README.md | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/cmd/ipfs2/README.md b/cmd/ipfs2/README.md index 0051f68e9..5b47554b3 100644 --- a/cmd/ipfs2/README.md +++ b/cmd/ipfs2/README.md @@ -10,20 +10,29 @@ ipfs - global versioned p2p merkledag file system Basic commands: - add Add an object to ipfs. - cat Show ipfs object data. - ls List links from an object. - refs List link hashes from an object. + init Initialize ipfs local configurationx + add Add an object to ipfs + cat Show ipfs object data + ls List links from an object Tool commands: - config Manage configuration. - version Show ipfs version information. - commands List all available commands. + config Manage configuration + update Download and apply go-ipfs updates + version Show ipfs version information + commands List all available commands Advanced Commands: - mount Mount an ipfs read-only mountpoint. + mount Mount an ipfs read-only mountpoint + serve Serve an interface to ipfs + diag Print diagnostics + +Plumbing commands: + + block Interact with raw blocks in the datastore + object Interact with raw dag nodes + Use "ipfs help " for more information about a command. ``` From aea2fce9879c78e7f674b98075fbe4eee62e55d7 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 16:26:46 -0800 Subject: [PATCH 232/383] cmd/ipfs2: Added explanation comment to ipfsHandler --- cmd/ipfs2/ipfsHandler.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/ipfs2/ipfsHandler.go b/cmd/ipfs2/ipfsHandler.go index b522607b0..07a64724a 100644 --- a/cmd/ipfs2/ipfsHandler.go +++ b/cmd/ipfs2/ipfsHandler.go @@ -22,6 +22,8 @@ type ipfs interface { NewDagReader(nd *dag.Node) (io.Reader, error) } +// ipfsHandler is a HTTP handler that serves IPFS objects (accessible by default at /ipfs/) +// (it serves requests like GET /ipfs/QmVRzPKPzNtSrEzBFm2UZfxmPAgnaLke4DMcerbsGGSaFe/link) type ipfsHandler struct { node *core.IpfsNode } From b358bb3ffd17fcb72ea654ce499abb8ebf2719ba Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 16:32:18 -0800 Subject: [PATCH 233/383] cmd/ipfs2: Added comment to explain default encoding logic --- cmd/ipfs2/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 83722bbf3..b6a15be3b 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -132,6 +132,8 @@ func createRequest(args []string) (cmds.Request, *cmds.Command, error) { ctx.ConfigRoot = configPath ctx.Config = conf + // if no encoding was specified by user, default to plaintext encoding + // (if command doesn't support plaintext, use JSON instead) if !req.Option("encoding").Found() { if req.Command().Marshallers != nil && req.Command().Marshallers[cmds.Text] != nil { req.SetOption("encoding", cmds.Text) From f48ce10efb23f1671b2c994ebefddc7b7727f0d6 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 16:38:57 -0800 Subject: [PATCH 234/383] commands/cli: Added comment to explain multiple root support in Parse --- commands/cli/parse.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index 982691a80..3a45e3f81 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -11,6 +11,8 @@ import ( // Parse parses the input commandline string (cmd, flags, and args). // returns the corresponding command Request object. +// Multiple root commands are supported: +// Parse will search each root to find the one that best matches the requested subcommand. func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, *cmds.Command, []string, error) { var root, cmd *cmds.Command var path, stringArgs []string From 5481230679b9ad0cb1571259dcf51415aaec1b61 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 16:43:14 -0800 Subject: [PATCH 235/383] commands/cli: Use better temp variable names in Parse --- commands/cli/parse.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index 3a45e3f81..df5a0344d 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -20,21 +20,21 @@ func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, // use the root that matches the longest path (most accurately matches request) maxLength := 0 - for _, r := range roots { - p, i, c := parsePath(input, r) - o, s, err := parseOptions(i) + for _, root2 := range roots { + path2, input2, cmd2 := parsePath(input, root2) + opts2, stringArgs2, err := parseOptions(input2) if err != nil { - return nil, root, c, p, err + return nil, root, cmd2, path2, err } - length := len(p) + length := len(path2) if length > maxLength { maxLength = length - root = r - path = p - cmd = c - opts = o - stringArgs = s + root = root2 + path = path2 + cmd = cmd2 + opts = opts2 + stringArgs = stringArgs2 } } From e14471f5e83928876e9845d6aeb411710f32e249 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 16:46:43 -0800 Subject: [PATCH 236/383] commands/cli: Use better variable name for parseArgs value index --- commands/cli/parse.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index df5a0344d..39f801751 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -134,40 +134,40 @@ func parseArgs(stringArgs []string, cmd *cmds.Command) ([]interface{}, error) { } } - j := 0 + valueIndex := 0 // the index of the current stringArgs value for _, argDef := range cmd.Arguments { // skip optional argument definitions if there aren't sufficient remaining values - if len(stringArgs)-j <= lenRequired && !argDef.Required { + if len(stringArgs)-valueIndex <= lenRequired && !argDef.Required { continue } else if argDef.Required { lenRequired-- } - if j >= len(stringArgs) { + if valueIndex >= len(stringArgs) { break } if argDef.Variadic { - for _, arg := range stringArgs[j:] { + for _, arg := range stringArgs[valueIndex:] { var err error args, err = appendArg(args, argDef, arg) if err != nil { return nil, err } - j++ + valueIndex++ } } else { var err error - args, err = appendArg(args, argDef, stringArgs[j]) + args, err = appendArg(args, argDef, stringArgs[valueIndex]) if err != nil { return nil, err } - j++ + valueIndex++ } } - if len(stringArgs)-j > 0 { - args = append(args, make([]interface{}, len(stringArgs)-j)) + if len(stringArgs)-valueIndex > 0 { + args = append(args, make([]interface{}, len(stringArgs)-valueIndex)) } return args, nil From eedc2e9cc742cf6099b8c3d441733464102ab56f Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 16:48:15 -0800 Subject: [PATCH 237/383] commands: s/lenRequired/numRequired/ --- commands/command.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/commands/command.go b/commands/command.go index fc8092d7b..b203234a9 100644 --- a/commands/command.go +++ b/commands/command.go @@ -159,10 +159,10 @@ func (c *Command) CheckArguments(req Request) error { } // count required argument definitions - lenRequired := 0 + numRequired := 0 for _, argDef := range c.Arguments { if argDef.Required { - lenRequired++ + numRequired++ } } @@ -170,7 +170,7 @@ func (c *Command) CheckArguments(req Request) error { j := 0 for _, argDef := range c.Arguments { // skip optional argument definitions if there aren't sufficient remaining values - if len(args)-j <= lenRequired && !argDef.Required { + if len(args)-j <= numRequired && !argDef.Required { continue } From 50751617b68086fc5602ae6d0af78727d9d80083 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 16:49:26 -0800 Subject: [PATCH 238/383] commands: s/j/valueIndex/ --- commands/command.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/commands/command.go b/commands/command.go index b203234a9..563b36024 100644 --- a/commands/command.go +++ b/commands/command.go @@ -167,18 +167,18 @@ func (c *Command) CheckArguments(req Request) error { } // iterate over the arg definitions - j := 0 + valueIndex := 0 // the index of the current value (in `args`) for _, argDef := range c.Arguments { // skip optional argument definitions if there aren't sufficient remaining values - if len(args)-j <= numRequired && !argDef.Required { + if len(args)-valueIndex <= numRequired && !argDef.Required { continue } // the value for this argument definition. can be nil if it wasn't provided by the caller var v interface{} - if j < len(args) { - v = args[j] - j++ + if valueIndex < len(args) { + v = args[valueIndex] + valueIndex++ } err := checkArgValue(v, argDef) @@ -187,8 +187,8 @@ func (c *Command) CheckArguments(req Request) error { } // any additional values are for the variadic arg definition - if argDef.Variadic && j < len(args)-1 { - for _, val := range args[j:] { + if argDef.Variadic && valueIndex < len(args)-1 { + for _, val := range args[valueIndex:] { err := checkArgValue(val, argDef) if err != nil { return err From f93b806d21bc204288b8d3c715b6374d3de9af75 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 17:43:19 -0800 Subject: [PATCH 239/383] commands/http: s/lenRequired/numRequired/ --- commands/http/parse.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/commands/http/parse.go b/commands/http/parse.go index 7c1a0e1ff..6c897ee5e 100644 --- a/commands/http/parse.go +++ b/commands/http/parse.go @@ -42,10 +42,10 @@ func Parse(r *http.Request, root *cmds.Command) (cmds.Request, error) { args := make([]interface{}, 0) // count required argument definitions - lenRequired := 0 + numRequired := 0 for _, argDef := range cmd.Arguments { if argDef.Required { - lenRequired++ + numRequired++ } } @@ -58,10 +58,10 @@ func Parse(r *http.Request, root *cmds.Command) (cmds.Request, error) { for _, argDef := range cmd.Arguments { // skip optional argument definitions if there aren't sufficient remaining values - if valCount <= lenRequired && !argDef.Required { + if valCount <= numRequired && !argDef.Required { continue } else if argDef.Required { - lenRequired-- + numRequired-- } if argDef.Type == cmds.ArgString { From 6869ca44fa844f9058d68cd389f9674c194b4ba1 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 18:13:14 -0800 Subject: [PATCH 240/383] core/command2: Fixed indentation --- core/commands2/add.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/commands2/add.go b/core/commands2/add.go index 4913b0d56..f6ec984d4 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -30,9 +30,9 @@ var addCmd = &cmds.Command{ }, Description: "Add an object to ipfs.", Help: `Adds contents of to ipfs. Use -r to add directories. - Note that directories are added recursively, to form the ipfs - MerkleDAG. A smarter partial add with a staging area (like git) - remains to be implemented. +Note that directories are added recursively, to form the ipfs +MerkleDAG. A smarter partial add with a staging area (like git) +remains to be implemented. `, Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node From 7666f8880c7e3e43b6a93f32a6e481e928ffd80b Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 18:44:28 -0800 Subject: [PATCH 241/383] commands: Allow overriding helptext sections with hand-written strings --- commands/cli/helptext.go | 30 ++++++++++++++++++++++++------ commands/command.go | 9 +++++++-- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index 3a82d58ad..2248e2699 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -36,18 +36,36 @@ func HelpText(rootName string, root *cmds.Command, path []string) (string, error } if cmd.Arguments != nil { - lines := indent(argumentText(cmd), " ") - s += fmt.Sprintf("Arguments:\n%v\n\n", strings.Join(lines, "\n")) + if len(cmd.ArgumentHelp) > 0 { + s += cmd.ArgumentHelp + } else { + section := strings.Join(indent(argumentText(cmd), " "), "\n") + s += fmt.Sprintf("Arguments:\n%v", section) + } + + s += "\n\n" } if cmd.Subcommands != nil { - lines := indent(subcommandText(cmd, rootName, path), " ") - s += fmt.Sprintf("Subcommands:\n%v\n\n", strings.Join(lines, "\n")) + if len(cmd.SubcommandHelp) > 0 { + s += cmd.SubcommandHelp + } else { + section := strings.Join(indent(subcommandText(cmd, rootName, path), " "), "\n") + s += fmt.Sprintf("Subcommands:\n%v", section) + } + + s += "\n\n" } if cmd.Options != nil { - lines := indent(optionText(cmd), " ") - s += fmt.Sprintf("Options:\n%v\n\n", strings.Join(lines, "\n")) + if len(cmd.OptionHelp) > 0 { + s += cmd.OptionHelp + } else { + section := strings.Join(indent(optionText(cmd), " "), "\n") + s += fmt.Sprintf("Options:\n%v", section) + } + + s += "\n\n" } return s, nil diff --git a/commands/command.go b/commands/command.go index 563b36024..6216c7fea 100644 --- a/commands/command.go +++ b/commands/command.go @@ -28,8 +28,13 @@ type Marshaller func(Response) ([]byte, error) // Command is a runnable command, with input arguments and options (flags). // It can also have Subcommands, to group units of work into sets. type Command struct { - Description string - Help string + // MAYBE_TODO: move all the text fields into a struct + // MAYBE_TODO: move these out of command and put them somewhere in commands/cli + Description string + Help string + SubcommandHelp string + OptionHelp string + ArgumentHelp string Options []Option Arguments []Argument From e23b537f3270f33118759826c8eb27ab3a6de0a0 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 19:09:25 -0800 Subject: [PATCH 242/383] Override root command help text --- cmd/ipfs2/main.go | 5 +---- core/commands2/root.go | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index b6a15be3b..4d6421416 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -102,11 +102,8 @@ func createRequest(args []string) (cmds.Request, *cmds.Command, error) { } } - // when generating help for the root command, we don't want the autogenerated subcommand text - // (since we have better hand-made subcommand list in the root Help field) if cmd == nil { - root = &*commands.Root - root.Subcommands = nil + root = commands.Root } // generate the help text for the command the user was trying to call (or root) diff --git a/core/commands2/root.go b/core/commands2/root.go index 187592ceb..5d22c5fab 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -14,7 +14,7 @@ type TestOutput struct { var Root = &cmds.Command{ Description: "Global P2P Merkle-DAG filesystem", - Help: `Basic commands: + SubcommandHelp: `Basic commands: init Initialize ipfs local configurationx add Add an object to ipfs From 981f18bb4902922795630e6710a34a771e14531d Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 20:52:17 -0800 Subject: [PATCH 243/383] change(Makefile) make install -> make install_1, make install_2 --- Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 7c7cb176c..69bb553c3 100644 --- a/Makefile +++ b/Makefile @@ -10,9 +10,12 @@ godep: vendor: godep godep save -r ./... -# TODO remove ipfs2 once new command refactoring is complete -install: +# TODO revert to `install` once new command refactoring is complete +install_1: cd cmd/ipfs && go install + +# TODO remove once new command refactoring is complete +install_2: cd cmd/ipfs2 && go install test: test_go test_sharness From f5973b5565cd05d385366f1b7e83a12e77955d3e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 21:19:28 -0800 Subject: [PATCH 244/383] add urgent todo. fix before merge --- commands/cli/parse.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index 39f801751..884cfe51f 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -178,7 +178,7 @@ func appendArg(args []interface{}, argDef cmds.Argument, value string) ([]interf return append(args, value), nil } else { - in, err := os.Open(value) + in, err := os.Open(value) // FIXME(btc) must close file. fix before merge if err != nil { return nil, err } From e6f2de40dc521dce1ec66929370456aefae64795 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 21:33:19 -0800 Subject: [PATCH 245/383] docs(commands) Type --- commands/command.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/commands/command.go b/commands/command.go index 6216c7fea..479c232e7 100644 --- a/commands/command.go +++ b/commands/command.go @@ -40,6 +40,12 @@ type Command struct { Arguments []Argument Run Function Marshallers map[EncodingType]Marshaller + + // Type describes the type of the output of the Command's Run Function. + // Precisely, the value of Type is an instance of the return type of the + // Run Function. + // + // ie. If command Run returns &Block{}, then Command.Type == &Block{} Type interface{} Subcommands map[string]*Command } From ea15bd6ffe92039cd7cb784093e1928aa12f9425 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 21:34:54 -0800 Subject: [PATCH 246/383] docs(commands) amend --- commands/command.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commands/command.go b/commands/command.go index 479c232e7..60262b26b 100644 --- a/commands/command.go +++ b/commands/command.go @@ -42,8 +42,8 @@ type Command struct { Marshallers map[EncodingType]Marshaller // Type describes the type of the output of the Command's Run Function. - // Precisely, the value of Type is an instance of the return type of the - // Run Function. + // In precise terms, the value of Type is an instance of the return type of + // the Run Function. // // ie. If command Run returns &Block{}, then Command.Type == &Block{} Type interface{} From 4970d8b5a3f61f4055f3bca0065487e053212a0e Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 21:57:27 -0800 Subject: [PATCH 247/383] commands/cli: Use template for helptext generation --- cmd/ipfs2/main.go | 14 ++--- commands/cli/helptext.go | 128 ++++++++++++++++++++++++++------------- 2 files changed, 91 insertions(+), 51 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 4d6421416..31ba731ce 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -107,11 +107,9 @@ func createRequest(args []string) (cmds.Request, *cmds.Command, error) { } // generate the help text for the command the user was trying to call (or root) - helpText, htErr := cmdsCli.HelpText("ipfs", root, path) + htErr := cmdsCli.LongHelp("ipfs", root, path, os.Stdout) if htErr != nil { fmt.Println(htErr) - } else { - fmt.Println(helpText) } return nil, nil, err } @@ -150,12 +148,10 @@ func handleHelpOption(req cmds.Request, root *cmds.Command) (helpTextDisplayed b if !help { return false, nil } - helpText, err := cmdsCli.HelpText("ipfs", root, req.Path()) + err = cmdsCli.LongHelp("ipfs", root, req.Path(), os.Stdout) if err != nil { return false, err } - fmt.Println(helpText) - return true, nil } @@ -214,11 +210,9 @@ func outputResponse(res cmds.Response, root *cmds.Command) error { // if this is a client error, we try to display help text if res.Error().Code == cmds.ErrClient { - helpText, err := cmdsCli.HelpText("ipfs", root, res.Request().Path()) + err := cmdsCli.LongHelp("ipfs", root, res.Request().Path(), os.Stdout) if err != nil { - fmt.Println(err.Error()) - } else { - fmt.Println(helpText) + fmt.Println(err) } } diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index 2248e2699..8ecc37c81 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -2,7 +2,9 @@ package cli import ( "fmt" + "io" "strings" + "text/template" cmds "github.com/jbenet/go-ipfs/commands" ) @@ -15,60 +17,104 @@ const ( optionType = "(%v)" whitespace = "\r\n\t " + + indentStr = " " ) -// HelpText returns a formatted CLI helptext string, generated for the given command -func HelpText(rootName string, root *cmds.Command, path []string) (string, error) { +type helpFields struct { + Indent string + Path string + ArgUsage string + Tagline string + Arguments string + Options string + Subcommands string + Description string +} + +const usageFormat = "{{.Path}}{{if .ArgUsage}} {{.ArgUsage}}{{end}} - {{.Tagline}}" + +const longHelpFormat = ` +{{.Indent}}{{template "usage" .}} + +{{if .Arguments}}ARGUMENTS: + +{{.Indent}}{{.Arguments}} + +{{end}}{{if .Options}}OPTIONS: + +{{.Indent}}{{.Options}} + +{{end}}{{if .Subcommands}}SUBCOMMANDS: + +{{.Indent}}{{.Subcommands}} + +{{.Indent}}Use '{{.Path}} --help' for more information about each command. + +{{end}}{{if .Description}}DESCRIPTION: + +{{.Indent}}{{.Description}} + +{{end}} +` + +var longHelpTemplate *template.Template +var usageTemplate *template.Template + +func init() { + tmpl, err := template.New("usage").Parse(usageFormat) + if err != nil { + panic(err) + } + usageTemplate = tmpl + + tmpl, err = usageTemplate.New("longHelp").Parse(longHelpFormat) + if err != nil { + panic(err) + } + longHelpTemplate = tmpl +} + +// LongHelp returns a formatted CLI helptext string, generated for the given command +func LongHelp(rootName string, root *cmds.Command, path []string, out io.Writer) error { cmd, err := root.Get(path) if err != nil { - return "", err + return err } - s := "" - usage := usageText(cmd) - if len(usage) > 0 { - usage += " " - } - s += fmt.Sprintf("%v %v %v- %v\n\n", rootName, strings.Join(path, " "), usage, cmd.Description) - - if len(cmd.Help) > 0 { - s += fmt.Sprintf("%v\n\n", strings.Trim(cmd.Help, whitespace)) + pathStr := rootName + if len(path) > 0 { + pathStr += " " + strings.Join(path, " ") } - if cmd.Arguments != nil { - if len(cmd.ArgumentHelp) > 0 { - s += cmd.ArgumentHelp - } else { - section := strings.Join(indent(argumentText(cmd), " "), "\n") - s += fmt.Sprintf("Arguments:\n%v", section) - } - - s += "\n\n" + fields := helpFields{ + Indent: indentStr, + Path: pathStr, + ArgUsage: usageText(cmd), + Tagline: cmd.Description, + Arguments: cmd.ArgumentHelp, + Options: cmd.OptionHelp, + Subcommands: cmd.SubcommandHelp, + Description: cmd.Help, } - if cmd.Subcommands != nil { - if len(cmd.SubcommandHelp) > 0 { - s += cmd.SubcommandHelp - } else { - section := strings.Join(indent(subcommandText(cmd, rootName, path), " "), "\n") - s += fmt.Sprintf("Subcommands:\n%v", section) - } - - s += "\n\n" + // autogen fields that are empty + if len(cmd.ArgumentHelp) == 0 { + fields.Arguments = strings.Join(argumentText(cmd), "\n") + } + if len(cmd.OptionHelp) == 0 { + fields.Options = strings.Join(optionText(cmd), "\n") + } + if len(cmd.SubcommandHelp) == 0 { + fields.Subcommands = strings.Join(subcommandText(cmd, rootName, path), "\n") } - if cmd.Options != nil { - if len(cmd.OptionHelp) > 0 { - s += cmd.OptionHelp - } else { - section := strings.Join(indent(optionText(cmd), " "), "\n") - s += fmt.Sprintf("Options:\n%v", section) - } + fields.Arguments = indentString(fields.Arguments, indentStr) + fields.Options = indentString(fields.Options, indentStr) + fields.Subcommands = indentString(fields.Subcommands, indentStr) + fields.Description = indentString(fields.Description, indentStr) - s += "\n\n" - } - - return s, nil + return longHelpTemplate.Execute(out, fields) } func argumentText(cmd *cmds.Command) []string { From 93c69a83e839cecc0c906d6c56562e15bc1e3545 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 22:13:48 -0800 Subject: [PATCH 248/383] commands/cli: Added short help text function --- commands/cli/helptext.go | 41 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index 8ecc37c81..240fb982c 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -57,9 +57,17 @@ const longHelpFormat = ` {{end}} ` +const shortHelpFormat = `USAGE: +{{.Indent}}{{template "usage" .}} +{{if .Description}} +{{.Indent}}{{.Description}} +{{end}} +Use '{{.Path}} --help' for more information about this command. +` -var longHelpTemplate *template.Template var usageTemplate *template.Template +var longHelpTemplate *template.Template +var shortHelpTemplate *template.Template func init() { tmpl, err := template.New("usage").Parse(usageFormat) @@ -73,6 +81,12 @@ func init() { panic(err) } longHelpTemplate = tmpl + + tmpl, err = usageTemplate.New("shortHelp").Parse(shortHelpFormat) + if err != nil { + panic(err) + } + shortHelpTemplate = tmpl } // LongHelp returns a formatted CLI helptext string, generated for the given command @@ -117,6 +131,31 @@ func LongHelp(rootName string, root *cmds.Command, path []string, out io.Writer) return longHelpTemplate.Execute(out, fields) } +// ShortHelp returns a formatted CLI helptext string, generated for the given command +func ShortHelp(rootName string, root *cmds.Command, path []string, out io.Writer) error { + cmd, err := root.Get(path) + if err != nil { + return err + } + + pathStr := rootName + if len(path) > 0 { + pathStr += " " + strings.Join(path, " ") + } + + fields := helpFields{ + Indent: indentStr, + Path: pathStr, + ArgUsage: usageText(cmd), + Tagline: cmd.Description, + Description: cmd.Help, + } + + fields.Description = indentString(fields.Description, indentStr) + + return shortHelpTemplate.Execute(out, fields) +} + func argumentText(cmd *cmds.Command) []string { lines := make([]string, len(cmd.Arguments)) From c76a52e42226448f7d03098b487615e5b95bfd12 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 22:14:38 -0800 Subject: [PATCH 249/383] cmd/ipfs2: Show short help text instead for usage errors --- cmd/ipfs2/main.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 31ba731ce..cad7368c8 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -92,11 +92,12 @@ func createRequest(args []string) (cmds.Request, *cmds.Command, error) { // handle parse error (which means the commandline input was wrong, // e.g. incorrect number of args, or nonexistent subcommand) if err != nil { + help, _ := req.Option("help").Bool() + // if the -help flag wasn't specified, show the error message // or if a path was returned (user specified a valid subcommand), show the error message // (this means there was an option or argument error) if path != nil && len(path) > 0 { - help, _ := req.Option("help").Bool() if !help { fmt.Printf(errorFormat, err) } @@ -106,8 +107,13 @@ func createRequest(args []string) (cmds.Request, *cmds.Command, error) { root = commands.Root } - // generate the help text for the command the user was trying to call (or root) - htErr := cmdsCli.LongHelp("ipfs", root, path, os.Stdout) + // show the long help text if the help flag was specified, otherwise short help text + helpFunc := cmdsCli.ShortHelp + if help { + helpFunc = cmdsCli.LongHelp + } + + htErr := helpFunc("ipfs", root, path, os.Stdout) if htErr != nil { fmt.Println(htErr) } @@ -210,7 +216,7 @@ func outputResponse(res cmds.Response, root *cmds.Command) error { // if this is a client error, we try to display help text if res.Error().Code == cmds.ErrClient { - err := cmdsCli.LongHelp("ipfs", root, res.Request().Path(), os.Stdout) + err := cmdsCli.ShortHelp("ipfs", root, res.Request().Path(), os.Stdout) if err != nil { fmt.Println(err) } From 7a505b44c79ddbeab006d7ca72ff995a6bda6218 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 22:24:04 -0800 Subject: [PATCH 250/383] Handle -h and --help differently (short text vs long text) --- cmd/ipfs2/main.go | 29 ++++++++++++++++++++++------- core/commands2/root.go | 6 ++---- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index cad7368c8..9d000ce48 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -92,13 +92,18 @@ func createRequest(args []string) (cmds.Request, *cmds.Command, error) { // handle parse error (which means the commandline input was wrong, // e.g. incorrect number of args, or nonexistent subcommand) if err != nil { - help, _ := req.Option("help").Bool() + var longHelp, shortHelp bool + + if req != nil { + longHelp, _ = req.Option("help").Bool() + shortHelp, _ = req.Option("h").Bool() + } // if the -help flag wasn't specified, show the error message // or if a path was returned (user specified a valid subcommand), show the error message // (this means there was an option or argument error) if path != nil && len(path) > 0 { - if !help { + if !longHelp && !shortHelp { fmt.Printf(errorFormat, err) } } @@ -107,9 +112,10 @@ func createRequest(args []string) (cmds.Request, *cmds.Command, error) { root = commands.Root } - // show the long help text if the help flag was specified, otherwise short help text + // show the long help text if the -help flag was specified or we are at the root command + // otherwise, show short help text helpFunc := cmdsCli.ShortHelp - if help { + if longHelp || len(path) == 0 { helpFunc = cmdsCli.LongHelp } @@ -147,14 +153,23 @@ func createRequest(args []string) (cmds.Request, *cmds.Command, error) { } func handleHelpOption(req cmds.Request, root *cmds.Command) (helpTextDisplayed bool, err error) { - help, err := req.Option("help").Bool() + longHelp, err := req.Option("help").Bool() if err != nil { return false, err } - if !help { + shortHelp, err := req.Option("h").Bool() + if err != nil { + return false, err + } + if !longHelp && !shortHelp { return false, nil } - err = cmdsCli.LongHelp("ipfs", root, req.Path(), os.Stdout) + helpFunc := cmdsCli.ShortHelp + if longHelp || len(req.Path()) == 0 { + helpFunc = cmdsCli.LongHelp + } + + err = helpFunc("ipfs", root, req.Path(), os.Stdout) if err != nil { return false, err } diff --git a/core/commands2/root.go b/core/commands2/root.go index 5d22c5fab..9356ac760 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -38,15 +38,13 @@ Plumbing commands: block Interact with raw blocks in the datastore object Interact with raw dag nodes - - -Use "ipfs --help" for more information about a command. `, Options: []cmds.Option{ cmds.StringOption("config", "c", "Path to the configuration file to use"), cmds.BoolOption("debug", "D", "Operate in debug mode"), - cmds.BoolOption("help", "h", "Show the command help text"), + cmds.BoolOption("help", "Show the full command help text"), + cmds.BoolOption("h", "Show a short version of the command help text"), cmds.BoolOption("local", "L", "Run the command locally, instead of using the daemon"), }, } From 45526224e40a9f1daf8ddd569f88bbd1bdfb57e2 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 21:45:09 -0800 Subject: [PATCH 251/383] todo @mappum, it's okay to make it a read TODO. in fact, it's a really nice practice since it's standard and we can grep it. When someone who has an answer for the concern comes across the standard TODO breadcrumb, he/she can address it. Using the conventional TODO improves discoverability. --- core/commands2/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/commands2/config.go b/core/commands2/config.go index 59dcd77e1..3eee94f06 100644 --- a/core/commands2/config.go +++ b/core/commands2/config.go @@ -140,7 +140,7 @@ func setConfig(filename string, key, value string) (*ConfigField, error) { } func showConfig(filename string) (io.Reader, error) { - // MAYBE_TODO: maybe we should omit privkey so we don't accidentally leak it? + // TODO maybe we should omit privkey so we don't accidentally leak it? file, err := os.Open(filename) if err != nil { From fcabf2226625a30a8c99529b80ae7df965096e82 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 22:08:38 -0800 Subject: [PATCH 252/383] feat(ipfs2/config) impl show and edit as options verified that option text hints match ipfs1 - btc --- core/commands2/config.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/core/commands2/config.go b/core/commands2/config.go index 3eee94f06..dd92c2729 100644 --- a/core/commands2/config.go +++ b/core/commands2/config.go @@ -1,10 +1,12 @@ package commands import ( + "bytes" "encoding/json" "errors" "fmt" "io" + "io/ioutil" "os" "os/exec" @@ -34,6 +36,10 @@ var configCmd = &cmds.Command{ cmds.StringArg("key", true, false, "The key of the config entry (e.g. \"Addresses.API\")"), cmds.StringArg("value", false, false, "The value to set the config entry to"), }, + Options: []cmds.Option{ + cmds.StringOption("show", "s", "Show config file"), + cmds.StringOption("edit", "e", "Edit config file in $EDITOR"), + }, Run: func(req cmds.Request) (interface{}, error) { args := req.Arguments() @@ -142,13 +148,12 @@ func setConfig(filename string, key, value string) (*ConfigField, error) { func showConfig(filename string) (io.Reader, error) { // TODO maybe we should omit privkey so we don't accidentally leak it? - file, err := os.Open(filename) + data, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - //defer file.Close() - return file, nil + return bytes.NewReader(data), nil } func editConfig(filename string) error { From 94e8218be8b354d8b98abad82f6c99ed8783d1c0 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 22:23:14 -0800 Subject: [PATCH 253/383] add todo --- commands/option.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/commands/option.go b/commands/option.go index f8b3c3b3c..0cc4713ef 100644 --- a/commands/option.go +++ b/commands/option.go @@ -29,6 +29,7 @@ type Option struct { // constructor helper functions func NewOption(kind reflect.Kind, names ...string) Option { if len(names) < 2 { + // FIXME(btc) don't panic (fix_before_merge) panic("Options require at least two string values (name and description)") } @@ -42,6 +43,12 @@ func NewOption(kind reflect.Kind, names ...string) Option { } } +// TODO handle description separately. this will take care of the panic case in +// NewOption + +// For all func {Type}Option(...string) functions, the last variadic argument +// is treated as the description field. + func BoolOption(names ...string) Option { return NewOption(Bool, names...) } From e32ed36ee96c1e7c8cafa059603a4f4d23d01b71 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 11 Nov 2014 21:43:38 -0800 Subject: [PATCH 254/383] cmds2/add: return correct error --- core/commands2/add.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/commands2/add.go b/core/commands2/add.go index f6ec984d4..148bb03c9 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -44,7 +44,7 @@ remains to be implemented. dagnodes, err := add(n, readers) if err != nil { - return nil, errors.New("cast error") + return nil, err } // TODO: include fs paths in output (will need a way to specify paths in underlying filearg system) From 6faeee830098166c371640669ff8877d73fbe951 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 11 Nov 2014 22:43:34 -0800 Subject: [PATCH 255/383] cmds2/add: temp fix for -r. horrible hack This will be removed soon, just gets us past landing cmds2. --- core/commands2/add.go | 118 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 105 insertions(+), 13 deletions(-) diff --git a/core/commands2/add.go b/core/commands2/add.go index 148bb03c9..862f3962d 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -4,6 +4,9 @@ import ( "errors" "fmt" "io" + "io/ioutil" + "os" + "path/filepath" cmds "github.com/jbenet/go-ipfs/commands" core "github.com/jbenet/go-ipfs/core" @@ -12,6 +15,7 @@ import ( "github.com/jbenet/go-ipfs/importer/chunk" dag "github.com/jbenet/go-ipfs/merkledag" pinning "github.com/jbenet/go-ipfs/pin" + ft "github.com/jbenet/go-ipfs/unixfs" ) // Error indicating the max depth has been exceded. @@ -26,7 +30,7 @@ var addCmd = &cmds.Command{ cmds.BoolOption("recursive", "r", "Must be specified when adding directories"), }, Arguments: []cmds.Argument{ - cmds.FileArg("file", true, true, "The path to a file to be added to IPFS"), + cmds.StringArg("file", true, true, "The path to a file to be added to IPFS"), }, Description: "Add an object to ipfs.", Help: `Adds contents of to ipfs. Use -r to add directories. @@ -37,27 +41,115 @@ remains to be implemented. Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node - readers, err := internal.CastToReaders(req.Arguments()) - if err != nil { - return nil, err + // THIS IS A HORRIBLE HACK -- FIXME!!! + // see https://github.com/jbenet/go-ipfs/issues/309 + var added []*Object + + // returns the last one + addDagnodes := func(dns []*dag.Node) error { + for _, dn := range dns { + o, err := getOutput(dn) + if err != nil { + return err + } + + added = append(added, o) + } + return nil } - dagnodes, err := add(n, readers) - if err != nil { - return nil, err - } + addFile := func(name string) (*dag.Node, error) { + f, err := os.Open(name) + if err != nil { + return nil, err + } + defer f.Close() - // TODO: include fs paths in output (will need a way to specify paths in underlying filearg system) - added := make([]*Object, 0, len(req.Arguments())) - for _, dagnode := range dagnodes { - object, err := getOutput(dagnode) + dns, err := add(n, []io.Reader{f}) if err != nil { return nil, err } - added = append(added, object) + if err := addDagnodes(dns); err != nil { + return nil, err + } + return dns[len(dns)-1], nil // last dag node is the file. } + var addPath func(name string) (*dag.Node, error) + addDir := func(name string) (*dag.Node, error) { + tree := &dag.Node{Data: ft.FolderPBData()} + + entries, err := ioutil.ReadDir(name) + if err != nil { + return nil, err + } + + // construct nodes for containing files. + for _, e := range entries { + fp := filepath.Join(name, e.Name()) + nd, err := addPath(fp) + if err != nil { + return nil, err + } + + if err = tree.AddNodeLink(e.Name(), nd); err != nil { + return nil, err + } + } + + log.Infof("adding dir: %s", name) + + if err := addDagnodes([]*dag.Node{tree}); err != nil { + return nil, err + } + return tree, nil + } + + addPath = func(fpath string) (*dag.Node, error) { + fi, err := os.Stat(fpath) + if err != nil { + return nil, err + } + + if fi.IsDir() { + return addDir(fpath) + } + return addFile(fpath) + } + + paths, err := internal.CastToStrings(req.Arguments()) + if err != nil { + return nil, err + } + + for _, f := range paths { + if _, err := addPath(f); err != nil { + return nil, err + } + } + + // readers, err := internal.CastToReaders(req.Arguments()) + // if err != nil { + // return nil, err + // } + // + // dagnodes, err := add(n, readers) + // if err != nil { + // return nil, err + // } + // + // // TODO: include fs paths in output (will need a way to specify paths in underlying filearg system) + // added := make([]*Object, 0, len(req.Arguments())) + // for _, dagnode := range dagnodes { + // object, err := getOutput(dagnode) + // if err != nil { + // return nil, err + // } + // + // added = append(added, object) + // } + return &AddOutput{added}, nil }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ From 8c9ee52a934b6fc26f47e3cb01f69a3431b94ca7 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Tue, 11 Nov 2014 23:14:01 -0800 Subject: [PATCH 256/383] commands: Fixed value mutation bug in Request#SetOption --- commands/request.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/request.go b/commands/request.go index d2b018219..ef29145b4 100644 --- a/commands/request.go +++ b/commands/request.go @@ -92,7 +92,7 @@ func (r *request) SetOption(name string, val interface{}) { // try all the possible names, if we already have a value then set over it for _, n := range option.Names { - val, found := r.options[n] + _, found := r.options[n] if found { r.options[n] = val return From 81dbb236025af553f61488542349158b70466f6f Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 00:05:57 -0800 Subject: [PATCH 257/383] commands: Cleanup Requests after command execution returns --- commands/command.go | 8 ++++++++ commands/request.go | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/commands/command.go b/commands/command.go index 60262b26b..2e8af5421 100644 --- a/commands/command.go +++ b/commands/command.go @@ -97,6 +97,14 @@ func (c *Command) Call(req Request) Response { return res } + // clean up the request (close the readers, e.g. fileargs) + // NOTE: this means commands can't expect to keep reading after cmd.Run returns (in a goroutine) + err = req.Cleanup() + if err != nil { + res.SetError(err, ErrNormal) + return res + } + res.SetOutput(output) return res } diff --git a/commands/request.go b/commands/request.go index ef29145b4..01619cb35 100644 --- a/commands/request.go +++ b/commands/request.go @@ -3,6 +3,7 @@ package commands import ( "errors" "fmt" + "io" "reflect" "strconv" @@ -28,6 +29,7 @@ type Request interface { Context() *Context SetContext(Context) Command() *Command + Cleanup() error ConvertOptions() error } @@ -119,6 +121,20 @@ func (r *request) Command() *Command { return r.cmd } +func (r *request) Cleanup() error { + for _, arg := range r.arguments { + closer, ok := arg.(io.Closer) + if ok { + err := closer.Close() + if err != nil { + return err + } + } + } + + return nil +} + type converter func(string) (interface{}, error) var converters = map[reflect.Kind]converter{ From 0e41e1930579d21782d7d9f79ab07a9f909e58ae Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 11 Nov 2014 22:50:17 -0800 Subject: [PATCH 258/383] add logging file --- core/commands2/add.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/commands2/add.go b/core/commands2/add.go index 862f3962d..465b9d71d 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -70,6 +70,7 @@ remains to be implemented. return nil, err } + log.Infof("adding file: %s", name) if err := addDagnodes(dns); err != nil { return nil, err } @@ -99,7 +100,6 @@ remains to be implemented. } log.Infof("adding dir: %s", name) - if err := addDagnodes([]*dag.Node{tree}); err != nil { return nil, err } From f6c1cefe6082920c70ffb7b2d7d9835841f1cd04 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 11 Nov 2014 23:16:45 -0800 Subject: [PATCH 259/383] cmds/add fixed add output --- core/commands2/add.go | 54 ++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/core/commands2/add.go b/core/commands2/add.go index 465b9d71d..89a784830 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -1,6 +1,7 @@ package commands import ( + "bytes" "errors" "fmt" "io" @@ -22,7 +23,8 @@ import ( var ErrDepthLimitExceeded = fmt.Errorf("depth limit exceeded") type AddOutput struct { - Added []*Object + Objects []*Object + Names []string } var addCmd = &cmds.Command{ @@ -39,22 +41,26 @@ MerkleDAG. A smarter partial add with a staging area (like git) remains to be implemented. `, Run: func(req cmds.Request) (interface{}, error) { + var added AddOutput n := req.Context().Node + recursive, err := req.Option("r").Bool() + if err != nil { + return nil, err + } + // THIS IS A HORRIBLE HACK -- FIXME!!! // see https://github.com/jbenet/go-ipfs/issues/309 - var added []*Object // returns the last one - addDagnodes := func(dns []*dag.Node) error { - for _, dn := range dns { - o, err := getOutput(dn) - if err != nil { - return err - } - - added = append(added, o) + addDagnode := func(name string, dn *dag.Node) error { + o, err := getOutput(dn) + if err != nil { + return err } + + added.Objects = append(added.Objects, o) + added.Names = append(added.Names, name) return nil } @@ -71,7 +77,7 @@ remains to be implemented. } log.Infof("adding file: %s", name) - if err := addDagnodes(dns); err != nil { + if err := addDagnode(name, dns[len(dns)-1]); err != nil { return nil, err } return dns[len(dns)-1], nil // last dag node is the file. @@ -100,7 +106,7 @@ remains to be implemented. } log.Infof("adding dir: %s", name) - if err := addDagnodes([]*dag.Node{tree}); err != nil { + if err := addDagnode(name, tree); err != nil { return nil, err } return tree, nil @@ -113,6 +119,9 @@ remains to be implemented. } if fi.IsDir() { + if !recursive { + return nil, errors.New("use -r to recursively add directories") + } return addDir(fpath) } return addFile(fpath) @@ -120,6 +129,7 @@ remains to be implemented. paths, err := internal.CastToStrings(req.Arguments()) if err != nil { + panic(err) return nil, err } @@ -128,6 +138,7 @@ remains to be implemented. return nil, err } } + return added, nil // readers, err := internal.CastToReaders(req.Arguments()) // if err != nil { @@ -149,26 +160,21 @@ remains to be implemented. // // added = append(added, object) // } - - return &AddOutput{added}, nil + // + // return &AddOutput{added}, nil }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ cmds.Text: func(res cmds.Response) ([]byte, error) { val, ok := res.Output().(*AddOutput) if !ok { - return nil, errors.New("cast err") - } - added := val.Added - if len(added) == 1 { - s := fmt.Sprintf("Added object: %s\n", added[0].Hash) - return []byte(s), nil + return nil, errors.New("cast error") } - s := fmt.Sprintf("Added %v objects:\n", len(added)) - for _, obj := range added { - s += fmt.Sprintf("- %s\n", obj.Hash) + var buf bytes.Buffer + for i, obj := range val.Objects { + buf.Write([]byte(fmt.Sprintf("added %s %s\n", obj.Hash, val.Names[i]))) } - return []byte(s), nil + return buf.Bytes(), nil }, }, Type: &AddOutput{}, From 49792b236218f12d987af6ce56827759f4bc8286 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Tue, 11 Nov 2014 23:53:48 -0800 Subject: [PATCH 260/383] refactored cast errors to use a util --- commands/http/client.go | 10 ++++------ commands/request.go | 4 ++-- core/commands2/add.go | 5 +++-- core/commands2/block.go | 5 ++--- core/commands2/bootstrap.go | 4 ++-- core/commands2/config.go | 5 +++-- core/commands2/internal/slice_util.go | 9 ++++----- core/commands2/object.go | 13 +++++++------ core/commands2/resolve.go | 3 ++- util/testutil/gen.go | 2 +- util/util.go | 9 +++++++++ 11 files changed, 39 insertions(+), 30 deletions(-) diff --git a/commands/http/client.go b/commands/http/client.go index 55d6f11a7..df8e0cfd2 100644 --- a/commands/http/client.go +++ b/commands/http/client.go @@ -3,7 +3,6 @@ package http import ( "bytes" "encoding/json" - "errors" "fmt" "io" "net/http" @@ -11,10 +10,9 @@ import ( "strings" cmds "github.com/jbenet/go-ipfs/commands" + u "github.com/jbenet/go-ipfs/util" ) -var castError = errors.New("cast error") - const ( ApiUrlFormat = "http://%s%s/%s?%s" ApiPath = "/api/v0" // TODO: make configurable @@ -70,7 +68,7 @@ func getQuery(req cmds.Request) (string, io.Reader, error) { for k, v := range req.Options() { str, ok := v.(string) if !ok { - return "", nil, castError + return "", nil, u.ErrCast() } query.Set(k, str) } @@ -87,7 +85,7 @@ func getQuery(req cmds.Request) (string, io.Reader, error) { if argDef.Type == cmds.ArgString { str, ok := arg.(string) if !ok { - return "", nil, castError + return "", nil, u.ErrCast() } query.Add("arg", str) @@ -99,7 +97,7 @@ func getQuery(req cmds.Request) (string, io.Reader, error) { var ok bool inputStream, ok = arg.(io.Reader) if !ok { - return "", nil, castError + return "", nil, u.ErrCast() } } } diff --git a/commands/request.go b/commands/request.go index 01619cb35..f18e694c3 100644 --- a/commands/request.go +++ b/commands/request.go @@ -1,7 +1,6 @@ package commands import ( - "errors" "fmt" "io" "reflect" @@ -9,6 +8,7 @@ import ( "github.com/jbenet/go-ipfs/config" "github.com/jbenet/go-ipfs/core" + u "github.com/jbenet/go-ipfs/util" ) type optMap map[string]interface{} @@ -176,7 +176,7 @@ func (r *request) ConvertOptions() error { convert := converters[opt.Type] str, ok := v.(string) if !ok { - return errors.New("cast error") + return u.ErrCast() } val, err := convert(str) if err != nil { diff --git a/core/commands2/add.go b/core/commands2/add.go index 89a784830..dc9566f35 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -17,6 +17,7 @@ import ( dag "github.com/jbenet/go-ipfs/merkledag" pinning "github.com/jbenet/go-ipfs/pin" ft "github.com/jbenet/go-ipfs/unixfs" + u "github.com/jbenet/go-ipfs/util" ) // Error indicating the max depth has been exceded. @@ -41,7 +42,7 @@ MerkleDAG. A smarter partial add with a staging area (like git) remains to be implemented. `, Run: func(req cmds.Request) (interface{}, error) { - var added AddOutput + added := &AddOutput{} n := req.Context().Node recursive, err := req.Option("r").Bool() @@ -167,7 +168,7 @@ remains to be implemented. cmds.Text: func(res cmds.Response) ([]byte, error) { val, ok := res.Output().(*AddOutput) if !ok { - return nil, errors.New("cast error") + return nil, u.ErrCast() } var buf bytes.Buffer diff --git a/core/commands2/block.go b/core/commands2/block.go index 65097ab17..7b0d1e175 100644 --- a/core/commands2/block.go +++ b/core/commands2/block.go @@ -2,7 +2,6 @@ package commands import ( "bytes" - "errors" "fmt" "io" "io/ioutil" @@ -45,7 +44,7 @@ It outputs to stdout, and is a base58 encoded multihash.`, key, ok := req.Arguments()[0].(string) if !ok { - return nil, errors.New("cast error") + return nil, u.ErrCast() } if !u.IsValidHash(key) { @@ -81,7 +80,7 @@ It reads from stdin, and is a base58 encoded multihash.`, in, ok := req.Arguments()[0].(io.Reader) if !ok { - return nil, errors.New("cast error") + return nil, u.ErrCast() } data, err := ioutil.ReadAll(in) diff --git a/core/commands2/bootstrap.go b/core/commands2/bootstrap.go index dd0edb0b0..636037b9f 100644 --- a/core/commands2/bootstrap.go +++ b/core/commands2/bootstrap.go @@ -1,13 +1,13 @@ package commands import ( - "errors" "fmt" "strings" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + u "github.com/jbenet/go-ipfs/util" cmds "github.com/jbenet/go-ipfs/commands" config "github.com/jbenet/go-ipfs/config" ) @@ -152,7 +152,7 @@ func bootstrapInputToPeers(input []interface{}) ([]*config.BootstrapPeer, error) for _, v := range input { addr, ok := v.(string) if !ok { - return nil, errors.New("cast error") + return nil, u.ErrCast() } addrS, peeridS := split(addr) diff --git a/core/commands2/config.go b/core/commands2/config.go index dd92c2729..40fb3cf91 100644 --- a/core/commands2/config.go +++ b/core/commands2/config.go @@ -12,6 +12,7 @@ import ( cmds "github.com/jbenet/go-ipfs/commands" config "github.com/jbenet/go-ipfs/config" + u "github.com/jbenet/go-ipfs/util" ) type ConfigField struct { @@ -45,7 +46,7 @@ var configCmd = &cmds.Command{ key, ok := args[0].(string) if !ok { - return nil, errors.New("cast error") + return nil, u.ErrCast() } filename, err := config.Filename(req.Context().ConfigRoot) @@ -58,7 +59,7 @@ var configCmd = &cmds.Command{ var ok bool value, ok = args[1].(string) if !ok { - return nil, errors.New("cast error") + return nil, u.ErrCast() } return setConfig(filename, key, value) diff --git a/core/commands2/internal/slice_util.go b/core/commands2/internal/slice_util.go index 2f9cf45c4..75a71fd78 100644 --- a/core/commands2/internal/slice_util.go +++ b/core/commands2/internal/slice_util.go @@ -1,18 +1,17 @@ package internal import ( - "errors" "io" -) -var CastErr = errors.New("cast error") + u "github.com/jbenet/go-ipfs/util" +) func CastToReaders(slice []interface{}) ([]io.Reader, error) { readers := make([]io.Reader, 0) for _, arg := range slice { reader, ok := arg.(io.Reader) if !ok { - return nil, CastErr + return nil, u.ErrCast() } readers = append(readers, reader) } @@ -24,7 +23,7 @@ func CastToStrings(slice []interface{}) ([]string, error) { for _, maybe := range slice { str, ok := maybe.(string) if !ok { - return nil, CastErr + return nil, u.ErrCast() } strs = append(strs, str) } diff --git a/core/commands2/object.go b/core/commands2/object.go index 48e1b27a3..6b43248fe 100644 --- a/core/commands2/object.go +++ b/core/commands2/object.go @@ -8,8 +8,9 @@ import ( "io/ioutil" cmds "github.com/jbenet/go-ipfs/commands" - "github.com/jbenet/go-ipfs/core" + core "github.com/jbenet/go-ipfs/core" dag "github.com/jbenet/go-ipfs/merkledag" + u "github.com/jbenet/go-ipfs/util" ) // ErrObjectTooLarge is returned when too much data was read from stdin. current limit 512k @@ -51,7 +52,7 @@ output is the raw data of the object. key, ok := req.Arguments()[0].(string) if !ok { - return nil, errors.New("cast error") + return nil, u.ErrCast() } return objectData(n, key) @@ -71,7 +72,7 @@ It outputs to stdout, and is a base58 encoded multihash.`, key, ok := req.Arguments()[0].(string) if !ok { - return nil, errors.New("cast error") + return nil, u.ErrCast() } return objectLinks(n, key) @@ -99,7 +100,7 @@ This command outputs data in the following encodings: key, ok := req.Arguments()[0].(string) if !ok { - return nil, errors.New("cast error") + return nil, u.ErrCast() } object, err := objectGet(n, key) @@ -151,12 +152,12 @@ Data should be in the format specified by . input, ok := req.Arguments()[0].(io.Reader) if !ok { - return nil, errors.New("cast error") + return nil, u.ErrCast() } encoding, ok := req.Arguments()[1].(string) if !ok { - return nil, errors.New("cast error") + return nil, u.ErrCast() } output, err := objectPut(n, input, encoding) diff --git a/core/commands2/resolve.go b/core/commands2/resolve.go index 422ee32ee..636f36faa 100644 --- a/core/commands2/resolve.go +++ b/core/commands2/resolve.go @@ -4,6 +4,7 @@ import ( "errors" cmds "github.com/jbenet/go-ipfs/commands" + u "github.com/jbenet/go-ipfs/util" ) var resolveCmd = &cmds.Command{ @@ -49,7 +50,7 @@ Resolve te value of another name: var ok bool name, ok = req.Arguments()[0].(string) if !ok { - return nil, errors.New("cast error") + return nil, u.ErrCast() } } diff --git a/util/testutil/gen.go b/util/testutil/gen.go index 111cbe728..be2fe5988 100644 --- a/util/testutil/gen.go +++ b/util/testutil/gen.go @@ -1,8 +1,8 @@ package testutil import ( - "testing" crand "crypto/rand" + "testing" "github.com/jbenet/go-ipfs/peer" diff --git a/util/util.go b/util/util.go index d4ce940d0..141cd8cba 100644 --- a/util/util.go +++ b/util/util.go @@ -8,6 +8,7 @@ import ( "math/rand" "os" "path/filepath" + "runtime/debug" "strings" "time" @@ -40,6 +41,14 @@ func TildeExpansion(filename string) (string, error) { return homedir.Expand(filename) } +// ErrCast is returned when a cast fails AND the program should not panic. +func ErrCast() error { + debug.PrintStack() + return errCast +} + +var errCast = errors.New("cast error") + // ExpandPathnames takes a set of paths and turns them into absolute paths func ExpandPathnames(paths []string) ([]string, error) { var out []string From a4e68c241bdb7367b4b9e4ed7ee4c5d9f7927c9d Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 00:55:04 -0800 Subject: [PATCH 261/383] commands/http: Get string representations of option values when creating querystring --- commands/http/client.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/commands/http/client.go b/commands/http/client.go index df8e0cfd2..a4dca5f0a 100644 --- a/commands/http/client.go +++ b/commands/http/client.go @@ -66,10 +66,7 @@ func getQuery(req cmds.Request) (string, io.Reader, error) { query := url.Values{} for k, v := range req.Options() { - str, ok := v.(string) - if !ok { - return "", nil, u.ErrCast() - } + str := fmt.Sprintf("%v", v) query.Set(k, str) } From 9f801561fd8bf32c681484f06e6d3135b0c199a2 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 23:32:11 -0800 Subject: [PATCH 262/383] feat(commands) add cast error --- commands/option.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/commands/option.go b/commands/option.go index 0cc4713ef..b4c393f2d 100644 --- a/commands/option.go +++ b/commands/option.go @@ -5,6 +5,8 @@ import ( "reflect" ) +var CastError = errors.New("cast error") + // Types of Command options const ( Invalid = reflect.Invalid From 71e92dace23b9b52de1710406e13993156522086 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 23:28:38 -0800 Subject: [PATCH 263/383] explain(commands/http) we've gotta allow the code to speak for itself. I wouldn't have been able to safely modify this code without having my hand held. I am but a lowly programmer with a simple mind. cc @jbenet @whyrusleeping @mappum --- commands/http/client.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/commands/http/client.go b/commands/http/client.go index a4dca5f0a..09cc48639 100644 --- a/commands/http/client.go +++ b/commands/http/client.go @@ -32,7 +32,14 @@ func NewClient(address string) Client { } func (c *client) Send(req cmds.Request) (cmds.Response, error) { - userEncoding, _ := req.Option(cmds.EncShort).String() + + // save user-provided encoding + previousUserProvidedEncoding, found, err := req.Option(cmds.EncShort).String() + if err != nil { + return nil, err + } + + // override with json to send to server req.SetOption(cmds.EncShort, cmds.JSON) query, inputStream, err := getQuery(req) @@ -43,18 +50,23 @@ func (c *client) Send(req cmds.Request) (cmds.Response, error) { path := strings.Join(req.Path(), "/") url := fmt.Sprintf(ApiUrlFormat, c.serverAddress, ApiPath, path, query) + // TODO extract string const? httpRes, err := http.Post(url, "application/octet-stream", inputStream) if err != nil { return nil, err } + // using the overridden JSON encoding in request res, err := getResponse(httpRes, req) if err != nil { return nil, err } - if len(userEncoding) > 0 { - req.SetOption(cmds.EncShort, userEncoding) + if found && len(previousUserProvidedEncoding) > 0 { + // reset to user provided encoding after sending request + // NB: if user has provided an encoding but it is the empty string, + // still leave it as JSON. + req.SetOption(cmds.EncShort, previousUserProvidedEncoding) } return res, nil From 69374b30be4c3da7dcf5692f476ae66f5bdf229d Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 23:31:02 -0800 Subject: [PATCH 264/383] refactor(commands/optionvalue) use @mappum this optionvalue is a really great abstraction. It makes maintenance super simple! @jbenet @whyrusleeping --- commands/option.go | 69 ++++++++++++++++++++++---------------------- commands/response.go | 8 ++--- 2 files changed, 38 insertions(+), 39 deletions(-) diff --git a/commands/option.go b/commands/option.go index b4c393f2d..ebc9aef42 100644 --- a/commands/option.go +++ b/commands/option.go @@ -78,60 +78,59 @@ func (ov OptionValue) Found() bool { } // value accessor methods, gets the value as a certain type -func (ov OptionValue) Bool() (bool, error) { +func (ov OptionValue) Bool() (value bool, found bool, err error) { + if !ov.found { + return false, false, nil + } val, ok := ov.value.(bool) if !ok { - var err error - if ov.value != nil { - err = errors.New("error casting to bool") - } - return false, err + err = CastError } - return val, nil + return val, ov.found, err } -func (ov OptionValue) Int() (int, error) { + +func (ov OptionValue) Int() (val int, found bool, err error) { + if !ov.found { + return 0, false, nil + } val, ok := ov.value.(int) if !ok { - var err error - if ov.value != nil { - err = errors.New("error casting to int") - } - return 0, err + err = CastError } - return val, nil + return val, ov.found, err } -func (ov OptionValue) Uint() (uint, error) { + +func (ov OptionValue) Uint() (val uint, found bool, err error) { + if !ov.found { + return 0, false, nil + } val, ok := ov.value.(uint) if !ok { - var err error - if ov.value != nil { - err = errors.New("error casting to uint") - } - return 0, err + err = CastError } - return val, nil + return val, ov.found, err } -func (ov OptionValue) Float() (float64, error) { + +func (ov OptionValue) Float() (val float64, found bool, err error) { + if !ov.found { + return 0, false, nil + } val, ok := ov.value.(float64) if !ok { - var err error - if ov.value != nil { - err = errors.New("error casting to float64") - } - return 0.0, err + err = CastError } - return val, nil + return val, ov.found, err } -func (ov OptionValue) String() (string, error) { + +func (ov OptionValue) String() (val string, found bool, err error) { + if !ov.found { + return "", false, nil + } val, ok := ov.value.(string) if !ok { - var err error - if ov.value != nil { - err = errors.New("error casting to string") - } - return "", err + err = CastError } - return val, nil + return val, ov.found, err } // Flag names diff --git a/commands/response.go b/commands/response.go index 8d8bdccda..d4c19d10e 100644 --- a/commands/response.go +++ b/commands/response.go @@ -108,13 +108,13 @@ func (r *response) Marshal() ([]byte, error) { return []byte{}, nil } - if !r.req.Option(EncShort).Found() { - return nil, fmt.Errorf("No encoding type was specified") - } - enc, err := r.req.Option(EncShort).String() + enc, found, err := r.req.Option(EncShort).String() if err != nil { return nil, err } + if !found { + return nil, fmt.Errorf("No encoding type was specified") + } encType := EncodingType(strings.ToLower(enc)) var marshaller Marshaller From 3c4f628493a3355c3a74ffdb68b374cbdb9c7053 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 23:32:47 -0800 Subject: [PATCH 265/383] tests(commands/option) test the OptionValue methods TODO add tests for remaning, untested methods. --- commands/option_test.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 commands/option_test.go diff --git a/commands/option_test.go b/commands/option_test.go new file mode 100644 index 000000000..1ca612ee0 --- /dev/null +++ b/commands/option_test.go @@ -0,0 +1,36 @@ +package commands + +import "testing" + +func TestOptionValueExtractBoolNotFound(t *testing.T) { + t.Log("ensure that no error is returned when value is not found") + optval := &OptionValue{found: false} + _, _, err := optval.Bool() + if err != nil { + t.Fatal("Found was false. Err should have been nil") + } + + t.Log("ensure that no error is returned when value is not found (even if value exists)") + optval = &OptionValue{value: "wrong type: a string", found: false} + _, _, err = optval.Bool() + if err != nil { + t.Fatal("Found was false. Err should have been nil") + } +} + +func TestOptionValueExtractWrongType(t *testing.T) { + + t.Log("ensure that error is returned when value if of wrong type") + + optval := &OptionValue{value: "wrong type: a string", found: true} + _, _, err := optval.Bool() + if err == nil { + t.Fatal("No error returned. Failure.") + } + + optval = &OptionValue{value: "wrong type: a string", found: true} + _, _, err = optval.Int() + if err == nil { + t.Fatal("No error returned. Failure.") + } +} From bff646fb26548892958debaafeeef0270b431156 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 23:35:36 -0800 Subject: [PATCH 266/383] fix(commands/optionvalue) don't shadow the return variable NB: return variables are provided for clarity. Otherwise, it's not entirely clear that the bool is for _found_. --- commands/option.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/commands/option.go b/commands/option.go index ebc9aef42..e5b81a3db 100644 --- a/commands/option.go +++ b/commands/option.go @@ -89,7 +89,7 @@ func (ov OptionValue) Bool() (value bool, found bool, err error) { return val, ov.found, err } -func (ov OptionValue) Int() (val int, found bool, err error) { +func (ov OptionValue) Int() (value int, found bool, err error) { if !ov.found { return 0, false, nil } @@ -100,7 +100,7 @@ func (ov OptionValue) Int() (val int, found bool, err error) { return val, ov.found, err } -func (ov OptionValue) Uint() (val uint, found bool, err error) { +func (ov OptionValue) Uint() (value uint, found bool, err error) { if !ov.found { return 0, false, nil } @@ -111,7 +111,7 @@ func (ov OptionValue) Uint() (val uint, found bool, err error) { return val, ov.found, err } -func (ov OptionValue) Float() (val float64, found bool, err error) { +func (ov OptionValue) Float() (value float64, found bool, err error) { if !ov.found { return 0, false, nil } @@ -122,7 +122,7 @@ func (ov OptionValue) Float() (val float64, found bool, err error) { return val, ov.found, err } -func (ov OptionValue) String() (val string, found bool, err error) { +func (ov OptionValue) String() (value string, found bool, err error) { if !ov.found { return "", false, nil } From c46102cec57c1d38d5e72e3bb46fc51ffe2a57cd Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 23:38:33 -0800 Subject: [PATCH 267/383] fix(commands/http/handler) check if found @mappum, could you CR this and let me know if I've interpreted the desired behavior correctly? --- commands/http/handler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commands/http/handler.go b/commands/http/handler.go index 79d862fad..2a2e3ff8c 100644 --- a/commands/http/handler.go +++ b/commands/http/handler.go @@ -57,8 +57,8 @@ func (i Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Header().Set(streamHeader, "1") } else { - enc, err := req.Option(cmds.EncShort).String() - if err != nil || len(enc) == 0 { + enc, found, err := req.Option(cmds.EncShort).String() + if err != nil || !found { w.WriteHeader(http.StatusInternalServerError) return } From 654fa3fe96ef5caab22e0ba216d33d19e3ae2c88 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 23:43:02 -0800 Subject: [PATCH 268/383] fix(commands/mount_unix, optionvalue) handle found, !found @mappum, could you provide a quick LGTM? --- core/commands2/mount_unix.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go index 792f8dec4..492e5c738 100644 --- a/core/commands2/mount_unix.go +++ b/core/commands2/mount_unix.go @@ -42,10 +42,12 @@ not be listable, as it is virtual. Accessing known paths directly. return nil, err } - // update fsdir with flag. - fsdir := ctx.Config.Mounts.IPFS - if req.Option("f").Found() { - fsdir, _ = req.Option("f").String() + fsdir, found, err := req.Option("f").String() + if err != nil { + return nil, err + } + if !found { + fsdir = ctx.Config.Mounts.IPFS // use default value } fsdone := mountIpfs(ctx.Node, fsdir) From b6f89db1d4f9f4ae9f6a8ce0670a15f4367b76f4 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 00:01:35 -0800 Subject: [PATCH 269/383] fix(mount_unix) optionvalue signature @mappum can you just sanity check me here with a quick LGTM CR? --- core/commands2/mount_unix.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go index 492e5c738..13918f8db 100644 --- a/core/commands2/mount_unix.go +++ b/core/commands2/mount_unix.go @@ -52,10 +52,14 @@ not be listable, as it is virtual. Accessing known paths directly. fsdone := mountIpfs(ctx.Node, fsdir) // get default mount points - nsdir := ctx.Config.Mounts.IPNS - if req.Option("n").Found() { - nsdir, _ = req.Option("n").String() + nsdir, found, err := req.Option("n").String() + if err != nil { + return nil, err } + if !found { + nsdir = ctx.Config.Mounts.IPNS // NB: be sure to not redeclare! + } + nsdone := mountIpns(ctx.Node, nsdir, fsdir) // wait until mounts return an error (or timeout if successful) From a95dfdcf822ebe8f5373bd357d65fa903d4ce924 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 00:06:05 -0800 Subject: [PATCH 270/383] fix(2/pin) optionvalue signature --- core/commands2/pin.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/core/commands2/pin.go b/core/commands2/pin.go index 84e8c3749..3ea467b42 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -34,7 +34,13 @@ on disk. n := req.Context().Node // set recursive flag - recursive, _ := req.Option("recursive").Bool() // false if cast fails. + recursive, found, err := req.Option("recursive").Bool() + if err != nil { + return nil, err + } + if !found { + recursive = false + } paths, err := internal.CastToStrings(req.Arguments()) if err != nil { @@ -67,7 +73,13 @@ collected if needed. n := req.Context().Node // set recursive flag - recursive, _ := req.Option("recursive").Bool() // false if cast fails. + recursive, found, err := req.Option("recursive").Bool() + if err != nil { + return nil, err + } + if !found { + recursive = false // default + } paths, err := internal.CastToStrings(req.Arguments()) if err != nil { From 7c752271907ddcf3a41045ff5365b1dd5f6e66c6 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 00:08:24 -0800 Subject: [PATCH 271/383] fix(2/refs) optionvalue signature @mappum this one is straightforward, but tagging you here just in case --- core/commands2/refs.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/core/commands2/refs.go b/core/commands2/refs.go index 9efc06b54..4b50928d2 100644 --- a/core/commands2/refs.go +++ b/core/commands2/refs.go @@ -34,8 +34,21 @@ Note: list all refs recursively with -r.`, Run: func(req cmds.Request) (interface{}, error) { n := req.Context().Node - unique, _ := req.Option("unique").Bool() - recursive, _ := req.Option("recursive").Bool() + unique, found, err := req.Option("unique").Bool() + if err != nil { + return nil, err + } + if !found { + unique = false + } + + recursive, found, err := req.Option("recursive").Bool() + if err != nil { + return nil, err + } + if !found { + recursive = false + } paths, err := internal.CastToStrings(req.Arguments()) if err != nil { From 3493e33ec9ab52f1e7ef0a33e304c4e5327bead8 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 00:10:47 -0800 Subject: [PATCH 272/383] fix(2/version) option value sig --- core/commands2/version.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/commands2/version.go b/core/commands2/version.go index 1e7adee8c..aa9e27a0d 100644 --- a/core/commands2/version.go +++ b/core/commands2/version.go @@ -27,7 +27,13 @@ var versionCmd = &cmds.Command{ v := res.Output().(*VersionOutput) s := "" - number, _ := res.Request().Option("number").Bool() + number, found, err := res.Request().Option("number").Bool() + if err != nil { + return nil, err + } + if !found { + number = false + } if !number { s += "ipfs version " From 013d98a35af1269612256120bc2212f2ee39bdac Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 00:40:01 -0800 Subject: [PATCH 273/383] fix(2/init, 2/add) fix optionvalue signature --- cmd/ipfs2/init.go | 10 +++++----- core/commands2/add.go | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index eb4b0d423..b7b83cb3c 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -27,25 +27,25 @@ var initCmd = &cmds.Command{ }, Run: func(req cmds.Request) (interface{}, error) { - dspath, err := req.Option("d").String() + dspathOverride, _, err := req.Option("d").String() // if !found it's okay. Let == "" if err != nil { return nil, err } - force, err := req.Option("f").Bool() + force, _, err := req.Option("f").Bool() // if !found, it's okay force == false if err != nil { return nil, err } - nBitsForKeypair, err := req.Option("b").Int() + nBitsForKeypair, bitsOptFound, err := req.Option("b").Int() if err != nil { return nil, err } - if !req.Option("b").Found() { + if !bitsOptFound { nBitsForKeypair = 4096 } - return nil, doInit(req.Context().ConfigRoot, dspath, force, nBitsForKeypair) + return nil, doInit(req.Context().ConfigRoot, dspathOverride, force, nBitsForKeypair) }, } diff --git a/core/commands2/add.go b/core/commands2/add.go index dc9566f35..ddffdd2aa 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -45,7 +45,7 @@ remains to be implemented. added := &AddOutput{} n := req.Context().Node - recursive, err := req.Option("r").Bool() + recursive, _, err := req.Option("r").Bool() if err != nil { return nil, err } From fed2f8d2c631639aac2e2804f776c56151c163d9 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 00:54:58 -0800 Subject: [PATCH 274/383] fix(2/main) option value signature --- cmd/ipfs2/main.go | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 9d000ce48..15d09043f 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -48,7 +48,7 @@ func run() error { return err } - debug, err := req.Option("debug").Bool() + debug, _, err := req.Option("debug").Bool() if err != nil { return err } @@ -95,8 +95,15 @@ func createRequest(args []string) (cmds.Request, *cmds.Command, error) { var longHelp, shortHelp bool if req != nil { - longHelp, _ = req.Option("help").Bool() - shortHelp, _ = req.Option("h").Bool() + // help and h are defined in the root. We expect them to be bool. + longHelp, _, err = req.Option("help").Bool() + if err != nil { + return nil, nil, err + } + shortHelp, _, err = req.Option("h").Bool() + if err != nil { + return nil, nil, err + } } // if the -help flag wasn't specified, show the error message @@ -153,11 +160,11 @@ func createRequest(args []string) (cmds.Request, *cmds.Command, error) { } func handleHelpOption(req cmds.Request, root *cmds.Command) (helpTextDisplayed bool, err error) { - longHelp, err := req.Option("help").Bool() + longHelp, _, err := req.Option("help").Bool() if err != nil { return false, err } - shortHelp, err := req.Option("h").Bool() + shortHelp, _, err := req.Option("h").Bool() if err != nil { return false, err } @@ -183,12 +190,14 @@ func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { res = root.Call(req) } else { - local, err := req.Option("local").Bool() + local, found, err := req.Option("local").Bool() if err != nil { return nil, err } - if (!req.Option("local").Found() || !local) && daemon.Locked(req.Context().ConfigRoot) { + remote := !found || !local + + if remote && daemon.Locked(req.Context().ConfigRoot) { addr, err := ma.NewMultiaddr(req.Context().Config.Addresses.API) if err != nil { return nil, err @@ -251,11 +260,11 @@ func outputResponse(res cmds.Response, root *cmds.Command) error { } func getConfigRoot(req cmds.Request) (string, error) { - configOpt, err := req.Option("config").String() + configOpt, found, err := req.Option("config").String() if err != nil { return "", err } - if configOpt != "" { + if found && configOpt != "" { return configOpt, nil } From 33ba3f6321415733ac8d8591e5e2265cc7fb5679 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 23:51:49 -0800 Subject: [PATCH 275/383] refactor(mount_unix) return the errs in select fix(mount_unix) rm extraneous error --- core/commands2/mount_unix.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go index 13918f8db..9753774e3 100644 --- a/core/commands2/mount_unix.go +++ b/core/commands2/mount_unix.go @@ -63,18 +63,17 @@ not be listable, as it is virtual. Accessing known paths directly. nsdone := mountIpns(ctx.Node, nsdir, fsdir) // wait until mounts return an error (or timeout if successful) - var err error select { - case err = <-fsdone: - case err = <-nsdone: + case err := <-fsdone: + return nil, err + case err := <-nsdone: + return nil, err // mounted successfully, we timed out with no errors case <-time.After(mountTimeout): output := ctx.Config.Mounts return &output, nil } - - return nil, err }, Type: &config.Mounts{}, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ From 7060e7dfe2e7a6a9a7226526dfb8cf07983a46d6 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 00:23:15 -0800 Subject: [PATCH 276/383] rename variable --- cmd/ipfs2/init.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index b7b83cb3c..a72b590b6 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -50,7 +50,8 @@ var initCmd = &cmds.Command{ } // TODO add default welcome hash: eaa68bedae247ed1e5bd0eb4385a3c0959b976e4 -func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) error { +// NB: if dspath is not provided, it will be retrieved from the config +func doInit(configRoot string, dspathOverride string, force bool, nBitsForKeypair int) error { u.POut("initializing ipfs node at %s\n", configRoot) @@ -67,7 +68,7 @@ func doInit(configRoot string, dspath string, force bool, nBitsForKeypair int) e } } - ds, err := datastoreConfig(dspath) + ds, err := datastoreConfig(dspathOverride) if err != nil { return err } From cc37cc18c91766abe5dcfd6fd1e155c1662af9a0 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Tue, 11 Nov 2014 23:45:38 -0800 Subject: [PATCH 277/383] question(mount_unix) add todo regarding mount timeout @whyrusleeping @jbenet is this non-deterministic? --- core/commands2/mount_unix.go | 1 + 1 file changed, 1 insertion(+) diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go index 9753774e3..a4b57cfbe 100644 --- a/core/commands2/mount_unix.go +++ b/core/commands2/mount_unix.go @@ -14,6 +14,7 @@ import ( ) // amount of time to wait for mount errors +// TODO is this non-deterministic? const mountTimeout = time.Second var mountCmd = &cmds.Command{ From 192377a852545bca5ceec85925c0e30936abbae5 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 01:04:02 -0800 Subject: [PATCH 278/383] fix(commands/optionvalue) use the util.ErrCast() --- commands/option.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/commands/option.go b/commands/option.go index e5b81a3db..3a679d9f9 100644 --- a/commands/option.go +++ b/commands/option.go @@ -1,11 +1,10 @@ package commands import ( - "errors" "reflect" -) -var CastError = errors.New("cast error") + "github.com/jbenet/go-ipfs/util" +) // Types of Command options const ( @@ -84,7 +83,7 @@ func (ov OptionValue) Bool() (value bool, found bool, err error) { } val, ok := ov.value.(bool) if !ok { - err = CastError + err = util.ErrCast() } return val, ov.found, err } @@ -95,7 +94,7 @@ func (ov OptionValue) Int() (value int, found bool, err error) { } val, ok := ov.value.(int) if !ok { - err = CastError + err = util.ErrCast() } return val, ov.found, err } @@ -106,7 +105,7 @@ func (ov OptionValue) Uint() (value uint, found bool, err error) { } val, ok := ov.value.(uint) if !ok { - err = CastError + err = util.ErrCast() } return val, ov.found, err } @@ -117,7 +116,7 @@ func (ov OptionValue) Float() (value float64, found bool, err error) { } val, ok := ov.value.(float64) if !ok { - err = CastError + err = util.ErrCast() } return val, ov.found, err } @@ -128,7 +127,7 @@ func (ov OptionValue) String() (value string, found bool, err error) { } val, ok := ov.value.(string) if !ok { - err = CastError + err = util.ErrCast() } return val, ov.found, err } From 7116591351727233cbace997ceb0575acda1e0de Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 01:14:55 -0800 Subject: [PATCH 279/383] docs(commands/request) --- commands/request.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/commands/request.go b/commands/request.go index f18e694c3..428a8ad2c 100644 --- a/commands/request.go +++ b/commands/request.go @@ -25,6 +25,12 @@ type Request interface { Option(name string) *OptionValue Options() optMap SetOption(name string, val interface{}) + + // Arguments() returns user provided arguments as declared on the Command. + // + // NB: `io.Reader`s returned by Arguments() are owned by the library. + // Readers are not guaranteed to remain open after the Command's Run + // function returns. Arguments() []interface{} // TODO: make argument value type instead of using interface{} Context() *Context SetContext(Context) From 475f3f485b79d28a4863e152f022cce856feaa1f Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 01:24:55 -0800 Subject: [PATCH 280/383] commands: Added HelpText struct to organize different help text fields in Commands --- commands/command.go | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/commands/command.go b/commands/command.go index 2e8af5421..3ef0eb27a 100644 --- a/commands/command.go +++ b/commands/command.go @@ -19,6 +19,21 @@ type Function func(Request) (interface{}, error) // (or an error on failure) type Marshaller func(Response) ([]byte, error) +// HelpText is a set of strings used to generate command help text. The help +// text follows formats similar to man pages, but not exactly the same. +type HelpText struct { + // required + Tagline string // used in + ShortDescription string // used in DESCRIPTION + + // optional - whole section overrides + Usage string // overrides USAGE section + LongDescription string // overrides DESCRIPTION section + Options string // overrides OPTIONS section + Arguments string // overrides ARGUMENTS section + Subcommands string // overrides SUBCOMMANDS section +} + // TODO: check Argument definitions when creating a Command // (might need to use a Command constructor) // * make sure any variadic args are at the end @@ -28,8 +43,7 @@ type Marshaller func(Response) ([]byte, error) // Command is a runnable command, with input arguments and options (flags). // It can also have Subcommands, to group units of work into sets. type Command struct { - // MAYBE_TODO: move all the text fields into a struct - // MAYBE_TODO: move these out of command and put them somewhere in commands/cli + // TODO: remove these fields after porting commands to HelpText struct Description string Help string SubcommandHelp string @@ -40,6 +54,7 @@ type Command struct { Arguments []Argument Run Function Marshallers map[EncodingType]Marshaller + Helptext HelpText // Type describes the type of the output of the Command's Run Function. // In precise terms, the value of Type is an instance of the return type of From 47987f5df5a481d9c7f62886c62c598ef9de2754 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 01:26:40 -0800 Subject: [PATCH 281/383] commands/cli: Transitionary commit - Generate helptext from HelpText fields if they aren't empty --- commands/cli/helptext.go | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index 240fb982c..d33087cb5 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -23,6 +23,7 @@ const ( type helpFields struct { Indent string + Usage string Path string ArgUsage string Tagline string @@ -32,7 +33,7 @@ type helpFields struct { Description string } -const usageFormat = "{{.Path}}{{if .ArgUsage}} {{.ArgUsage}}{{end}} - {{.Tagline}}" +const usageFormat = "{{if .Usage}}{{.Usage}}{{else}}{{.Path}}{{if .ArgUsage}} {{.ArgUsage}}{{end}} - {{.Tagline}}{{end}}" const longHelpFormat = ` {{.Indent}}{{template "usage" .}} @@ -101,6 +102,7 @@ func LongHelp(rootName string, root *cmds.Command, path []string, out io.Writer) pathStr += " " + strings.Join(path, " ") } + // TODO: get the fields from the HelpText struct by default (when commands are ported to use it) fields := helpFields{ Indent: indentStr, Path: pathStr, @@ -112,6 +114,17 @@ func LongHelp(rootName string, root *cmds.Command, path []string, out io.Writer) Description: cmd.Help, } + // TODO: don't do these checks, just use these fields by default (when commands get ported to it) + if len(cmd.Helptext.Tagline) > 0 { + fields.Tagline = cmd.Helptext.Tagline + } + if len(cmd.Helptext.ShortDescription) > 0 { + fields.Description = cmd.Helptext.ShortDescription + } + if len(cmd.Helptext.Usage) > 0 { + fields.Usage = cmd.Helptext.Subcommands + } + // autogen fields that are empty if len(cmd.ArgumentHelp) == 0 { fields.Arguments = strings.Join(argumentText(cmd), "\n") @@ -151,6 +164,28 @@ func ShortHelp(rootName string, root *cmds.Command, path []string, out io.Writer Description: cmd.Help, } + // TODO: don't do these checks, just use these fields by default (when commands get ported to it) + if len(cmd.Helptext.Tagline) > 0 { + fields.Tagline = cmd.Helptext.Tagline + } + if len(cmd.Helptext.Arguments) > 0 { + fields.Arguments = cmd.Helptext.Arguments + } + if len(cmd.Helptext.Options) > 0 { + fields.Options = cmd.Helptext.Options + } + if len(cmd.Helptext.Subcommands) > 0 { + fields.Subcommands = cmd.Helptext.Subcommands + } + if len(cmd.Helptext.LongDescription) > 0 { + fields.Description = cmd.Helptext.LongDescription + } else if len(cmd.Helptext.ShortDescription) > 0 { + fields.Description = cmd.Helptext.ShortDescription + } + if len(cmd.Helptext.Usage) > 0 { + fields.Usage = cmd.Helptext.Subcommands + } + fields.Description = indentString(fields.Description, indentStr) return shortHelpTemplate.Execute(out, fields) From c25bf522d6e5f0294abf7d530ed2a01f72d7c784 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 12 Nov 2014 01:04:38 -0800 Subject: [PATCH 282/383] newline in short helptext --- commands/cli/helptext.go | 1 + 1 file changed, 1 insertion(+) diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index d33087cb5..3029f83af 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -59,6 +59,7 @@ const longHelpFormat = ` {{end}} ` const shortHelpFormat = `USAGE: + {{.Indent}}{{template "usage" .}} {{if .Description}} {{.Indent}}{{.Description}} From ec8be23cbc22a4890516f4cfa3a55181140980ac Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 12 Nov 2014 01:40:02 -0800 Subject: [PATCH 283/383] cmds/main decomposed handleParseError --- cmd/ipfs2/main.go | 82 +++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 15d09043f..33fa93f91 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -92,45 +92,7 @@ func createRequest(args []string) (cmds.Request, *cmds.Command, error) { // handle parse error (which means the commandline input was wrong, // e.g. incorrect number of args, or nonexistent subcommand) if err != nil { - var longHelp, shortHelp bool - - if req != nil { - // help and h are defined in the root. We expect them to be bool. - longHelp, _, err = req.Option("help").Bool() - if err != nil { - return nil, nil, err - } - shortHelp, _, err = req.Option("h").Bool() - if err != nil { - return nil, nil, err - } - } - - // if the -help flag wasn't specified, show the error message - // or if a path was returned (user specified a valid subcommand), show the error message - // (this means there was an option or argument error) - if path != nil && len(path) > 0 { - if !longHelp && !shortHelp { - fmt.Printf(errorFormat, err) - } - } - - if cmd == nil { - root = commands.Root - } - - // show the long help text if the -help flag was specified or we are at the root command - // otherwise, show short help text - helpFunc := cmdsCli.ShortHelp - if longHelp || len(path) == 0 { - helpFunc = cmdsCli.LongHelp - } - - htErr := helpFunc("ipfs", root, path, os.Stdout) - if htErr != nil { - fmt.Println(htErr) - } - return nil, nil, err + return nil, nil, handleParseError(req, root, cmd, path) } configPath, err := getConfigRoot(req) @@ -159,6 +121,48 @@ func createRequest(args []string) (cmds.Request, *cmds.Command, error) { return req, root, nil } +func handleParseError(req cmds.Request, root *cmds.Command, cmd *cmds.Command, path []string) (err error) { + var longHelp, shortHelp bool + + if req != nil { + // help and h are defined in the root. We expect them to be bool. + longHelp, _, err = req.Option("help").Bool() + if err != nil { + return err + } + shortHelp, _, err = req.Option("h").Bool() + if err != nil { + return err + } + } + + // if the -help flag wasn't specified, show the error message + // or if a path was returned (user specified a valid subcommand), show the error message + // (this means there was an option or argument error) + if path != nil && len(path) > 0 { + if !longHelp && !shortHelp { + fmt.Printf(errorFormat, err) + } + } + + if cmd == nil { + root = commands.Root + } + + // show the long help text if the -help flag was specified or we are at the root command + // otherwise, show short help text + helpFunc := cmdsCli.ShortHelp + if longHelp || len(path) == 0 { + helpFunc = cmdsCli.LongHelp + } + + htErr := helpFunc("ipfs", root, path, os.Stdout) + if htErr != nil { + fmt.Println(htErr) + } + return err +} + func handleHelpOption(req cmds.Request, root *cmds.Command) (helpTextDisplayed bool, err error) { longHelp, _, err := req.Option("help").Bool() if err != nil { From 78bc748342dfe851cd3849e39d5200b391d2c4ee Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 01:50:39 -0800 Subject: [PATCH 284/383] cmd/ipfs: Fixed bug where handleParseError sets error to nil --- cmd/ipfs2/main.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 33fa93f91..8f17fd49f 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -92,7 +92,7 @@ func createRequest(args []string) (cmds.Request, *cmds.Command, error) { // handle parse error (which means the commandline input was wrong, // e.g. incorrect number of args, or nonexistent subcommand) if err != nil { - return nil, nil, handleParseError(req, root, cmd, path) + return nil, nil, handleParseError(req, root, cmd, path, err) } configPath, err := getConfigRoot(req) @@ -121,11 +121,12 @@ func createRequest(args []string) (cmds.Request, *cmds.Command, error) { return req, root, nil } -func handleParseError(req cmds.Request, root *cmds.Command, cmd *cmds.Command, path []string) (err error) { +func handleParseError(req cmds.Request, root *cmds.Command, cmd *cmds.Command, path []string, parseError error) error { var longHelp, shortHelp bool if req != nil { // help and h are defined in the root. We expect them to be bool. + var err error longHelp, _, err = req.Option("help").Bool() if err != nil { return err @@ -141,7 +142,7 @@ func handleParseError(req cmds.Request, root *cmds.Command, cmd *cmds.Command, p // (this means there was an option or argument error) if path != nil && len(path) > 0 { if !longHelp && !shortHelp { - fmt.Printf(errorFormat, err) + fmt.Printf(errorFormat, parseError) } } @@ -160,7 +161,7 @@ func handleParseError(req cmds.Request, root *cmds.Command, cmd *cmds.Command, p if htErr != nil { fmt.Println(htErr) } - return err + return parseError } func handleHelpOption(req cmds.Request, root *cmds.Command) (helpTextDisplayed bool, err error) { From 8aa532fbc9b4a513007102cb999acad5490aa7ad Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 02:10:29 -0800 Subject: [PATCH 285/383] commands/cli: Less line breaks in autogenerated formatting --- commands/cli/helptext.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index 3029f83af..2cf66b4e5 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -196,9 +196,8 @@ func argumentText(cmd *cmds.Command) []string { lines := make([]string, len(cmd.Arguments)) for i, arg := range cmd.Arguments { - lines[i] = argUsageText(arg) - lines[i] += "\n" + arg.Description - lines[i] = indentString(lines[i], " ") + "\n" + lines[i] = argUsageText(arg) + " - " + arg.Description + lines[i] = indentString(lines[i], " ") } return lines @@ -241,17 +240,18 @@ func optionText(cmd ...*cmds.Command) []string { lines = align(lines) j++ } + lines = align(lines) // add option types to output for i, opt := range options { - lines[i] += " " + fmt.Sprintf(optionType, opt.Type) + lines[i] += " " + fmt.Sprintf("%v", opt.Type) } lines = align(lines) // add option descriptions to output for i, opt := range options { - lines[i] += "\n" + opt.Description - lines[i] = indentString(lines[i], " ") + "\n" + lines[i] += " - " + opt.Description + lines[i] = indentString(lines[i], " ") } return lines @@ -267,9 +267,8 @@ func subcommandText(cmd *cmds.Command, rootName string, path []string) []string i := 0 for name, sub := range cmd.Subcommands { usage := usageText(sub) - lines[i] = fmt.Sprintf("%v%v %v", prefix, name, usage) - lines[i] += fmt.Sprintf("\n%v", sub.Description) - lines[i] = indentString(lines[i], " ") + "\n" + lines[i] = fmt.Sprintf("%v%v %v - %v", prefix, name, usage, sub.Description) + lines[i] = indentString(lines[i], " ") i++ } From 3c9fb48699704cb6b5405a1045c703b2c70730cb Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 03:02:09 -0800 Subject: [PATCH 286/383] fix(2/bootstrap) catch hidden panic @mappum any idea what might cause the type to not match here? --- core/commands2/bootstrap.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/commands2/bootstrap.go b/core/commands2/bootstrap.go index 636037b9f..449b7a387 100644 --- a/core/commands2/bootstrap.go +++ b/core/commands2/bootstrap.go @@ -7,9 +7,9 @@ import ( ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" - u "github.com/jbenet/go-ipfs/util" cmds "github.com/jbenet/go-ipfs/commands" config "github.com/jbenet/go-ipfs/config" + u "github.com/jbenet/go-ipfs/util" ) type BootstrapOutput struct { @@ -129,7 +129,10 @@ var bootstrapListCmd = &cmds.Command{ } func bootstrapMarshaller(res cmds.Response) ([]byte, error) { - v := res.Output().(*BootstrapOutput) + v, ok := res.Output().(*BootstrapOutput) + if !ok { + return nil, u.ErrCast() + } s := "" for _, peer := range v.Peers { From fc7c199d6ac55b7498a03ce6a793e2529679c378 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 12 Nov 2014 03:12:22 -0800 Subject: [PATCH 287/383] cmds/helptext: indent + newlines + synopsis --- commands/cli/helptext.go | 73 +++++++++++++++++++++++++++++++--------- commands/command.go | 1 + 2 files changed, 59 insertions(+), 15 deletions(-) diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index 2cf66b4e5..e27a27bf9 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -29,10 +29,46 @@ type helpFields struct { Tagline string Arguments string Options string + Synopsis string Subcommands string Description string } +// TrimNewlines removes extra newlines from fields. This makes aligning +// commands easier. Below, the leading + tralining newlines are removed: +// Synopsis: ` +// ipfs config - Get value of +// ipfs config - Set value of to +// ipfs config --show - Show config file +// ipfs config --edit - Edit config file in $EDITOR +// ` +func (f *helpFields) TrimNewlines() { + f.Path = strings.Trim(f.Path, "\n") + f.ArgUsage = strings.Trim(f.ArgUsage, "\n") + f.Tagline = strings.Trim(f.Tagline, "\n") + f.Arguments = strings.Trim(f.Arguments, "\n") + f.Options = strings.Trim(f.Options, "\n") + f.Synopsis = strings.Trim(f.Synopsis, "\n") + f.Subcommands = strings.Trim(f.Subcommands, "\n") + f.Description = strings.Trim(f.Description, "\n") +} + +// Indent adds whitespace the lines of fields. +func (f *helpFields) IndentAll() { + indent := func(s string) string { + if s == "" { + return s + } + return indentString(s, indentStr) + } + + f.Arguments = indent(f.Arguments) + f.Options = indent(f.Options) + f.Synopsis = indent(f.Synopsis) + f.Subcommands = indent(f.Subcommands) + f.Description = indent(f.Description) +} + const usageFormat = "{{if .Usage}}{{.Usage}}{{else}}{{.Path}}{{if .ArgUsage}} {{.ArgUsage}}{{end}} - {{.Tagline}}{{end}}" const longHelpFormat = ` @@ -40,29 +76,31 @@ const longHelpFormat = ` {{if .Arguments}}ARGUMENTS: -{{.Indent}}{{.Arguments}} +{{.Arguments}} {{end}}{{if .Options}}OPTIONS: -{{.Indent}}{{.Options}} +{{.Options}} {{end}}{{if .Subcommands}}SUBCOMMANDS: -{{.Indent}}{{.Subcommands}} +{{.Subcommands}} {{.Indent}}Use '{{.Path}} --help' for more information about each command. {{end}}{{if .Description}}DESCRIPTION: -{{.Indent}}{{.Description}} +{{.Description}} {{end}} ` const shortHelpFormat = `USAGE: {{.Indent}}{{template "usage" .}} -{{if .Description}} -{{.Indent}}{{.Description}} +{{if .Synopsis}} +{{.Synopsis}} +{{end}}{{if .Description}} +{{.Description}} {{end}} Use '{{.Path}} --help' for more information about this command. ` @@ -111,6 +149,7 @@ func LongHelp(rootName string, root *cmds.Command, path []string, out io.Writer) Tagline: cmd.Description, Arguments: cmd.ArgumentHelp, Options: cmd.OptionHelp, + Synopsis: cmd.Helptext.Synopsis, Subcommands: cmd.SubcommandHelp, Description: cmd.Help, } @@ -137,10 +176,11 @@ func LongHelp(rootName string, root *cmds.Command, path []string, out io.Writer) fields.Subcommands = strings.Join(subcommandText(cmd, rootName, path), "\n") } - fields.Arguments = indentString(fields.Arguments, indentStr) - fields.Options = indentString(fields.Options, indentStr) - fields.Subcommands = indentString(fields.Subcommands, indentStr) - fields.Description = indentString(fields.Description, indentStr) + // trim the extra newlines (see TrimNewlines doc) + fields.TrimNewlines() + + // indent all fields that have been set + fields.IndentAll() return longHelpTemplate.Execute(out, fields) } @@ -162,6 +202,7 @@ func ShortHelp(rootName string, root *cmds.Command, path []string, out io.Writer Path: pathStr, ArgUsage: usageText(cmd), Tagline: cmd.Description, + Synopsis: cmd.Helptext.Synopsis, Description: cmd.Help, } @@ -178,16 +219,18 @@ func ShortHelp(rootName string, root *cmds.Command, path []string, out io.Writer if len(cmd.Helptext.Subcommands) > 0 { fields.Subcommands = cmd.Helptext.Subcommands } - if len(cmd.Helptext.LongDescription) > 0 { - fields.Description = cmd.Helptext.LongDescription - } else if len(cmd.Helptext.ShortDescription) > 0 { + if len(cmd.Helptext.ShortDescription) > 0 { fields.Description = cmd.Helptext.ShortDescription } if len(cmd.Helptext.Usage) > 0 { fields.Usage = cmd.Helptext.Subcommands } - fields.Description = indentString(fields.Description, indentStr) + // trim the extra newlines (see TrimNewlines doc) + fields.TrimNewlines() + + // indent all fields that have been set + fields.IndentAll() return shortHelpTemplate.Execute(out, fields) } @@ -330,5 +373,5 @@ func indent(lines []string, prefix string) []string { } func indentString(line string, prefix string) string { - return strings.Replace(line, "\n", "\n"+prefix, -1) + return prefix + strings.Replace(line, "\n", "\n"+prefix, -1) } diff --git a/commands/command.go b/commands/command.go index 3ef0eb27a..a31935d21 100644 --- a/commands/command.go +++ b/commands/command.go @@ -25,6 +25,7 @@ type HelpText struct { // required Tagline string // used in ShortDescription string // used in DESCRIPTION + Synopsis string // showcasing the cmd // optional - whole section overrides Usage string // overrides USAGE section From ef4480fe6d3bc1aee7d8718034362e7b09e714a3 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 12 Nov 2014 03:13:17 -0800 Subject: [PATCH 288/383] cmds/config: better help text --- commands/cli/helptext.go | 2 ++ core/commands2/config.go | 37 +++++++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index e27a27bf9..9dfa8bf37 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -98,6 +98,8 @@ const shortHelpFormat = `USAGE: {{.Indent}}{{template "usage" .}} {{if .Synopsis}} +SYNOPSIS + {{.Synopsis}} {{end}}{{if .Description}} {{.Description}} diff --git a/core/commands2/config.go b/core/commands2/config.go index 40fb3cf91..ab6706594 100644 --- a/core/commands2/config.go +++ b/core/commands2/config.go @@ -21,17 +21,34 @@ type ConfigField struct { } var configCmd = &cmds.Command{ - Description: "Get/set IPFS config values", - Help: `Examples: - - Get the value of the 'datastore.path' key: - - ipfs config datastore.path - - Set the value of the 'datastore.path' key: - - ipfs config datastore.path ~/.go-ipfs/datastore + Helptext: cmds.HelpText{ + Tagline: "get and set IPFS config values", + Synopsis: ` +ipfs config - Get value of +ipfs config - Set value of to +ipfs config --show - Show config file +ipfs config --edit - Edit config file in $EDITOR `, + ShortDescription: ` +ipfs config controls configuration variables. It works like 'git config'. +The configuration values are stored in a config file inside your IPFS +repository.`, + LongDescription: ` +ipfs config controls configuration variables. It works +much like 'git config'. The configuration values are stored in a config +file inside your IPFS repository. + +EXAMPLES: + +Get the value of the 'datastore.path' key: + + ipfs config datastore.path + +Set the value of the 'datastore.path' key: + + ipfs config datastore.path ~/.go-ipfs/datastore +`, + }, Arguments: []cmds.Argument{ cmds.StringArg("key", true, false, "The key of the config entry (e.g. \"Addresses.API\")"), From 80d743541eb0e921ef6fd1f99fad2ad61ee4d514 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 02:35:52 -0800 Subject: [PATCH 289/383] commands/cli: Fixed spacing error --- commands/cli/helptext.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index 9dfa8bf37..f4fb9ce72 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -312,7 +312,10 @@ func subcommandText(cmd *cmds.Command, rootName string, path []string) []string i := 0 for name, sub := range cmd.Subcommands { usage := usageText(sub) - lines[i] = fmt.Sprintf("%v%v %v - %v", prefix, name, usage, sub.Description) + if len(usage) > 0 { + usage = " " + usage + } + lines[i] = fmt.Sprintf("%v%v%v - %v", prefix, name, usage, sub.Description) lines[i] = indentString(lines[i], " ") i++ } From c0b3b4377394c0feef2d1bb078de6f3d1b9b2f43 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 12 Nov 2014 03:21:18 -0800 Subject: [PATCH 290/383] cmds2/helptext moved indent + tagline in subcmd --- commands/cli/helptext.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index f4fb9ce72..edda0078b 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -242,7 +242,6 @@ func argumentText(cmd *cmds.Command) []string { for i, arg := range cmd.Arguments { lines[i] = argUsageText(arg) + " - " + arg.Description - lines[i] = indentString(lines[i], " ") } return lines @@ -296,7 +295,6 @@ func optionText(cmd ...*cmds.Command) []string { // add option descriptions to output for i, opt := range options { lines[i] += " - " + opt.Description - lines[i] = indentString(lines[i], " ") } return lines @@ -315,8 +313,11 @@ func subcommandText(cmd *cmds.Command, rootName string, path []string) []string if len(usage) > 0 { usage = " " + usage } - lines[i] = fmt.Sprintf("%v%v%v - %v", prefix, name, usage, sub.Description) - lines[i] = indentString(lines[i], " ") + if len(sub.Helptext.Tagline) > 0 { + lines[i] = fmt.Sprintf("%v%v%v - %v", prefix, name, usage, sub.Helptext.Tagline) + } else { + lines[i] = fmt.Sprintf("%v%v%v - %v", prefix, name, usage, sub.Description) + } i++ } From b836e72aea9ed4552f9ed427f8b2404f2520a175 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 12 Nov 2014 03:39:15 -0800 Subject: [PATCH 291/383] cmd2 align arguments properly --- commands/cli/helptext.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index edda0078b..fa885ee92 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -241,7 +241,11 @@ func argumentText(cmd *cmds.Command) []string { lines := make([]string, len(cmd.Arguments)) for i, arg := range cmd.Arguments { - lines[i] = argUsageText(arg) + " - " + arg.Description + lines[i] = argUsageText(arg) + } + lines = align(lines) + for i, arg := range cmd.Arguments { + lines[i] += " - " + arg.Description } return lines From f2c54f5a4b3c98bab49cbef4132c0f9af748719a Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 12 Nov 2014 03:40:34 -0800 Subject: [PATCH 292/383] cmd2 bootstrapCmd missing type --- core/commands2/bootstrap.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/commands2/bootstrap.go b/core/commands2/bootstrap.go index 449b7a387..2f88b34fc 100644 --- a/core/commands2/bootstrap.go +++ b/core/commands2/bootstrap.go @@ -25,6 +25,8 @@ var bootstrapCmd = &cmds.Command{ Run: bootstrapListCmd.Run, Marshallers: bootstrapListCmd.Marshallers, + Type: bootstrapListCmd.Type, + Subcommands: map[string]*cmds.Command{ "list": bootstrapListCmd, "add": bootstrapAddCmd, From a2d06b5f9a41a256558f19e848de94844dc3a545 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 12 Nov 2014 03:54:40 -0800 Subject: [PATCH 293/383] cmds2: errHelpRequested error --- cmd/ipfs2/main.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 8f17fd49f..33a0eee86 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -25,6 +25,9 @@ import ( // log is the command logger var log = u.Logger("cmd/ipfs") +// signal to output help +var errHelpRequested = errors.New("Help Requested") + const ( cpuProfile = "ipfs.cpuprof" heapProfile = "ipfs.memprof" @@ -45,6 +48,10 @@ func run() error { args := os.Args[1:] req, root, err := createRequest(args) if err != nil { + // when the error is errOutputHelp, just exit gracefully. + if err == errHelpRequested { + return nil + } return err } @@ -135,6 +142,9 @@ func handleParseError(req cmds.Request, root *cmds.Command, cmd *cmds.Command, p if err != nil { return err } + + // override the error to avoid signaling other issues. + parseError = errHelpRequested } // if the -help flag wasn't specified, show the error message From 86b08f4e4be649479d5215792376a55130d57d53 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 12 Nov 2014 04:25:07 -0800 Subject: [PATCH 294/383] cmds2 config outputs nicely --- config/serialize.go | 17 ++++++++++++++++- core/commands2/config.go | 14 +++++--------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/config/serialize.go b/config/serialize.go index 647e19e33..ba17686cb 100644 --- a/config/serialize.go +++ b/config/serialize.go @@ -53,10 +53,25 @@ func WriteFile(filename string, buf []byte) error { return err } +// HumanOutput gets a config value ready for printing +func HumanOutput(value interface{}) ([]byte, error) { + s, ok := value.(string) + if ok { + return []byte(strings.Trim(s, "\n")), nil + } + return Marshal(value) +} + +// Marshal configuration with JSON +func Marshal(value interface{}) ([]byte, error) { + // need to prettyprint, hence MarshalIndent, instead of Encoder + return json.MarshalIndent(value, "", " ") +} + // Encode configuration with JSON func Encode(w io.Writer, value interface{}) error { // need to prettyprint, hence MarshalIndent, instead of Encoder - buf, err := json.MarshalIndent(value, "", " ") + buf, err := Marshal(value) if err != nil { return err } diff --git a/core/commands2/config.go b/core/commands2/config.go index ab6706594..d49d73c7a 100644 --- a/core/commands2/config.go +++ b/core/commands2/config.go @@ -2,7 +2,6 @@ package commands import ( "bytes" - "encoding/json" "errors" "fmt" "io" @@ -87,20 +86,17 @@ Set the value of the 'datastore.path' key: }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ cmds.Text: func(res cmds.Response) ([]byte, error) { - v := res.Output().(*ConfigField) - - s := "" if len(res.Request().Arguments()) == 2 { - s += fmt.Sprintf("'%s' set to: ", v.Key) + return nil, nil // dont output anything } - marshalled, err := json.Marshal(v.Value) + v := res.Output().(*ConfigField) + buf, err := config.HumanOutput(v.Value) if err != nil { return nil, err } - s += fmt.Sprintf("%s\n", marshalled) - - return []byte(s), nil + buf = append(buf, byte('\n')) + return buf, nil }, }, Type: &ConfigField{}, From 22a4cc7d30a67b1743d1ba847005d139e825a22e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 05:07:17 -0800 Subject: [PATCH 295/383] fix(ipfs2) output info to logger when checking daemon status --- cmd/ipfs2/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 33a0eee86..97927aa47 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -212,6 +212,7 @@ func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { remote := !found || !local + log.Info("Checking if daemon is running...") if remote && daemon.Locked(req.Context().ConfigRoot) { addr, err := ma.NewMultiaddr(req.Context().Config.Addresses.API) if err != nil { @@ -231,6 +232,7 @@ func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { } } else { + log.Info("Executing command locally: daemon not running") node, err := core.NewIpfsNode(req.Context().Config, false) if err != nil { return nil, err From 4e740f63eb062ba66d502db991f5f336a1c72ebc Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 15:24:52 -0800 Subject: [PATCH 296/383] docs(ipfs2/main) @mappum License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 97927aa47..ff12ff45e 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -252,6 +252,8 @@ func outputResponse(res cmds.Response, root *cmds.Command) error { fmt.Printf(errorFormat, res.Error().Error()) if res.Error().Code != cmds.ErrClient { + // TODO does ErrClient mean "error the client should see" or "this + // is an error caused by the user ie. user error"? return res.Error() } From 53e875e5fc15f9d08fba2a65a3b155976ea196eb Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 16:45:18 -0800 Subject: [PATCH 297/383] core/commands2: Format 'commands' to match original output (re: #317) --- core/commands2/commands.go | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/core/commands2/commands.go b/core/commands2/commands.go index 6df3cd075..e50294ace 100644 --- a/core/commands2/commands.go +++ b/core/commands2/commands.go @@ -2,7 +2,6 @@ package commands import ( "fmt" - "strings" cmds "github.com/jbenet/go-ipfs/commands" ) @@ -24,7 +23,7 @@ var commandsCmd = &cmds.Command{ Marshallers: map[cmds.EncodingType]cmds.Marshaller{ cmds.Text: func(res cmds.Response) ([]byte, error) { v := res.Output().(*Command) - s := formatCommand(v, 0) + s := formatCommand("", v) return []byte(s), nil }, }, @@ -46,16 +45,15 @@ func outputCommand(name string, cmd *cmds.Command) Command { return output } -func formatCommand(cmd *Command, depth int) string { - var s string - - if depth > 0 { - indent := strings.Repeat(" ", depth-1) - s = fmt.Sprintf("%s%s\n", indent, cmd.Name) +func formatCommand(prefix string, cmd *Command) string { + if len(prefix) > 0 { + prefix += " " } + s := fmt.Sprintf("%s%s\n", prefix, cmd.Name) + prefix += cmd.Name for _, sub := range cmd.Subcommands { - s += formatCommand(&sub, depth+1) + s += formatCommand(prefix, &sub) } return s From fd8b1930af82effe5f03097f1acdd89ac9ae893a Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 16:46:51 -0800 Subject: [PATCH 298/383] cmd/ipfs2: Copy subcommands from core/commands2 root into cmd/ipfs2 root --- cmd/ipfs2/ipfs.go | 24 +++++++++++++++++++----- cmd/ipfs2/main.go | 2 +- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go index ef359e370..7e3b0e873 100644 --- a/cmd/ipfs2/ipfs.go +++ b/cmd/ipfs2/ipfs.go @@ -8,9 +8,23 @@ import ( var Root = &cmds.Command{ Options: commands.Root.Options, Help: commands.Root.Help, - Subcommands: map[string]*cmds.Command{ - "daemon": daemonCmd, // TODO name - "init": initCmd, // TODO name - "tour": cmdTour, - }, +} + +var rootSubcommands = map[string]*cmds.Command{ + "daemon": daemonCmd, // TODO name + "init": initCmd, // TODO name + "tour": cmdTour, +} + +func init() { + // setting here instead of in literal to prevent initialization loop + // (some commands make references to Root) + Root.Subcommands = rootSubcommands + + // copy all subcommands from commands.Root into this root (if they aren't already present) + for k, v := range commands.Root.Subcommands { + if _, found := Root.Subcommands[k]; !found { + Root.Subcommands[k] = v + } + } } diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index ff12ff45e..ded62b2dc 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -94,7 +94,7 @@ func run() error { } func createRequest(args []string) (cmds.Request, *cmds.Command, error) { - req, root, cmd, path, err := cmdsCli.Parse(args, Root, commands.Root) + req, root, cmd, path, err := cmdsCli.Parse(args, Root) // handle parse error (which means the commandline input was wrong, // e.g. incorrect number of args, or nonexistent subcommand) From f797d1357933966ef82fc65557954414c88b5885 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 16:55:08 -0800 Subject: [PATCH 299/383] core/commands2: Added function to generate a 'commands' command for a root --- core/commands2/commands.go | 34 +++++++++++++++++++--------------- core/commands2/root.go | 2 +- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/core/commands2/commands.go b/core/commands2/commands.go index e50294ace..81f61f9ba 100644 --- a/core/commands2/commands.go +++ b/core/commands2/commands.go @@ -11,23 +11,27 @@ type Command struct { Subcommands []Command } -var commandsCmd = &cmds.Command{ - Description: "List all available commands.", - Help: `Lists all available commands (and subcommands) and exits. -`, +// CommandsCmd takes in a root command, +// and returns a command that lists the subcommands in that root +func CommandsCmd(root *cmds.Command) *cmds.Command { + return &cmds.Command{ + Description: "List all available commands.", + Help: `Lists all available commands (and subcommands) and exits. + `, - Run: func(req cmds.Request) (interface{}, error) { - root := outputCommand("ipfs", Root) - return &root, nil - }, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ - cmds.Text: func(res cmds.Response) ([]byte, error) { - v := res.Output().(*Command) - s := formatCommand("", v) - return []byte(s), nil + Run: func(req cmds.Request) (interface{}, error) { + root := outputCommand("ipfs", root) + return &root, nil }, - }, - Type: &Command{}, + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + v := res.Output().(*Command) + s := formatCommand("", v) + return []byte(s), nil + }, + }, + Type: &Command{}, + } } func outputCommand(name string, cmd *cmds.Command) Command { diff --git a/core/commands2/root.go b/core/commands2/root.go index 9356ac760..29bce74c9 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -52,7 +52,7 @@ Plumbing commands: var rootSubcommands = map[string]*cmds.Command{ "cat": catCmd, "ls": lsCmd, - "commands": commandsCmd, + "commands": CommandsCmd(Root), "name": nameCmd, "add": addCmd, "log": logCmd, From 092524fcbb6e7cd5ecfefb9a0b208f7a6856e37d Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 16:55:21 -0800 Subject: [PATCH 300/383] cmd/ipfs2: Added a 'commands' command for CLI root --- cmd/ipfs2/ipfs.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go index 7e3b0e873..5e8dcff68 100644 --- a/cmd/ipfs2/ipfs.go +++ b/cmd/ipfs2/ipfs.go @@ -11,9 +11,10 @@ var Root = &cmds.Command{ } var rootSubcommands = map[string]*cmds.Command{ - "daemon": daemonCmd, // TODO name - "init": initCmd, // TODO name - "tour": cmdTour, + "daemon": daemonCmd, // TODO name + "init": initCmd, // TODO name + "tour": cmdTour, + "commands": commands.CommandsCmd(Root), } func init() { From e41f861f8ac1d2e717502ae196c8aa5f0a89cbc7 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 16:59:51 -0800 Subject: [PATCH 301/383] commands/cli: Added some TODOs to Parse --- commands/cli/parse.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index 884cfe51f..3c0f6b586 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -13,6 +13,8 @@ import ( // returns the corresponding command Request object. // Multiple root commands are supported: // Parse will search each root to find the one that best matches the requested subcommand. +// TODO: get rid of extraneous return values (e.g. we ended up not needing the root value anymore) +// TODO: get rid of multiple-root support, we should only need one now func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, *cmds.Command, []string, error) { var root, cmd *cmds.Command var path, stringArgs []string From 3e7592f2ccda96182df7ff4abb308b62009ebd5a Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 18:32:44 -0800 Subject: [PATCH 302/383] commands/cli: Sort options by length when generating options helptext --- commands/cli/helptext.go | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index fa885ee92..a7c8e378f 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -3,6 +3,7 @@ package cli import ( "fmt" "io" + "sort" "strings" "text/template" @@ -270,10 +271,12 @@ func optionText(cmd ...*cmds.Command) []string { if len(lines) < i+1 { lines = append(lines, "") } - if len(opt.Names) >= j+1 { - lines[i] += fmt.Sprintf(optionFlag, opt.Names[j]) + + names := sortByLength(opt.Names) + if len(names) >= j+1 { + lines[i] += fmt.Sprintf(optionFlag, names[j]) } - if len(opt.Names) > j+1 { + if len(names) > j+1 { lines[i] += ", " done = false } @@ -385,3 +388,24 @@ func indent(lines []string, prefix string) []string { func indentString(line string, prefix string) string { return prefix + strings.Replace(line, "\n", "\n"+prefix, -1) } + +type lengthSlice []string + +func (ls lengthSlice) Len() int { + return len(ls) +} +func (ls lengthSlice) Swap(a, b int) { + ls[a], ls[b] = ls[b], ls[a] +} +func (ls lengthSlice) Less(a, b int) bool { + return len(ls[a]) < len(ls[b]) +} + +func sortByLength(slice []string) []string { + output := make(lengthSlice, len(slice)) + for i, val := range slice { + output[i] = val + } + sort.Sort(output) + return []string(output) +} From 973a8f5cb10c30ed312a889605298be16a814c5b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 12 Nov 2014 16:05:15 -0800 Subject: [PATCH 303/383] cmds2: commands --- core/commands2/commands.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/commands2/commands.go b/core/commands2/commands.go index 81f61f9ba..b5542976f 100644 --- a/core/commands2/commands.go +++ b/core/commands2/commands.go @@ -15,9 +15,10 @@ type Command struct { // and returns a command that lists the subcommands in that root func CommandsCmd(root *cmds.Command) *cmds.Command { return &cmds.Command{ - Description: "List all available commands.", - Help: `Lists all available commands (and subcommands) and exits. - `, + Helptext: cmds.HelpText{ + Tagline: "List all available commands.", + ShortDescription: `Lists all available commands (and subcommands) and exits.`, + }, Run: func(req cmds.Request) (interface{}, error) { root := outputCommand("ipfs", root) From f738e899c2f25f1b226275b53ab9d2238038e0da Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 12 Nov 2014 17:48:22 -0800 Subject: [PATCH 304/383] cmd2: simplified main Attention @maybebtc @mappum I cleaned up + simplified the main flow. Now, all printing is contained inside main itself! (:cheer:). I do this with the help of a cmdInvocation struct that has both a Parse and Run. The only major clunkiness left is that the "CallCommand" is still its own function. But *shrug*. Please test it works as we would expect. i changed much of the flow, so likely that i missed a complicated edge case. main roadmap: - parse the commandline to get a cmdInvocation - if user requests, help, print it and exit. - run the command invocation - output the response - if anything fails, print error, maybe with help --- cmd/ipfs2/main.go | 264 +++++++++++++++++++----------------------- commands/cli/parse.go | 5 +- 2 files changed, 126 insertions(+), 143 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index ded62b2dc..404c8bc2e 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -17,7 +17,6 @@ import ( cmdsHttp "github.com/jbenet/go-ipfs/commands/http" "github.com/jbenet/go-ipfs/config" "github.com/jbenet/go-ipfs/core" - commands "github.com/jbenet/go-ipfs/core/commands2" daemon "github.com/jbenet/go-ipfs/daemon2" u "github.com/jbenet/go-ipfs/util" ) @@ -34,174 +33,170 @@ const ( errorFormat = "ERROR: %v\n\n" ) -func main() { - err := run() - if err != nil { - fmt.Println(err) - os.Exit(1) - } +type cmdInvocation struct { + path []string + cmd *cmds.Command + root *cmds.Command + req cmds.Request } -func run() error { +// main roadmap: +// - parse the commandline to get a cmdInvocation +// - if user requests, help, print it and exit. +// - run the command invocation +// - output the response +// - if anything fails, print error, maybe with help +func main() { + var invoc cmdInvocation + var err error + + // we'll call this local helper to output errors. + // this is so we control how to print errors in one place. + printErr := func(err error) { + fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error()) + } + + // this is a local helper to print out help text. + // there's some considerations that this makes easier. + printHelp := func(long bool) { + helpFunc := cmdsCli.ShortHelp + if long { + helpFunc = cmdsCli.LongHelp + } + + helpFunc("ipfs", invoc.root, invoc.path, os.Stderr) + } + + // parse the commandline into a command invocation + err = invoc.Parse(os.Args[1:]) + + // BEFORE handling the parse error, if we have enough information + // AND the user requested help, print it out and exit + if invoc.cmd != nil { + longH, shortH, err := invoc.requestedHelp() + if err != nil { + printErr(err) + os.Exit(1) + } + if longH || shortH { + printHelp(longH) + os.Exit(0) + } + } + + // ok now handle handle parse error (which means cli input was wrong, + // e.g. incorrect number of args, or nonexistent subcommand) + if err != nil { + printErr(err) + + // this was a user error, print help. + if invoc.cmd != nil { + // we need a newline space. + fmt.Fprintf(os.Stderr, "\n") + printHelp(false) + } + os.Exit(1) + } + + // ok, finally, run the command invocation. + output, err := invoc.Run() + if err != nil { + printErr(err) + + // if this error was a client error, print short help too. + if isClientError(err) { + printHelp(false) + } + os.Exit(1) + } + + // everything went better than expected :) + io.Copy(os.Stdout, output) +} + +func (i *cmdInvocation) Run() (output io.Reader, err error) { handleInterrupt() - args := os.Args[1:] - req, root, err := createRequest(args) + // check if user wants to debug. option OR env var. + debug, _, err := i.req.Option("debug").Bool() if err != nil { - // when the error is errOutputHelp, just exit gracefully. - if err == errHelpRequested { - return nil - } - return err + return nil, err } - - debug, _, err := req.Option("debug").Bool() - if err != nil { - return err - } - if debug { + if debug || u.GetenvBool("DEBUG") { u.Debug = true u.SetAllLoggers(logging.DEBUG) } + // if debugging, let's profile. + // TODO maybe change this to its own option... profiling makes it slower. if u.Debug { stopProfilingFunc, err := startProfiling() if err != nil { - return err + return nil, err } defer stopProfilingFunc() // to be executed as late as possible } - helpTextDisplayed, err := handleHelpOption(req, root) + res, err := callCommand(i.req, i.root) if err != nil { - return err - } - if helpTextDisplayed { - return nil + return nil, err } - res, err := callCommand(req, root) - if err != nil { - return err - } - - err = outputResponse(res, root) - if err != nil { - return err - } - - return nil + return res.Reader() } -func createRequest(args []string) (cmds.Request, *cmds.Command, error) { - req, root, cmd, path, err := cmdsCli.Parse(args, Root) +func (i *cmdInvocation) Parse(args []string) error { + var err error - // handle parse error (which means the commandline input was wrong, - // e.g. incorrect number of args, or nonexistent subcommand) + i.req, i.root, i.cmd, i.path, err = cmdsCli.Parse(args, Root) if err != nil { - return nil, nil, handleParseError(req, root, cmd, path, err) + return err } - configPath, err := getConfigRoot(req) + configPath, err := getConfigRoot(i.req) if err != nil { - return nil, nil, err + return err } conf, err := getConfig(configPath) if err != nil { - return nil, nil, err + return err } - ctx := req.Context() + ctx := i.req.Context() ctx.ConfigRoot = configPath ctx.Config = conf // if no encoding was specified by user, default to plaintext encoding // (if command doesn't support plaintext, use JSON instead) - if !req.Option("encoding").Found() { - if req.Command().Marshallers != nil && req.Command().Marshallers[cmds.Text] != nil { - req.SetOption("encoding", cmds.Text) + if !i.req.Option("encoding").Found() { + if i.req.Command().Marshallers != nil && i.req.Command().Marshallers[cmds.Text] != nil { + i.req.SetOption("encoding", cmds.Text) } else { - req.SetOption("encoding", cmds.JSON) + i.req.SetOption("encoding", cmds.JSON) } } - return req, root, nil + return nil } -func handleParseError(req cmds.Request, root *cmds.Command, cmd *cmds.Command, path []string, parseError error) error { - var longHelp, shortHelp bool - - if req != nil { - // help and h are defined in the root. We expect them to be bool. - var err error - longHelp, _, err = req.Option("help").Bool() - if err != nil { - return err - } - shortHelp, _, err = req.Option("h").Bool() - if err != nil { - return err - } - - // override the error to avoid signaling other issues. - parseError = errHelpRequested - } - - // if the -help flag wasn't specified, show the error message - // or if a path was returned (user specified a valid subcommand), show the error message - // (this means there was an option or argument error) - if path != nil && len(path) > 0 { - if !longHelp && !shortHelp { - fmt.Printf(errorFormat, parseError) - } - } - - if cmd == nil { - root = commands.Root - } - - // show the long help text if the -help flag was specified or we are at the root command - // otherwise, show short help text - helpFunc := cmdsCli.ShortHelp - if longHelp || len(path) == 0 { - helpFunc = cmdsCli.LongHelp - } - - htErr := helpFunc("ipfs", root, path, os.Stdout) - if htErr != nil { - fmt.Println(htErr) - } - return parseError -} - -func handleHelpOption(req cmds.Request, root *cmds.Command) (helpTextDisplayed bool, err error) { - longHelp, _, err := req.Option("help").Bool() +func (i *cmdInvocation) requestedHelp() (short bool, long bool, err error) { + longHelp, _, err := i.req.Option("help").Bool() if err != nil { - return false, err + return false, false, err } - shortHelp, _, err := req.Option("h").Bool() + shortHelp, _, err := i.req.Option("h").Bool() if err != nil { - return false, err + return false, false, err } - if !longHelp && !shortHelp { - return false, nil - } - helpFunc := cmdsCli.ShortHelp - if longHelp || len(req.Path()) == 0 { - helpFunc = cmdsCli.LongHelp - } - - err = helpFunc("ipfs", root, req.Path(), os.Stdout) - if err != nil { - return false, err - } - return true, nil + return longHelp, shortHelp, nil } func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { var res cmds.Response - if root == Root { // TODO explain what it means when root == Root + // TODO explain what it means when root == Root + // @mappum o/ + if root == Root { res = root.Call(req) } else { @@ -247,35 +242,20 @@ func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { return res, nil } -func outputResponse(res cmds.Response, root *cmds.Command) error { - if res.Error() != nil { - fmt.Printf(errorFormat, res.Error().Error()) - - if res.Error().Code != cmds.ErrClient { - // TODO does ErrClient mean "error the client should see" or "this - // is an error caused by the user ie. user error"? - return res.Error() - } - - // if this is a client error, we try to display help text - if res.Error().Code == cmds.ErrClient { - err := cmdsCli.ShortHelp("ipfs", root, res.Request().Path(), os.Stdout) - if err != nil { - fmt.Println(err) - } - } - - emptyErr := errors.New("") // already displayed error text - return emptyErr +func isClientError(err error) bool { + // cast to cmds.Error + cmdErr, ok := err.(*cmds.Error) + if !ok { + return false } - out, err := res.Reader() - if err != nil { - return err + // here we handle the case where commands with + // no Run func are invoked directly. As help requests. + if err == cmds.ErrNotCallable { + return true } - io.Copy(os.Stdout, out) - return nil + return cmdErr.Code == cmds.ErrClient } func getConfigRoot(req cmds.Request) (string, error) { diff --git a/commands/cli/parse.go b/commands/cli/parse.go index 3c0f6b586..c90c6f86a 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -9,6 +9,9 @@ import ( cmds "github.com/jbenet/go-ipfs/commands" ) +// ErrInvalidSubcmd signals when the parse error is not found +var ErrInvalidSubcmd = errors.New("subcommand not found") + // Parse parses the input commandline string (cmd, flags, and args). // returns the corresponding command Request object. // Multiple root commands are supported: @@ -41,7 +44,7 @@ func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, } if maxLength == 0 { - return nil, root, nil, path, errors.New("Not a valid subcommand") + return nil, root, nil, path, ErrInvalidSubcmd } args, err := parseArgs(stringArgs, cmd) From c46d4c8953cc632d7add162579cd00bbf8f7933b Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 12 Nov 2014 17:51:57 -0800 Subject: [PATCH 305/383] cmd2 fixed config panic The way the current marshallers marshal out output requires a ton of error checking. I wish there was a way to have the library call our marshaller with the right type (rather than an interface). Maybe can do this with Reflect someday. --- core/commands2/config.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/core/commands2/config.go b/core/commands2/config.go index d49d73c7a..2b26f509a 100644 --- a/core/commands2/config.go +++ b/core/commands2/config.go @@ -90,8 +90,17 @@ Set the value of the 'datastore.path' key: return nil, nil // dont output anything } - v := res.Output().(*ConfigField) - buf, err := config.HumanOutput(v.Value) + v := res.Output() + if v == nil { + k := res.Request().Arguments()[0] + return nil, fmt.Errorf("config does not contain key: %s", k) + } + vf, ok := v.(*ConfigField) + if !ok { + return nil, u.ErrCast() + } + + buf, err := config.HumanOutput(vf.Value) if err != nil { return nil, err } From 3352aeee15e2c2e703e89c639bc548e003cd74be Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 12 Nov 2014 19:21:53 -0800 Subject: [PATCH 306/383] cmds2: fixed show help on root + noncallable --- cmd/ipfs2/ipfs.go | 4 ++-- cmd/ipfs2/main.go | 23 ++++++++++++----------- commands/cli/helptext.go | 5 +++++ commands/cli/parse.go | 30 ++++++------------------------ core/commands2/root.go | 8 +++++--- 5 files changed, 30 insertions(+), 40 deletions(-) diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go index 5e8dcff68..d2286e408 100644 --- a/cmd/ipfs2/ipfs.go +++ b/cmd/ipfs2/ipfs.go @@ -6,8 +6,8 @@ import ( ) var Root = &cmds.Command{ - Options: commands.Root.Options, - Help: commands.Root.Help, + Options: commands.Root.Options, + Helptext: commands.Root.Helptext, } var rootSubcommands = map[string]*cmds.Command{ diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 404c8bc2e..565de9b37 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -68,7 +68,7 @@ func main() { } // parse the commandline into a command invocation - err = invoc.Parse(os.Args[1:]) + parseErr := invoc.Parse(os.Args[1:]) // BEFORE handling the parse error, if we have enough information // AND the user requested help, print it out and exit @@ -84,10 +84,18 @@ func main() { } } - // ok now handle handle parse error (which means cli input was wrong, + // here we handle the cases where + // - commands with no Run func are invoked directly. + // - the main command is invoked. + if invoc.cmd == nil || invoc.cmd.Run == nil { + printHelp(false) + os.Exit(0) + } + + // ok now handle parse error (which means cli input was wrong, // e.g. incorrect number of args, or nonexistent subcommand) - if err != nil { - printErr(err) + if parseErr != nil { + printErr(parseErr) // this was a user error, print help. if invoc.cmd != nil { @@ -248,13 +256,6 @@ func isClientError(err error) bool { if !ok { return false } - - // here we handle the case where commands with - // no Run func are invoked directly. As help requests. - if err == cmds.ErrNotCallable { - return true - } - return cmdErr.Code == cmds.ErrClient } diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index a7c8e378f..2a305fb4a 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -195,6 +195,11 @@ func ShortHelp(rootName string, root *cmds.Command, path []string, out io.Writer return err } + // default cmd to root if there is no path + if path == nil && cmd == nil { + cmd = root + } + pathStr := rootName if len(path) > 0 { pathStr += " " + strings.Join(path, " ") diff --git a/commands/cli/parse.go b/commands/cli/parse.go index c90c6f86a..8e1dba86f 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -14,36 +14,18 @@ var ErrInvalidSubcmd = errors.New("subcommand not found") // Parse parses the input commandline string (cmd, flags, and args). // returns the corresponding command Request object. -// Multiple root commands are supported: // Parse will search each root to find the one that best matches the requested subcommand. // TODO: get rid of extraneous return values (e.g. we ended up not needing the root value anymore) // TODO: get rid of multiple-root support, we should only need one now -func Parse(input []string, roots ...*cmds.Command) (cmds.Request, *cmds.Command, *cmds.Command, []string, error) { - var root, cmd *cmds.Command - var path, stringArgs []string - var opts map[string]interface{} - +func Parse(input []string, root *cmds.Command) (cmds.Request, *cmds.Command, *cmds.Command, []string, error) { // use the root that matches the longest path (most accurately matches request) - maxLength := 0 - for _, root2 := range roots { - path2, input2, cmd2 := parsePath(input, root2) - opts2, stringArgs2, err := parseOptions(input2) - if err != nil { - return nil, root, cmd2, path2, err - } - - length := len(path2) - if length > maxLength { - maxLength = length - root = root2 - path = path2 - cmd = cmd2 - opts = opts2 - stringArgs = stringArgs2 - } + path, input, cmd := parsePath(input, root) + opts, stringArgs, err := parseOptions(input) + if err != nil { + return nil, root, cmd, path, err } - if maxLength == 0 { + if len(path) == 0 { return nil, root, nil, path, ErrInvalidSubcmd } diff --git a/core/commands2/root.go b/core/commands2/root.go index 29bce74c9..d2372dcc5 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -13,8 +13,10 @@ type TestOutput struct { } var Root = &cmds.Command{ - Description: "Global P2P Merkle-DAG filesystem", - SubcommandHelp: `Basic commands: + Helptext: cmds.HelpText{ + Tagline: "Global P2P Merkle-DAG filesystem", + ShortDescription: ` +Basic commands: init Initialize ipfs local configurationx add Add an object to ipfs @@ -39,7 +41,7 @@ Plumbing commands: block Interact with raw blocks in the datastore object Interact with raw dag nodes `, - + }, Options: []cmds.Option{ cmds.StringOption("config", "c", "Path to the configuration file to use"), cmds.BoolOption("debug", "D", "Operate in debug mode"), From 9eccce1f93906cc2a75ffcd9f7cdb112874a26ab Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 19:20:42 -0800 Subject: [PATCH 307/383] fix(2/diag) match ipfs output License: MIT Signed-off-by: Brian Tiger Chow --- core/commands2/diag.go | 81 +++++++++++++++++++++++++++++------------- diagnostics/diag.go | 1 + 2 files changed, 57 insertions(+), 25 deletions(-) diff --git a/core/commands2/diag.go b/core/commands2/diag.go index 82da78891..aba89c8fd 100644 --- a/core/commands2/diag.go +++ b/core/commands2/diag.go @@ -1,25 +1,27 @@ package commands import ( - "fmt" + "bytes" "io" + "text/template" "time" cmds "github.com/jbenet/go-ipfs/commands" - diagn "github.com/jbenet/go-ipfs/diagnostics" + util "github.com/jbenet/go-ipfs/util" ) type DiagnosticConnection struct { - ID string - Latency int64 + ID string + // TODO use milliseconds or microseconds for human readability + NanosecondsLatency uint64 } type DiagnosticPeer struct { - ID string - LifeSpan float64 - BandwidthIn uint64 - BandwidthOut uint64 - Connections []DiagnosticConnection + ID string + UptimeSeconds uint64 + BandwidthBytesIn uint64 + BandwidthBytesOut uint64 + Connections []DiagnosticConnection } type DiagnosticOutput struct { @@ -58,33 +60,62 @@ connected peers and latencies between them. connections := make([]DiagnosticConnection, len(peer.Connections)) for j, conn := range peer.Connections { connections[j] = DiagnosticConnection{ - ID: conn.ID, - Latency: conn.Latency.Nanoseconds(), + ID: conn.ID, + NanosecondsLatency: uint64(conn.Latency.Nanoseconds()), } } output[i] = DiagnosticPeer{ - ID: peer.ID, - LifeSpan: peer.LifeSpan.Minutes(), - BandwidthIn: peer.BwIn, - BandwidthOut: peer.BwOut, - Connections: connections, + ID: peer.ID, + UptimeSeconds: uint64(peer.LifeSpan.Seconds()), + BandwidthBytesIn: peer.BwIn, + BandwidthBytesOut: peer.BwOut, + Connections: connections, } } return &DiagnosticOutput{output}, nil }, Type: &DiagnosticOutput{}, + Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + cmds.Text: func(r cmds.Response) ([]byte, error) { + output, ok := r.Output().(*DiagnosticOutput) + if !ok { + return nil, util.ErrCast() + } + var buf bytes.Buffer + err := printDiagnostics(&buf, output) + if err != nil { + return nil, err + } + return buf.Bytes(), nil + }, + }, } -func PrintDiagnostics(info []*diagn.DiagInfo, out io.Writer) { - for _, i := range info { - fmt.Fprintf(out, "Peer: %s\n", i.ID) - fmt.Fprintf(out, "\tUp for: %s\n", i.LifeSpan.String()) - fmt.Fprintf(out, "\tConnected To:\n") - for _, c := range i.Connections { - fmt.Fprintf(out, "\t%s\n\t\tLatency = %s\n", c.ID, c.Latency.String()) - } - fmt.Fprintln(out) +func printDiagnostics(out io.Writer, info *DiagnosticOutput) error { + + diagTmpl := ` +{{ range $peer := .Peers }} +ID {{ $peer.ID }} + up {{ $peer.UptimeSeconds }} seconds + connected to {{ len .Connections }}... + {{ range $connection := .Connections }} + ID {{ $connection.ID }} + latency: {{ $connection.NanosecondsLatency }} ns + {{ end }} +{{end}} +` + + templ, err := template.New("DiagnosticOutput").Parse(diagTmpl) + if err != nil { + return err } + + err = templ.Execute(out, info) + if err != nil { + return err + } + + return nil } diff --git a/diagnostics/diag.go b/diagnostics/diag.go index f0f0678b2..f31fd03d4 100644 --- a/diagnostics/diag.go +++ b/diagnostics/diag.go @@ -67,6 +67,7 @@ type DiagInfo struct { Keys []string // How long this node has been running for + // TODO rename Uptime LifeSpan time.Duration // Incoming Bandwidth Usage From d18902df27d5b0e1c1b2e562a409b80185797897 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 12 Nov 2014 22:37:02 -0800 Subject: [PATCH 308/383] cmds2: changed how the ctx.Node works --- cmd/ipfs2/daemon.go | 21 ++++++--- cmd/ipfs2/main.go | 84 ++++++++++++++++++++---------------- cmd/ipfs2/tour.go | 25 ++++++++--- commands/request.go | 40 ++++++++++++++++- core/commands2/add.go | 5 ++- core/commands2/block.go | 10 ++++- core/commands2/bootstrap.go | 21 +++++++-- core/commands2/cat.go | 6 ++- core/commands2/diag.go | 5 ++- core/commands2/ls.go | 5 ++- core/commands2/mount_unix.go | 22 +++++++--- core/commands2/object.go | 20 +++++++-- core/commands2/pin.go | 10 ++++- core/commands2/publish.go | 5 ++- core/commands2/refs.go | 5 ++- core/commands2/resolve.go | 6 ++- core/commands2/update.go | 15 +++++-- 17 files changed, 225 insertions(+), 80 deletions(-) diff --git a/cmd/ipfs2/daemon.go b/cmd/ipfs2/daemon.go index 4b7d0b95d..bd5d72076 100644 --- a/cmd/ipfs2/daemon.go +++ b/cmd/ipfs2/daemon.go @@ -22,21 +22,28 @@ var daemonCmd = &cmds.Command{ } func daemonFunc(req cmds.Request) (interface{}, error) { - ctx := req.Context() - - lock, err := daemon.Lock(ctx.ConfigRoot) + lock, err := daemon.Lock(req.Context().ConfigRoot) if err != nil { return nil, fmt.Errorf("Couldn't obtain lock. Is another daemon already running?") } defer lock.Close() - node, err := core.NewIpfsNode(ctx.Config, true) + cfg, err := req.Context().GetConfig() if err != nil { return nil, err } - ctx.Node = node - addr, err := ma.NewMultiaddr(ctx.Config.Addresses.API) + // setup function that constructs the context. we have to do it this way + // to play along with how the Context works and thus not expose its internals + req.Context().ConstructNode = func() (*core.IpfsNode, error) { + return core.NewIpfsNode(cfg, true) + } + node, err := req.Context().GetNode() + if err != nil { + return nil, err + } + + addr, err := ma.NewMultiaddr(cfg.Addresses.API) if err != nil { return nil, err } @@ -46,7 +53,7 @@ func daemonFunc(req cmds.Request) (interface{}, error) { return nil, err } - cmdHandler := cmdsHttp.NewHandler(*ctx, commands.Root) + cmdHandler := cmdsHttp.NewHandler(*req.Context(), commands.Root) http.Handle(cmdsHttp.ApiPath+"/", cmdHandler) ifpsHandler := &ipfsHandler{node} diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 565de9b37..d24c422e4 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -166,13 +166,10 @@ func (i *cmdInvocation) Parse(args []string) error { return err } - conf, err := getConfig(configPath) - if err != nil { - return err - } + // this sets up the function that will initialize the config lazily. ctx := i.req.Context() ctx.ConfigRoot = configPath - ctx.Config = conf + ctx.LoadConfig = loadConfig // if no encoding was specified by user, default to plaintext encoding // (if command doesn't support plaintext, use JSON instead) @@ -202,51 +199,62 @@ func (i *cmdInvocation) requestedHelp() (short bool, long bool, err error) { func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { var res cmds.Response - // TODO explain what it means when root == Root - // @mappum o/ - if root == Root { - res = root.Call(req) + local, found, err := req.Option("local").Bool() + if err != nil { + return nil, err + } + remote := !found || !local - } else { - local, found, err := req.Option("local").Bool() + log.Info("Checking if daemon is running...") + if remote && daemon.Locked(req.Context().ConfigRoot) { + + cfg, err := req.Context().GetConfig() if err != nil { return nil, err } - remote := !found || !local + addr, err := ma.NewMultiaddr(cfg.Addresses.API) + if err != nil { + return nil, err + } - log.Info("Checking if daemon is running...") - if remote && daemon.Locked(req.Context().ConfigRoot) { - addr, err := ma.NewMultiaddr(req.Context().Config.Addresses.API) + _, host, err := manet.DialArgs(addr) + if err != nil { + return nil, err + } + + client := cmdsHttp.NewClient(host) + + res, err = client.Send(req) + if err != nil { + return nil, err + } + + } else { + log.Info("Executing command locally: daemon not running") + + // this sets up the function that will initialize the node + // this is so that we can construct the node lazily. + ctx := req.Context() + ctx.ConstructNode = func() (*core.IpfsNode, error) { + cfg, err := ctx.GetConfig() if err != nil { return nil, err } + return core.NewIpfsNode(cfg, false) + } - _, host, err := manet.DialArgs(addr) - if err != nil { - return nil, err - } + // Okay!!!!! NOW we can call the command. + res = root.Call(req) - client := cmdsHttp.NewClient(host) - - res, err = client.Send(req) - if err != nil { - return nil, err - } - - } else { - log.Info("Executing command locally: daemon not running") - node, err := core.NewIpfsNode(req.Context().Config, false) - if err != nil { - return nil, err - } - defer node.Close() - req.Context().Node = node - - res = root.Call(req) + // let's not forget teardown. If a node was initialized, we must close it. + // Note that this means the underlying req.Context().Node variable is exposed. + // this is gross, and should be changed when we extract out the exec Context. + node := req.Context().NodeWithoutConstructing() + if node != nil { + node.Close() } } - return res, nil } @@ -275,7 +283,7 @@ func getConfigRoot(req cmds.Request) (string, error) { return configPath, nil } -func getConfig(path string) (*config.Config, error) { +func loadConfig(path string) (*config.Config, error) { configFile, err := config.Filename(path) if err != nil { return nil, err diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index 9d3e1247b..aef426a2a 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -32,7 +32,11 @@ IPFS very quickly. To start, run: Run: func(req cmds.Request) (interface{}, error) { out := new(bytes.Buffer) - cfg := req.Context().Config + cfg, err := req.Context().GetConfig() + if err != nil { + return nil, err + } + strs, err := internal.CastToStrings(req.Arguments()) if err != nil { return nil, err @@ -57,8 +61,11 @@ var cmdIpfsTourNext = &cmds.Command{ Run: func(req cmds.Request) (interface{}, error) { var w bytes.Buffer - cfg := req.Context().Config path := req.Context().ConfigRoot + cfg, err := req.Context().GetConfig() + if err != nil { + return nil, err + } topic := tour.NextTopic(tour.TopicID(cfg.Tour.Last)) if err := tourShow(&w, topic); err != nil { @@ -84,10 +91,13 @@ var cmdIpfsTourRestart = &cmds.Command{ Run: func(req cmds.Request) (interface{}, error) { path := req.Context().ConfigRoot - cfg := req.Context().Config + cfg, err := req.Context().GetConfig() + if err != nil { + return nil, err + } cfg.Tour.Last = "" - err := writeConfig(path, cfg) + err = writeConfig(path, cfg) if err != nil { return nil, err } @@ -99,8 +109,13 @@ var cmdIpfsTourList = &cmds.Command{ Description: "Show a list of IPFS Tour topics", Run: func(req cmds.Request) (interface{}, error) { + cfg, err := req.Context().GetConfig() + if err != nil { + return nil, err + } + var w bytes.Buffer - tourListCmd(&w, req.Context().Config) + tourListCmd(&w, cfg) w.WriteTo(os.Stdout) // TODO use res.SetOutput(output) return nil, nil }, diff --git a/commands/request.go b/commands/request.go index 428a8ad2c..6e8844682 100644 --- a/commands/request.go +++ b/commands/request.go @@ -15,8 +15,44 @@ type optMap map[string]interface{} type Context struct { ConfigRoot string - Config *config.Config - Node *core.IpfsNode + + config *config.Config + LoadConfig func(path string) (*config.Config, error) + + node *core.IpfsNode + ConstructNode func() (*core.IpfsNode, error) +} + +// GetConfig returns the config of the current Command exection +// context. It may load it with the providied function. +func (c *Context) GetConfig() (*config.Config, error) { + var err error + if c.config == nil { + if c.LoadConfig == nil { + panic("nil LoadConfig function") + } + c.config, err = c.LoadConfig(c.ConfigRoot) + } + return c.config, err +} + +// GetNode returns the node of the current Command exection +// context. It may construct it with the providied function. +func (c *Context) GetNode() (*core.IpfsNode, error) { + var err error + if c.node == nil { + if c.ConstructNode == nil { + panic("nil ConstructNode function") + } + c.node, err = c.ConstructNode() + } + return c.node, err +} + +// NodeWithoutConstructing returns the underlying node variable +// so that clients may close it. +func (c *Context) NodeWithoutConstructing() *core.IpfsNode { + return c.node } // Request represents a call to a command from a consumer diff --git a/core/commands2/add.go b/core/commands2/add.go index ddffdd2aa..c2606a00e 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -43,7 +43,10 @@ remains to be implemented. `, Run: func(req cmds.Request) (interface{}, error) { added := &AddOutput{} - n := req.Context().Node + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } recursive, _, err := req.Option("r").Bool() if err != nil { diff --git a/core/commands2/block.go b/core/commands2/block.go index 7b0d1e175..ece378911 100644 --- a/core/commands2/block.go +++ b/core/commands2/block.go @@ -40,7 +40,10 @@ It outputs to stdout, and is a base58 encoded multihash.`, cmds.StringArg("key", true, false, "The base58 multihash of an existing block to get"), }, Run: func(req cmds.Request) (interface{}, error) { - n := req.Context().Node + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } key, ok := req.Arguments()[0].(string) if !ok { @@ -76,7 +79,10 @@ It reads from stdin, and is a base58 encoded multihash.`, cmds.FileArg("data", true, false, "The data to be stored as an IPFS block"), }, Run: func(req cmds.Request) (interface{}, error) { - n := req.Context().Node + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } in, ok := req.Arguments()[0].(io.Reader) if !ok { diff --git a/core/commands2/bootstrap.go b/core/commands2/bootstrap.go index 2f88b34fc..a222fbdb5 100644 --- a/core/commands2/bootstrap.go +++ b/core/commands2/bootstrap.go @@ -54,7 +54,12 @@ in the bootstrap list). return nil, err } - added, err := bootstrapAdd(filename, req.Context().Config, input) + cfg, err := req.Context().GetConfig() + if err != nil { + return nil, err + } + + added, err := bootstrapAdd(filename, cfg, input) if err != nil { return nil, err } @@ -94,7 +99,12 @@ var bootstrapRemoveCmd = &cmds.Command{ return nil, err } - removed, err := bootstrapRemove(filename, req.Context().Config, input) + cfg, err := req.Context().GetConfig() + if err != nil { + return nil, err + } + + removed, err := bootstrapRemove(filename, cfg, input) if err != nil { return nil, err } @@ -121,7 +131,12 @@ var bootstrapListCmd = &cmds.Command{ `, Run: func(req cmds.Request) (interface{}, error) { - peers := req.Context().Config.Bootstrap + cfg, err := req.Context().GetConfig() + if err != nil { + return nil, err + } + + peers := cfg.Bootstrap return &BootstrapOutput{peers}, nil }, Type: &BootstrapOutput{}, diff --git a/core/commands2/cat.go b/core/commands2/cat.go index 89384fe55..5c7080a90 100644 --- a/core/commands2/cat.go +++ b/core/commands2/cat.go @@ -19,7 +19,11 @@ it contains. cmds.StringArg("ipfs-path", true, true, "The path to the IPFS object(s) to be outputted"), }, Run: func(req cmds.Request) (interface{}, error) { - node := req.Context().Node + node, err := req.Context().GetNode() + if err != nil { + return nil, err + } + readers := make([]io.Reader, 0, len(req.Arguments())) paths, err := internal.CastToStrings(req.Arguments()) diff --git a/core/commands2/diag.go b/core/commands2/diag.go index aba89c8fd..caace1e91 100644 --- a/core/commands2/diag.go +++ b/core/commands2/diag.go @@ -44,7 +44,10 @@ connected peers and latencies between them. `, Run: func(req cmds.Request) (interface{}, error) { - n := req.Context().Node + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } if !n.OnlineMode() { return nil, errNotOnline diff --git a/core/commands2/ls.go b/core/commands2/ls.go index 0850178a5..bef59251f 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -34,7 +34,10 @@ it contains, with the following format: cmds.StringArg("ipfs-path", false, true, "The path to the IPFS object(s) to list links from"), }, Run: func(req cmds.Request) (interface{}, error) { - node := req.Context().Node + node, err := req.Context().GetNode() + if err != nil { + return nil, err + } paths, err := internal.CastToStrings(req.Arguments()) if err != nil { diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go index a4b57cfbe..786b09c4c 100644 --- a/core/commands2/mount_unix.go +++ b/core/commands2/mount_unix.go @@ -32,10 +32,18 @@ not be listable, as it is virtual. Accessing known paths directly. cmds.StringOption("n", "The path where IPNS should be mounted\n(default is '/ipns')"), }, Run: func(req cmds.Request) (interface{}, error) { - ctx := req.Context() + cfg, err := req.Context().GetConfig() + if err != nil { + return nil, err + } + + node, err := req.Context().GetNode() + if err != nil { + return nil, err + } // error if we aren't running node in online mode - if ctx.Node.Network == nil { + if node.Network == nil { return nil, errNotOnline } @@ -48,9 +56,9 @@ not be listable, as it is virtual. Accessing known paths directly. return nil, err } if !found { - fsdir = ctx.Config.Mounts.IPFS // use default value + fsdir = cfg.Mounts.IPFS // use default value } - fsdone := mountIpfs(ctx.Node, fsdir) + fsdone := mountIpfs(node, fsdir) // get default mount points nsdir, found, err := req.Option("n").String() @@ -58,10 +66,10 @@ not be listable, as it is virtual. Accessing known paths directly. return nil, err } if !found { - nsdir = ctx.Config.Mounts.IPNS // NB: be sure to not redeclare! + nsdir = cfg.Mounts.IPNS // NB: be sure to not redeclare! } - nsdone := mountIpns(ctx.Node, nsdir, fsdir) + nsdone := mountIpns(node, nsdir, fsdir) // wait until mounts return an error (or timeout if successful) select { @@ -72,7 +80,7 @@ not be listable, as it is virtual. Accessing known paths directly. // mounted successfully, we timed out with no errors case <-time.After(mountTimeout): - output := ctx.Config.Mounts + output := cfg.Mounts return &output, nil } }, diff --git a/core/commands2/object.go b/core/commands2/object.go index 6b43248fe..90508ed2b 100644 --- a/core/commands2/object.go +++ b/core/commands2/object.go @@ -48,7 +48,10 @@ output is the raw data of the object. cmds.StringArg("key", true, false, "Key of the object to retrieve, in base58-encoded multihash format"), }, Run: func(req cmds.Request) (interface{}, error) { - n := req.Context().Node + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } key, ok := req.Arguments()[0].(string) if !ok { @@ -68,7 +71,10 @@ It outputs to stdout, and is a base58 encoded multihash.`, cmds.StringArg("key", true, false, "Key of the object to retrieve, in base58-encoded multihash format"), }, Run: func(req cmds.Request) (interface{}, error) { - n := req.Context().Node + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } key, ok := req.Arguments()[0].(string) if !ok { @@ -96,7 +102,10 @@ This command outputs data in the following encodings: cmds.StringArg("key", true, false, "Key of the object to retrieve\n(in base58-encoded multihash format)"), }, Run: func(req cmds.Request) (interface{}, error) { - n := req.Context().Node + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } key, ok := req.Arguments()[0].(string) if !ok { @@ -148,7 +157,10 @@ Data should be in the format specified by . cmds.StringArg("encoding", true, false, "Encoding type of , either \"protobuf\" or \"json\""), }, Run: func(req cmds.Request) (interface{}, error) { - n := req.Context().Node + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } input, ok := req.Arguments()[0].(io.Reader) if !ok { diff --git a/core/commands2/pin.go b/core/commands2/pin.go index 3ea467b42..93d8696e1 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -31,7 +31,10 @@ on disk. cmds.BoolOption("recursive", "r", "Recursively pin the object linked to by the specified object(s)"), }, Run: func(req cmds.Request) (interface{}, error) { - n := req.Context().Node + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } // set recursive flag recursive, found, err := req.Option("recursive").Bool() @@ -70,7 +73,10 @@ collected if needed. cmds.BoolOption("recursive", "r", "Recursively unpin the object linked to by the specified object(s)"), }, Run: func(req cmds.Request) (interface{}, error) { - n := req.Context().Node + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } // set recursive flag recursive, found, err := req.Option("recursive").Bool() diff --git a/core/commands2/publish.go b/core/commands2/publish.go index 68c5d71c2..f0d29d478 100644 --- a/core/commands2/publish.go +++ b/core/commands2/publish.go @@ -39,8 +39,11 @@ Publish a to another public key: }, Run: func(req cmds.Request) (interface{}, error) { log.Debug("Begin Publish") + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } - n := req.Context().Node args := req.Arguments() if n.Network == nil { diff --git a/core/commands2/refs.go b/core/commands2/refs.go index 4b50928d2..a6e369257 100644 --- a/core/commands2/refs.go +++ b/core/commands2/refs.go @@ -32,7 +32,10 @@ Note: list all refs recursively with -r.`, cmds.BoolOption("recursive", "r", "Recursively list links of child nodes"), }, Run: func(req cmds.Request) (interface{}, error) { - n := req.Context().Node + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } unique, found, err := req.Option("unique").Bool() if err != nil { diff --git a/core/commands2/resolve.go b/core/commands2/resolve.go index 636f36faa..323790df3 100644 --- a/core/commands2/resolve.go +++ b/core/commands2/resolve.go @@ -33,7 +33,11 @@ Resolve te value of another name: }, Run: func(req cmds.Request) (interface{}, error) { - n := req.Context().Node + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } + var name string if n.Network == nil { diff --git a/core/commands2/update.go b/core/commands2/update.go index 881cddd89..07a0f8d03 100644 --- a/core/commands2/update.go +++ b/core/commands2/update.go @@ -20,7 +20,10 @@ var updateCmd = &cmds.Command{ `, Run: func(req cmds.Request) (interface{}, error) { - n := req.Context().Node + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } return updateApply(n) }, Type: &UpdateOutput{}, @@ -51,7 +54,10 @@ Nothing will be downloaded or installed. `, Run: func(req cmds.Request) (interface{}, error) { - n := req.Context().Node + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } return updateCheck(n) }, Type: &UpdateOutput{}, @@ -76,7 +82,10 @@ var updateLogCmd = &cmds.Command{ `, Run: func(req cmds.Request) (interface{}, error) { - n := req.Context().Node + n, err := req.Context().GetNode() + if err != nil { + return nil, err + } return updateLog(n) }, } From 7daf888902b854a7144154851ad3f54243df5351 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 12 Nov 2014 22:48:25 -0800 Subject: [PATCH 309/383] cmds2: handle error return codes --- cmd/ipfs2/main.go | 4 ++++ commands/response.go | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index d24c422e4..2ccef1645 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -150,6 +150,10 @@ func (i *cmdInvocation) Run() (output io.Reader, err error) { return nil, err } + if err := res.Error(); err != nil { + return nil, err + } + return res.Reader() } diff --git a/commands/response.go b/commands/response.go index d4c19d10e..74b6b5e07 100644 --- a/commands/response.go +++ b/commands/response.go @@ -117,6 +117,11 @@ func (r *response) Marshal() ([]byte, error) { } encType := EncodingType(strings.ToLower(enc)) + // Special case: if text encoding and an error, just print it out. + if encType == Text && r.Error() != nil { + return []byte(r.Error().Error()), nil + } + var marshaller Marshaller if r.req.Command() != nil && r.req.Command().Marshallers != nil { marshaller = r.req.Command().Marshallers[encType] From 1342575d242198ff672160b67c4c454c6f45fbba Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Wed, 12 Nov 2014 22:49:15 -0800 Subject: [PATCH 310/383] cmds2/commands: better sorting --- core/commands2/commands.go | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/core/commands2/commands.go b/core/commands2/commands.go index b5542976f..8ce2b0e06 100644 --- a/core/commands2/commands.go +++ b/core/commands2/commands.go @@ -1,7 +1,8 @@ package commands import ( - "fmt" + "bytes" + "sort" cmds "github.com/jbenet/go-ipfs/commands" ) @@ -21,21 +22,24 @@ func CommandsCmd(root *cmds.Command) *cmds.Command { }, Run: func(req cmds.Request) (interface{}, error) { - root := outputCommand("ipfs", root) + root := cmd2outputCmd("ipfs", root) return &root, nil }, Marshallers: map[cmds.EncodingType]cmds.Marshaller{ cmds.Text: func(res cmds.Response) ([]byte, error) { v := res.Output().(*Command) - s := formatCommand("", v) - return []byte(s), nil + var buf bytes.Buffer + for _, s := range cmdPathStrings(v) { + buf.Write([]byte(s + "\n")) + } + return buf.Bytes(), nil }, }, Type: &Command{}, } } -func outputCommand(name string, cmd *cmds.Command) Command { +func cmd2outputCmd(name string, cmd *cmds.Command) Command { output := Command{ Name: name, Subcommands: make([]Command, len(cmd.Subcommands)), @@ -43,23 +47,25 @@ func outputCommand(name string, cmd *cmds.Command) Command { i := 0 for name, sub := range cmd.Subcommands { - output.Subcommands[i] = outputCommand(name, sub) + output.Subcommands[i] = cmd2outputCmd(name, sub) i++ } return output } -func formatCommand(prefix string, cmd *Command) string { - if len(prefix) > 0 { - prefix += " " - } - s := fmt.Sprintf("%s%s\n", prefix, cmd.Name) +func cmdPathStrings(cmd *Command) []string { + var cmds []string - prefix += cmd.Name - for _, sub := range cmd.Subcommands { - s += formatCommand(prefix, &sub) + var recurse func(prefix string, cmd *Command) + recurse = func(prefix string, cmd *Command) { + cmds = append(cmds, prefix+cmd.Name) + for _, sub := range cmd.Subcommands { + recurse(prefix+cmd.Name+" ", &sub) + } } - return s + recurse("", cmd) + sort.Sort(sort.StringSlice(cmds)) + return cmds } From 2fd8f39c65831af799a11655660819d4c17679f5 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 19:13:42 -0800 Subject: [PATCH 311/383] cmd/ipfs2: Added comments to explain purpose of CLI root --- cmd/ipfs2/ipfs.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go index d2286e408..5eccefd2c 100644 --- a/cmd/ipfs2/ipfs.go +++ b/cmd/ipfs2/ipfs.go @@ -5,12 +5,17 @@ import ( commands "github.com/jbenet/go-ipfs/core/commands2" ) +// This is the CLI root, used for executing commands accessible to CLI clients. +// Some subcommands (like 'ipfs daemon' or 'ipfs init') are only accessible here, +// and can't be called through the HTTP API. var Root = &cmds.Command{ Options: commands.Root.Options, Helptext: commands.Root.Helptext, } -var rootSubcommands = map[string]*cmds.Command{ +// Commands in localCommands should always be run locally (even if daemon is running). +// They can override subcommands in commands.Root by defining a subcommand with the same name. +var localCommands = map[string]*cmds.Command{ "daemon": daemonCmd, // TODO name "init": initCmd, // TODO name "tour": cmdTour, @@ -20,7 +25,7 @@ var rootSubcommands = map[string]*cmds.Command{ func init() { // setting here instead of in literal to prevent initialization loop // (some commands make references to Root) - Root.Subcommands = rootSubcommands + Root.Subcommands = localCommands // copy all subcommands from commands.Root into this root (if they aren't already present) for k, v := range commands.Root.Subcommands { From 2dd6f24157379c26732ccf1835b8a299a33c1217 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 20:40:09 -0800 Subject: [PATCH 312/383] core/commands2: Refactored some commands to HelpText struct for helptext fields --- cmd/ipfs2/tour.go | 7 ++++-- core/commands2/name.go | 12 ++++++++-- core/commands2/object.go | 46 +++++++++++++++++++++++++++++---------- core/commands2/pin.go | 14 ++++++++---- core/commands2/refs.go | 10 ++++++--- core/commands2/resolve.go | 12 ++++++++-- core/commands2/update.go | 21 +++++++++++------- core/commands2/version.go | 7 +++--- 8 files changed, 94 insertions(+), 35 deletions(-) diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index aef426a2a..b3f55ebd2 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -13,13 +13,16 @@ import ( ) var cmdTour = &cmds.Command{ - Description: "An introduction to IPFS", - Help: `This is a tour that takes you through various IPFS concepts, + Helptext: cmds.HelpText{ + Tagline: "An introduction to IPFS", + ShortDescription: ` +This is a tour that takes you through various IPFS concepts, features, and tools to make sure you get up to speed with IPFS very quickly. To start, run: ipfs tour `, + }, Arguments: []cmds.Argument{ cmds.StringArg("number", false, false, "The number of the topic you would like to tour"), diff --git a/core/commands2/name.go b/core/commands2/name.go index 33971d9ca..8b30d5c15 100644 --- a/core/commands2/name.go +++ b/core/commands2/name.go @@ -8,8 +8,15 @@ type IpnsEntry struct { } var nameCmd = &cmds.Command{ - Description: "IPFS namespace (IPNS) tool", - Help: `IPNS is a PKI namespace, where names are the hashes of public keys, and + Helptext: cmds.HelpText{ + Tagline: "IPFS namespace (IPNS) tool", + ShortDescription: ` +IPNS is a PKI namespace, where names are the hashes of public keys, and +the private key enables publishing new (signed) values. In both publish +and resolve, the default value of is your own identity public key. +`, + LongDescription: ` +IPNS is a PKI namespace, where names are the hashes of public keys, and the private key enables publishing new (signed) values. In both publish and resolve, the default value of is your own identity public key. @@ -37,6 +44,7 @@ Resolve the value of another name: QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy `, + }, Subcommands: map[string]*cmds.Command{ "publish": publishCmd, diff --git a/core/commands2/object.go b/core/commands2/object.go index 90508ed2b..dc5be0a0f 100644 --- a/core/commands2/object.go +++ b/core/commands2/object.go @@ -24,8 +24,10 @@ type Node struct { } var objectCmd = &cmds.Command{ - Description: "Interact with ipfs objects", - Help: `'ipfs object' is a plumbing command used to manipulate DAG objects directly.`, + Helptext: cmds.HelpText{ + Tagline: "Interact with ipfs objects", + ShortDescription: "'ipfs object' is a plumbing command used to manipulate DAG objects directly.", + }, Subcommands: map[string]*cmds.Command{ "data": objectDataCmd, @@ -36,13 +38,16 @@ var objectCmd = &cmds.Command{ } var objectDataCmd = &cmds.Command{ - Description: "Outputs the raw bytes in an IPFS object", - Help: `ipfs data is a plumbing command for retreiving the raw bytes stored in a DAG node. + Helptext: cmds.HelpText{ + Tagline: "Outputs the raw bytes in an IPFS object", + ShortDescription: ` +ipfs data is a plumbing command for retreiving the raw bytes stored in a DAG node. It outputs to stdout, and is a base58 encoded multihash. Note that the "--encoding" option does not affect the output, since the output is the raw data of the object. `, + }, Arguments: []cmds.Argument{ cmds.StringArg("key", true, false, "Key of the object to retrieve, in base58-encoded multihash format"), @@ -63,9 +68,13 @@ output is the raw data of the object. } var objectLinksCmd = &cmds.Command{ - Description: "Outputs the links pointed to by the specified object", - Help: `'ipfs block get' is a plumbing command for retreiving raw IPFS blocks. -It outputs to stdout, and is a base58 encoded multihash.`, + Helptext: cmds.HelpText{ + Tagline: "Outputs the links pointed to by the specified object", + ShortDescription: ` +'ipfs block get' is a plumbing command for retreiving raw IPFS blocks. +It outputs to stdout, and is a base58 encoded multihash. +`, + }, Arguments: []cmds.Argument{ cmds.StringArg("key", true, false, "Key of the object to retrieve, in base58-encoded multihash format"), @@ -87,8 +96,15 @@ It outputs to stdout, and is a base58 encoded multihash.`, } var objectGetCmd = &cmds.Command{ - Description: "Get and serialize the DAG node named by ", - Help: `'ipfs object get' is a plumbing command for retreiving DAG nodes. + Helptext: cmds.HelpText{ + Tagline: "Get and serialize the DAG node named by ", + ShortDescription: ` +'ipfs object get' is a plumbing command for retreiving DAG nodes. +It serializes the DAG node to the format specified by the "--encoding" flag. +It outputs to stdout, and is a base58 encoded multihash. +`, + LongDescription: ` +'ipfs object get' is a plumbing command for retreiving DAG nodes. It serializes the DAG node to the format specified by the "--encoding" flag. It outputs to stdout, and is a base58 encoded multihash. @@ -97,6 +113,7 @@ This command outputs data in the following encodings: * "json" * "xml" (Specified by the "--encoding" or "-enc" flags)`, + }, Arguments: []cmds.Argument{ cmds.StringArg("key", true, false, "Key of the object to retrieve\n(in base58-encoded multihash format)"), @@ -142,8 +159,14 @@ This command outputs data in the following encodings: } var objectPutCmd = &cmds.Command{ - Description: "Stores input as a DAG object, outputs its key", - Help: `'ipfs object put' is a plumbing command for storing DAG nodes. + Helptext: cmds.HelpText{ + Tagline: "Stores input as a DAG object, outputs its key", + ShortDescription: ` +'ipfs object put' is a plumbing command for storing DAG nodes. +It reads from stdin, and the output is a base58 encoded multihash. +`, + LongDescription: ` +'ipfs object put' is a plumbing command for storing DAG nodes. It reads from stdin, and the output is a base58 encoded multihash. Data should be in the format specified by . @@ -151,6 +174,7 @@ Data should be in the format specified by . * "protobuf" * "json" `, + }, Arguments: []cmds.Argument{ cmds.FileArg("data", true, false, "Data to be stored as a DAG object\nMust be encoded as specified in "), diff --git a/core/commands2/pin.go b/core/commands2/pin.go index 93d8696e1..02e4d68a7 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -19,10 +19,13 @@ var pinCmd = &cmds.Command{ } var addPinCmd = &cmds.Command{ - Description: "Pins objects to local storage", - Help: `Retrieves the object named by and stores it locally + Helptext: cmds.HelpText{ + Tagline: "Pins objects to local storage", + ShortDescription: ` +Retrieves the object named by and stores it locally on disk. `, + }, Arguments: []cmds.Argument{ cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be pinned"), @@ -61,10 +64,13 @@ on disk. } var rmPinCmd = &cmds.Command{ - Description: "Unpin an object from local storage", - Help: `Removes the pin from the given object allowing it to be garbage + Helptext: cmds.HelpText{ + Tagline: "Unpin an object from local storage", + ShortDescription: ` +Removes the pin from the given object allowing it to be garbage collected if needed. `, + }, Arguments: []cmds.Argument{ cmds.StringArg("ipfs-path", true, true, "Path to object(s) to be unpinned"), diff --git a/core/commands2/refs.go b/core/commands2/refs.go index a6e369257..00cb5e7b4 100644 --- a/core/commands2/refs.go +++ b/core/commands2/refs.go @@ -16,13 +16,17 @@ type RefsOutput struct { } var refsCmd = &cmds.Command{ - Description: "Lists link hashes from an object", - Help: `Retrieves the object named by and displays the link + Helptext: cmds.HelpText{ + Tagline: "Lists link hashes from an object", + ShortDescription: ` +Retrieves the object named by and displays the link hashes it contains, with the following format: -Note: list all refs recursively with -r.`, +Note: list all refs recursively with -r. +`, + }, Arguments: []cmds.Argument{ cmds.StringArg("ipfs-path", true, true, "Path to the object(s) to list refs from"), diff --git a/core/commands2/resolve.go b/core/commands2/resolve.go index 323790df3..0f9287303 100644 --- a/core/commands2/resolve.go +++ b/core/commands2/resolve.go @@ -8,8 +8,15 @@ import ( ) var resolveCmd = &cmds.Command{ - Description: "Gets the value currently published at an IPNS name", - Help: `IPNS is a PKI namespace, where names are the hashes of public keys, and + Helptext: cmds.HelpText{ + Tagline: "Gets the value currently published at an IPNS name", + ShortDescription: ` +IPNS is a PKI namespace, where names are the hashes of public keys, and +the private key enables publishing new (signed) values. In resolve, the +default value of is your own identity public key. +`, + LongDescription: ` +IPNS is a PKI namespace, where names are the hashes of public keys, and the private key enables publishing new (signed) values. In resolve, the default value of is your own identity public key. @@ -27,6 +34,7 @@ Resolve te value of another name: QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy `, + }, Arguments: []cmds.Argument{ cmds.StringArg("name", false, false, "The IPNS name to resolve. Defaults to your node's peerID."), diff --git a/core/commands2/update.go b/core/commands2/update.go index 07a0f8d03..8dbfb3e54 100644 --- a/core/commands2/update.go +++ b/core/commands2/update.go @@ -15,9 +15,10 @@ type UpdateOutput struct { } var updateCmd = &cmds.Command{ - Description: "Downloads and installs updates for IPFS", - Help: `ipfs update is a utility command used to check for updates and apply them. -`, + Helptext: cmds.HelpText{ + Tagline: "Downloads and installs updates for IPFS", + ShortDescription: "ipfs update is a utility command used to check for updates and apply them.", + }, Run: func(req cmds.Request) (interface{}, error) { n, err := req.Context().GetNode() @@ -47,11 +48,14 @@ var updateCmd = &cmds.Command{ } var updateCheckCmd = &cmds.Command{ - Description: "Checks if updates are available", - Help: `'ipfs update check' checks if any updates are available for IPFS. + Helptext: cmds.HelpText{ + Tagline: "Checks if updates are available", + ShortDescription: ` +'ipfs update check' checks if any updates are available for IPFS. Nothing will be downloaded or installed. `, + }, Run: func(req cmds.Request) (interface{}, error) { n, err := req.Context().GetNode() @@ -77,9 +81,10 @@ Nothing will be downloaded or installed. } var updateLogCmd = &cmds.Command{ - Description: "List the changelog for the latest versions of IPFS", - Help: `This command is not yet implemented. -`, + Helptext: cmds.HelpText{ + Tagline: "List the changelog for the latest versions of IPFS", + ShortDescription: "This command is not yet implemented.", + }, Run: func(req cmds.Request) (interface{}, error) { n, err := req.Context().GetNode() diff --git a/core/commands2/version.go b/core/commands2/version.go index aa9e27a0d..575f57f6c 100644 --- a/core/commands2/version.go +++ b/core/commands2/version.go @@ -10,9 +10,10 @@ type VersionOutput struct { } var versionCmd = &cmds.Command{ - Description: "Outputs the current version of IPFS", - Help: `Returns the version number of IPFS and exits. -`, + Helptext: cmds.HelpText{ + Tagline: "Outputs the current version of IPFS", + ShortDescription: "Returns the version number of IPFS and exits.", + }, Options: []cmds.Option{ cmds.BoolOption("number", "n", "Only output the version number"), From b7b15b8b30eee41c8127f649a0bd1d51e83d6214 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 20:49:54 -0800 Subject: [PATCH 313/383] cmd/ipfs2: Added isLocal function for testing if a command is local only --- cmd/ipfs2/ipfs.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go index 5eccefd2c..a149e7eb1 100644 --- a/cmd/ipfs2/ipfs.go +++ b/cmd/ipfs2/ipfs.go @@ -21,6 +21,7 @@ var localCommands = map[string]*cmds.Command{ "tour": cmdTour, "commands": commands.CommandsCmd(Root), } +var localMap = make(map[*cmds.Command]bool) func init() { // setting here instead of in literal to prevent initialization loop @@ -33,4 +34,14 @@ func init() { Root.Subcommands[k] = v } } + + for _, v := range localCommands { + localMap[v] = true + } +} + +// isLocal returns true if the command should only be run locally (not sent to daemon), otherwise false +func isLocal(cmd *cmds.Command) bool { + _, found := localMap[cmd] + return found } From 9afb85714a922af7c975087366e5c8f1759330d6 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 20:52:16 -0800 Subject: [PATCH 314/383] commands/cli: Don't return root in Parse --- cmd/ipfs2/main.go | 2 +- commands/cli/parse.go | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 2ccef1645..0ef3ac69a 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -160,7 +160,7 @@ func (i *cmdInvocation) Run() (output io.Reader, err error) { func (i *cmdInvocation) Parse(args []string) error { var err error - i.req, i.root, i.cmd, i.path, err = cmdsCli.Parse(args, Root) + i.req, i.cmd, i.path, err = cmdsCli.Parse(args, Root) if err != nil { return err } diff --git a/commands/cli/parse.go b/commands/cli/parse.go index 8e1dba86f..3beb0e600 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -15,38 +15,36 @@ var ErrInvalidSubcmd = errors.New("subcommand not found") // Parse parses the input commandline string (cmd, flags, and args). // returns the corresponding command Request object. // Parse will search each root to find the one that best matches the requested subcommand. -// TODO: get rid of extraneous return values (e.g. we ended up not needing the root value anymore) -// TODO: get rid of multiple-root support, we should only need one now -func Parse(input []string, root *cmds.Command) (cmds.Request, *cmds.Command, *cmds.Command, []string, error) { +func Parse(input []string, root *cmds.Command) (cmds.Request, *cmds.Command, []string, error) { // use the root that matches the longest path (most accurately matches request) path, input, cmd := parsePath(input, root) opts, stringArgs, err := parseOptions(input) if err != nil { - return nil, root, cmd, path, err + return nil, cmd, path, err } if len(path) == 0 { - return nil, root, nil, path, ErrInvalidSubcmd + return nil, nil, path, ErrInvalidSubcmd } args, err := parseArgs(stringArgs, cmd) if err != nil { - return nil, root, cmd, path, err + return nil, cmd, path, err } optDefs, err := root.GetOptions(path) if err != nil { - return nil, root, cmd, path, err + return nil, cmd, path, err } req := cmds.NewRequest(path, opts, args, cmd, optDefs) err = cmd.CheckArguments(req) if err != nil { - return req, root, cmd, path, err + return req, cmd, path, err } - return req, root, cmd, path, nil + return req, cmd, path, nil } // parsePath separates the command path and the opts and args from a command string From 7f24a9965f83d24361126de6a1db9382a13cbe41 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 22:37:09 -0800 Subject: [PATCH 315/383] core/commands: Ported commands to use HelpText struct for helptext fields --- core/commands2/add.go | 16 ++++++++++------ core/commands2/block.go | 21 +++++++++++++++------ core/commands2/bootstrap.go | 25 ++++++++++++++++--------- core/commands2/cat.go | 9 ++++++--- core/commands2/diag.go | 7 +++++-- core/commands2/log.go | 7 +++++-- core/commands2/ls.go | 7 +++++-- core/commands2/mount_unix.go | 7 +++++-- core/commands2/mount_windows.go | 6 ++++-- 9 files changed, 71 insertions(+), 34 deletions(-) diff --git a/core/commands2/add.go b/core/commands2/add.go index c2606a00e..8dfd26ad2 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -29,18 +29,22 @@ type AddOutput struct { } var addCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "Add an object to ipfs.", + ShortDescription: ` +Adds contents of to ipfs. Use -r to add directories. +Note that directories are added recursively, to form the ipfs +MerkleDAG. A smarter partial add with a staging area (like git) +remains to be implemented. +`, + }, + Options: []cmds.Option{ cmds.BoolOption("recursive", "r", "Must be specified when adding directories"), }, Arguments: []cmds.Argument{ cmds.StringArg("file", true, true, "The path to a file to be added to IPFS"), }, - Description: "Add an object to ipfs.", - Help: `Adds contents of to ipfs. Use -r to add directories. -Note that directories are added recursively, to form the ipfs -MerkleDAG. A smarter partial add with a staging area (like git) -remains to be implemented. -`, Run: func(req cmds.Request) (interface{}, error) { added := &AddOutput{} n, err := req.Context().GetNode() diff --git a/core/commands2/block.go b/core/commands2/block.go index ece378911..de85ec5fd 100644 --- a/core/commands2/block.go +++ b/core/commands2/block.go @@ -21,10 +21,15 @@ type Block struct { } var blockCmd = &cmds.Command{ - Description: "Manipulate raw IPFS blocks", - Help: `'ipfs block' is a plumbing command used to manipulate raw ipfs blocks. + Helptext: cmds.HelpText{ + Tagline: "Manipulate raw IPFS blocks", + ShortDescription: ` +'ipfs block' is a plumbing command used to manipulate raw ipfs blocks. Reads from stdin or writes to stdout, and is a base58 encoded -multihash.`, +multihash. +`, + }, + Subcommands: map[string]*cmds.Command{ "get": blockGetCmd, "put": blockPutCmd, @@ -32,9 +37,13 @@ multihash.`, } var blockGetCmd = &cmds.Command{ - Description: "Get a raw IPFS block", - Help: `ipfs block get is a plumbing command for retreiving raw ipfs blocks. -It outputs to stdout, and is a base58 encoded multihash.`, + Helptext: cmds.HelpText{ + Tagline: "Get a raw IPFS block", + ShortDescription: ` +'ipfs block get' is a plumbing command for retreiving raw ipfs blocks. +It outputs to stdout, and is a base58 encoded multihash. +`, + }, Arguments: []cmds.Argument{ cmds.StringArg("key", true, false, "The base58 multihash of an existing block to get"), diff --git a/core/commands2/bootstrap.go b/core/commands2/bootstrap.go index a222fbdb5..c1e487b69 100644 --- a/core/commands2/bootstrap.go +++ b/core/commands2/bootstrap.go @@ -19,9 +19,11 @@ type BootstrapOutput struct { var peerOptionDesc = "A peer to add to the bootstrap list (in the format '/')" var bootstrapCmd = &cmds.Command{ - Description: "Show or edit the list of bootstrap peers", - Help: `Running 'ipfs bootstrap' with no arguments will run 'ipfs bootstrap list'. + Helptext: cmds.HelpText{ + Tagline: "Show or edit the list of bootstrap peers", + ShortDescription: `Running 'ipfs bootstrap' with no arguments will run 'ipfs bootstrap list'. ` + bootstrapSecurityWarning, + }, Run: bootstrapListCmd.Run, Marshallers: bootstrapListCmd.Marshallers, @@ -35,10 +37,12 @@ var bootstrapCmd = &cmds.Command{ } var bootstrapAddCmd = &cmds.Command{ - Description: "Add peers to the bootstrap list", - Help: `Outputs a list of peers that were added (that weren't already + Helptext: cmds.HelpText{ + Tagline: "Add peers to the bootstrap list", + ShortDescription: `Outputs a list of peers that were added (that weren't already in the bootstrap list). ` + bootstrapSecurityWarning, + }, Arguments: []cmds.Argument{ cmds.StringArg("peer", true, true, peerOptionDesc), @@ -81,9 +85,11 @@ in the bootstrap list). } var bootstrapRemoveCmd = &cmds.Command{ - Description: "Removes peers from the bootstrap list", - Help: `Outputs the list of peers that were removed. + Helptext: cmds.HelpText{ + Tagline: "Removes peers from the bootstrap list", + ShortDescription: `Outputs the list of peers that were removed. ` + bootstrapSecurityWarning, + }, Arguments: []cmds.Argument{ cmds.StringArg("peer", true, true, peerOptionDesc), @@ -126,9 +132,10 @@ var bootstrapRemoveCmd = &cmds.Command{ } var bootstrapListCmd = &cmds.Command{ - Description: "Show peers in the bootstrap list", - Help: `Peers are output in the format '/'. -`, + Helptext: cmds.HelpText{ + Tagline: "Show peers in the bootstrap list", + ShortDescription: "Peers are output in the format '/'.", + }, Run: func(req cmds.Request) (interface{}, error) { cfg, err := req.Context().GetConfig() diff --git a/core/commands2/cat.go b/core/commands2/cat.go index 5c7080a90..3bd760269 100644 --- a/core/commands2/cat.go +++ b/core/commands2/cat.go @@ -10,10 +10,13 @@ import ( ) var catCmd = &cmds.Command{ - Description: "Show IPFS object data", - Help: `Retrieves the object named by and outputs the data + Helptext: cmds.HelpText{ + Tagline: "Show IPFS object data", + ShortDescription: ` +Retrieves the object named by and outputs the data it contains. - `, +`, + }, Arguments: []cmds.Argument{ cmds.StringArg("ipfs-path", true, true, "The path to the IPFS object(s) to be outputted"), diff --git a/core/commands2/diag.go b/core/commands2/diag.go index caace1e91..d011e56b5 100644 --- a/core/commands2/diag.go +++ b/core/commands2/diag.go @@ -37,11 +37,14 @@ var diagCmd = &cmds.Command{ } var diagNetCmd = &cmds.Command{ - Description: "Generates a network diagnostics report", - Help: `Sends out a message to each node in the network recursively + Helptext: cmds.HelpText{ + Tagline: "Generates a network diagnostics report", + ShortDescription: ` +Sends out a message to each node in the network recursively requesting a listing of data about them including number of connected peers and latencies between them. `, + }, Run: func(req cmds.Request) (interface{}, error) { n, err := req.Context().GetNode() diff --git a/core/commands2/log.go b/core/commands2/log.go index 4ed3233ad..a3d941346 100644 --- a/core/commands2/log.go +++ b/core/commands2/log.go @@ -8,10 +8,13 @@ import ( ) var logCmd = &cmds.Command{ - Description: "Change the logging level", - Help: `'ipfs log' is a utility command used to change the logging + Helptext: cmds.HelpText{ + Tagline: "Change the logging level", + ShortDescription: ` +'ipfs log' is a utility command used to change the logging output of a running daemon. `, + }, Arguments: []cmds.Argument{ cmds.StringArg("subsystem", true, false, "the subsystem logging identifier. Use * for all subsystems."), diff --git a/core/commands2/ls.go b/core/commands2/ls.go index bef59251f..1cc36f99b 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -23,12 +23,15 @@ type LsOutput struct { } var lsCmd = &cmds.Command{ - Description: "List links from an object.", - Help: `Retrieves the object named by and displays the links + Helptext: cmds.HelpText{ + Tagline: "List links from an object.", + ShortDescription: ` +Retrieves the object named by and displays the links it contains, with the following format: `, + }, Arguments: []cmds.Argument{ cmds.StringArg("ipfs-path", false, true, "The path to the IPFS object(s) to list links from"), diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go index 786b09c4c..490bce154 100644 --- a/core/commands2/mount_unix.go +++ b/core/commands2/mount_unix.go @@ -18,11 +18,14 @@ import ( const mountTimeout = time.Second var mountCmd = &cmds.Command{ - Description: "Mounts IPFS to the filesystem (read-only)", - Help: `Mount ipfs at a read-only mountpoint on the OS. All ipfs objects + Helptext: cmds.HelpText{ + Tagline: "Mounts IPFS to the filesystem (read-only)", + ShortDescription: ` +Mount ipfs at a read-only mountpoint on the OS. All ipfs objects will be accessible under that directory. Note that the root will not be listable, as it is virtual. Accessing known paths directly. `, + }, Options: []cmds.Option{ // TODO longform diff --git a/core/commands2/mount_windows.go b/core/commands2/mount_windows.go index 29e105c80..6f5132e3f 100644 --- a/core/commands2/mount_windows.go +++ b/core/commands2/mount_windows.go @@ -7,8 +7,10 @@ import ( ) var ipfsMount = &cmds.Command{ - Description: "Not yet implemented on Windows", - Help: `Not yet implemented on Windows. :(`, + Helptext: cmds.HelpText{ + Tagline: "Not yet implemented on Windows", + ShortDescription: "Not yet implemented on Windows. :(", + }, Run: func(req cmds.Request) (interface{}, error) { return errors.New("Mount isn't compatible with Windows yet"), nil From 188bf779dbdc27ad2ec2a3c30fa8ef1ab53f5000 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 22:58:36 -0800 Subject: [PATCH 316/383] Removed cmdInvocation.root --- cmd/ipfs2/main.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 0ef3ac69a..f6cb0fc03 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -36,7 +36,6 @@ const ( type cmdInvocation struct { path []string cmd *cmds.Command - root *cmds.Command req cmds.Request } @@ -64,7 +63,7 @@ func main() { helpFunc = cmdsCli.LongHelp } - helpFunc("ipfs", invoc.root, invoc.path, os.Stderr) + helpFunc("ipfs", Root, invoc.path, os.Stderr) } // parse the commandline into a command invocation @@ -145,7 +144,7 @@ func (i *cmdInvocation) Run() (output io.Reader, err error) { defer stopProfilingFunc() // to be executed as late as possible } - res, err := callCommand(i.req, i.root) + res, err := callCommand(i.req, Root) if err != nil { return nil, err } From fb187e49e3e3075c22c360e3eb8384d51df8af51 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 22:30:44 -0800 Subject: [PATCH 317/383] test(2/diag) test print diagnostics License: MIT Signed-off-by: Brian Tiger Chow --- core/commands2/diag_test.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 core/commands2/diag_test.go diff --git a/core/commands2/diag_test.go b/core/commands2/diag_test.go new file mode 100644 index 000000000..5e6c383e2 --- /dev/null +++ b/core/commands2/diag_test.go @@ -0,0 +1,29 @@ +package commands + +import ( + "bytes" + "testing" +) + +func TestPrintDiagnostics(t *testing.T) { + output := DiagnosticOutput{ + Peers: []DiagnosticPeer{ + DiagnosticPeer{ID: "QmNrjRuUtBNZAigzLRdZGN1YCNUxdF2WY2HnKyEFJqoTeg", + UptimeSeconds: 14, + Connections: []DiagnosticConnection{ + DiagnosticConnection{ID: "QmNrjRuUtBNZAigzLRdZGN1YCNUxdF2WY2HnKyEFJqoTeg", + NanosecondsLatency: 1347899, + }, + }, + }, + DiagnosticPeer{ID: "QmUaUZDp6QWJabBYSKfiNmXLAXD8HNKnWZh9Zoz6Zri9Ti", + UptimeSeconds: 14, + }, + }, + } + var buf bytes.Buffer + if err := printDiagnostics(&buf, &output); err != nil { + t.Fatal(err) + } + t.Log(buf.String()) +} From 131c15e924447a3dbe42a446f2faf70ebdc66da2 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 23:27:18 -0800 Subject: [PATCH 318/383] fix(2/log) use 'all' as the specifier to set all log levels fixes #322 cc @mappum License: MIT Signed-off-by: Brian Tiger Chow --- core/commands2/log.go | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/core/commands2/log.go b/core/commands2/log.go index a3d941346..157bdca27 100644 --- a/core/commands2/log.go +++ b/core/commands2/log.go @@ -7,6 +7,12 @@ import ( u "github.com/jbenet/go-ipfs/util" ) +// Golang os.Args overrides * and replaces the character argument with +// an array which includes every file in the user's CWD. As a +// workaround, we use 'all' instead. The util library still uses * so +// we convert it at this step. +var logAllKeyword = "all" + var logCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Change the logging level", @@ -17,16 +23,29 @@ output of a running daemon. }, Arguments: []cmds.Argument{ - cmds.StringArg("subsystem", true, false, "the subsystem logging identifier. Use * for all subsystems."), + // TODO use a different keyword for 'all' because all can theoretically + // clash with a subsystem name + cmds.StringArg("subsystem", true, false, fmt.Sprintf("the subsystem logging identifier. Use '%s' for all subsystems.", logAllKeyword)), cmds.StringArg("level", true, false, "one of: debug, info, notice, warning, error, critical"), }, Run: func(req cmds.Request) (interface{}, error) { + args := req.Arguments() - if err := u.SetLogLevel(args[0].(string), args[1].(string)); err != nil { + subsystem, ok1 := args[0].(string) + level, ok2 := args[1].(string) + if !ok1 || !ok2 { + return nil, u.ErrCast() + } + + if subsystem == logAllKeyword { + subsystem = "*" + } + + if err := u.SetLogLevel(subsystem, level); err != nil { return nil, err } - s := fmt.Sprintf("Changed log level of '%s' to '%s'", args[0], args[1]) + s := fmt.Sprintf("Changed log level of '%s' to '%s'", subsystem, level) log.Info(s) return &MessageOutput{s}, nil }, From 5a9de188d75163a6544de40ab9b75c63adbb2b6a Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 23:31:37 -0800 Subject: [PATCH 319/383] fix(commands/request) return err when unable to load config/node due to nil function cc @jbenet @mappum License: MIT Signed-off-by: Brian Tiger Chow --- commands/request.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/commands/request.go b/commands/request.go index 6e8844682..5bdfe7ade 100644 --- a/commands/request.go +++ b/commands/request.go @@ -1,6 +1,7 @@ package commands import ( + "errors" "fmt" "io" "reflect" @@ -29,7 +30,7 @@ func (c *Context) GetConfig() (*config.Config, error) { var err error if c.config == nil { if c.LoadConfig == nil { - panic("nil LoadConfig function") + return nil, errors.New("nil LoadConfig function") } c.config, err = c.LoadConfig(c.ConfigRoot) } @@ -42,7 +43,7 @@ func (c *Context) GetNode() (*core.IpfsNode, error) { var err error if c.node == nil { if c.ConstructNode == nil { - panic("nil ConstructNode function") + return nil, errors.New("nil ConstructNode function") } c.node, err = c.ConstructNode() } From 27b9aec3702691e84b6d849704a54f9524667406 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 23:35:00 -0800 Subject: [PATCH 320/383] style(commands/cli/helptext) use helper function to cut down on boilerplate I know you'll love this one @mappum License: MIT Signed-off-by: Brian Tiger Chow --- commands/cli/helptext.go | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index 2a305fb4a..84207f3d4 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -113,23 +113,9 @@ var longHelpTemplate *template.Template var shortHelpTemplate *template.Template func init() { - tmpl, err := template.New("usage").Parse(usageFormat) - if err != nil { - panic(err) - } - usageTemplate = tmpl - - tmpl, err = usageTemplate.New("longHelp").Parse(longHelpFormat) - if err != nil { - panic(err) - } - longHelpTemplate = tmpl - - tmpl, err = usageTemplate.New("shortHelp").Parse(shortHelpFormat) - if err != nil { - panic(err) - } - shortHelpTemplate = tmpl + usageTemplate = template.Must(template.New("usage").Parse(usageFormat)) + longHelpTemplate = template.Must(usageTemplate.New("longHelp").Parse(longHelpFormat)) + shortHelpTemplate = template.Must(usageTemplate.New("shortHelp").Parse(shortHelpFormat)) } // LongHelp returns a formatted CLI helptext string, generated for the given command From b4735eb10bf16bea8a743f4ea286b9392dad41e3 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 23:35:41 -0800 Subject: [PATCH 321/383] main: Test if commands are local-only when choosing daemon vs. local --- cmd/ipfs2/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index f6cb0fc03..1a7aefa8c 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -206,7 +206,7 @@ func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { if err != nil { return nil, err } - remote := !found || !local + remote := !isLocal(req.Command()) && !found || !local log.Info("Checking if daemon is running...") if remote && daemon.Locked(req.Context().ConfigRoot) { From 28306a49c42792fbdd67b35be984ac187c42852d Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Wed, 12 Nov 2014 23:38:14 -0800 Subject: [PATCH 322/383] commands/cli,http: Properly preserve argument value count when checking argument validity --- commands/cli/parse.go | 21 +++++++++------------ commands/http/parse.go | 23 +++++++++++------------ 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index 3beb0e600..db1d13403 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -109,8 +109,6 @@ func parseOptions(input []string) (map[string]interface{}, []string, error) { } func parseArgs(stringArgs []string, cmd *cmds.Command) ([]interface{}, error) { - args := make([]interface{}, 0) - // count required argument definitions lenRequired := 0 for _, argDef := range cmd.Arguments { @@ -119,6 +117,8 @@ func parseArgs(stringArgs []string, cmd *cmds.Command) ([]interface{}, error) { } } + args := make([]interface{}, len(stringArgs)) + valueIndex := 0 // the index of the current stringArgs value for _, argDef := range cmd.Arguments { // skip optional argument definitions if there aren't sufficient remaining values @@ -134,39 +134,36 @@ func parseArgs(stringArgs []string, cmd *cmds.Command) ([]interface{}, error) { if argDef.Variadic { for _, arg := range stringArgs[valueIndex:] { - var err error - args, err = appendArg(args, argDef, arg) + value, err := argValue(argDef, arg) if err != nil { return nil, err } + args[valueIndex] = value valueIndex++ } } else { var err error - args, err = appendArg(args, argDef, stringArgs[valueIndex]) + value, err := argValue(argDef, stringArgs[valueIndex]) if err != nil { return nil, err } + args[valueIndex] = value valueIndex++ } } - if len(stringArgs)-valueIndex > 0 { - args = append(args, make([]interface{}, len(stringArgs)-valueIndex)) - } - return args, nil } -func appendArg(args []interface{}, argDef cmds.Argument, value string) ([]interface{}, error) { +func argValue(argDef cmds.Argument, value string) (interface{}, error) { if argDef.Type == cmds.ArgString { - return append(args, value), nil + return value, nil } else { in, err := os.Open(value) // FIXME(btc) must close file. fix before merge if err != nil { return nil, err } - return append(args, in), nil + return in, nil } } diff --git a/commands/http/parse.go b/commands/http/parse.go index 6c897ee5e..a59bb3b78 100644 --- a/commands/http/parse.go +++ b/commands/http/parse.go @@ -39,8 +39,6 @@ func Parse(r *http.Request, root *cmds.Command) (cmds.Request, error) { opts, stringArgs2 := parseOptions(r) stringArgs = append(stringArgs, stringArgs2...) - args := make([]interface{}, 0) - // count required argument definitions numRequired := 0 for _, argDef := range cmd.Arguments { @@ -52,13 +50,16 @@ func Parse(r *http.Request, root *cmds.Command) (cmds.Request, error) { // count the number of provided argument values valCount := len(stringArgs) // TODO: add total number of parts in request body (instead of just 1 if body is present) - if r.Body != nil { + if r.Body != nil && r.ContentLength != 0 { valCount += 1 } + args := make([]interface{}, valCount) + + valIndex := 0 for _, argDef := range cmd.Arguments { // skip optional argument definitions if there aren't sufficient remaining values - if valCount <= numRequired && !argDef.Required { + if valCount-valIndex <= numRequired && !argDef.Required { continue } else if argDef.Required { numRequired-- @@ -67,14 +68,15 @@ func Parse(r *http.Request, root *cmds.Command) (cmds.Request, error) { if argDef.Type == cmds.ArgString { if argDef.Variadic { for _, s := range stringArgs { - args = append(args, s) + args[valIndex] = s + valIndex++ } valCount -= len(stringArgs) } else if len(stringArgs) > 0 { - args = append(args, stringArgs[0]) + args[valIndex] = stringArgs[0] stringArgs = stringArgs[1:] - valCount-- + valIndex++ } else { break @@ -82,14 +84,11 @@ func Parse(r *http.Request, root *cmds.Command) (cmds.Request, error) { } else { // TODO: create multipart streams for file args - args = append(args, r.Body) + args[valIndex] = r.Body + valIndex++ } } - if valCount-1 > 0 { - args = append(args, make([]interface{}, valCount-1)) - } - optDefs, err := root.GetOptions(path) if err != nil { return nil, err From 562500491f255e7710de2f2cfe57c7670dc2f4a0 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 23:44:16 -0800 Subject: [PATCH 323/383] test(commands/parse) take args instead of cmd for easier testing @mappum License: MIT Signed-off-by: Brian Tiger Chow --- commands/cli/parse.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index db1d13403..803d19d79 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -27,7 +27,7 @@ func Parse(input []string, root *cmds.Command) (cmds.Request, *cmds.Command, []s return nil, nil, path, ErrInvalidSubcmd } - args, err := parseArgs(stringArgs, cmd) + args, err := parseArgs(stringArgs, cmd.Arguments) if err != nil { return nil, cmd, path, err } @@ -108,10 +108,10 @@ func parseOptions(input []string) (map[string]interface{}, []string, error) { return opts, args, nil } -func parseArgs(stringArgs []string, cmd *cmds.Command) ([]interface{}, error) { +func parseArgs(stringArgs []string, arguments []cmds.Argument) ([]interface{}, error) { // count required argument definitions lenRequired := 0 - for _, argDef := range cmd.Arguments { + for _, argDef := range arguments { if argDef.Required { lenRequired++ } @@ -120,7 +120,7 @@ func parseArgs(stringArgs []string, cmd *cmds.Command) ([]interface{}, error) { args := make([]interface{}, len(stringArgs)) valueIndex := 0 // the index of the current stringArgs value - for _, argDef := range cmd.Arguments { + for _, argDef := range arguments { // skip optional argument definitions if there aren't sufficient remaining values if len(stringArgs)-valueIndex <= lenRequired && !argDef.Required { continue From e6c5fc250c8de0563b32b9832964f0cbfce796bc Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 23:53:49 -0800 Subject: [PATCH 324/383] docs(commands) todo License: MIT Signed-off-by: Brian Tiger Chow --- commands/cli/parse.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index 803d19d79..b84ecca76 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -160,7 +160,10 @@ func argValue(argDef cmds.Argument, value string) (interface{}, error) { return value, nil } else { - in, err := os.Open(value) // FIXME(btc) must close file. fix before merge + // NB At the time of this commit, file cleanup is performed when + // Requests are cleaned up. TODO try to perform open and close at the + // same level of abstraction (or at least in the same package!) + in, err := os.Open(value) if err != nil { return nil, err } From 6597a5f7fe9a2941da0a9969297ad636ba1f6c8d Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Wed, 12 Nov 2014 23:57:58 -0800 Subject: [PATCH 325/383] fix(2/ls) require arg License: MIT Signed-off-by: Brian Tiger Chow --- core/commands2/ls.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/commands2/ls.go b/core/commands2/ls.go index 1cc36f99b..d948f635e 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -34,7 +34,7 @@ it contains, with the following format: }, Arguments: []cmds.Argument{ - cmds.StringArg("ipfs-path", false, true, "The path to the IPFS object(s) to list links from"), + cmds.StringArg("ipfs-path", true, true, "The path to the IPFS object(s) to list links from"), }, Run: func(req cmds.Request) (interface{}, error) { node, err := req.Context().GetNode() From e700b165761811fbed302cd56504157f78054397 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 13 Nov 2014 00:09:58 -0800 Subject: [PATCH 326/383] Ported remaining command helptext to HelpText struct --- cmd/ipfs2/daemon.go | 5 ++++- cmd/ipfs2/init.go | 7 ++++--- cmd/ipfs2/tour.go | 12 +++++++++--- core/commands2/block.go | 10 +++++++--- core/commands2/config.go | 14 ++++++++++---- core/commands2/diag.go | 4 +++- core/commands2/pin.go | 4 +++- core/commands2/publish.go | 13 ++++++++++--- 8 files changed, 50 insertions(+), 19 deletions(-) diff --git a/cmd/ipfs2/daemon.go b/cmd/ipfs2/daemon.go index bd5d72076..3a463d90a 100644 --- a/cmd/ipfs2/daemon.go +++ b/cmd/ipfs2/daemon.go @@ -15,8 +15,11 @@ import ( ) var daemonCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "run a network-connected ipfs node", // TODO adjust copy + }, + Options: []cmds.Option{}, - Help: "run a network-connected ipfs node", // TODO adjust copy Subcommands: map[string]*cmds.Command{}, Run: daemonFunc, } diff --git a/cmd/ipfs2/init.go b/cmd/ipfs2/init.go index a72b590b6..6b04a4418 100644 --- a/cmd/ipfs2/init.go +++ b/cmd/ipfs2/init.go @@ -15,9 +15,10 @@ import ( ) var initCmd = &cmds.Command{ - Description: "Initializes IPFS config file", - Help: `Initializes IPFS configuration files and generates a new keypair. -`, + Helptext: cmds.HelpText{ + Tagline: "Initializes IPFS config file", + ShortDescription: "Initializes IPFS configuration files and generates a new keypair.", + }, Options: []cmds.Option{ cmds.IntOption("bits", "b", "Number of bits to use in the generated RSA private key (defaults to 4096)"), diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index b3f55ebd2..efca847f7 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -60,7 +60,9 @@ IPFS very quickly. To start, run: } var cmdIpfsTourNext = &cmds.Command{ - Description: "Show the next IPFS Tour topic", + Helptext: cmds.HelpText{ + Tagline: "Show the next IPFS Tour topic", + }, Run: func(req cmds.Request) (interface{}, error) { var w bytes.Buffer @@ -90,7 +92,9 @@ var cmdIpfsTourNext = &cmds.Command{ } var cmdIpfsTourRestart = &cmds.Command{ - Description: "Restart the IPFS Tour", + Helptext: cmds.HelpText{ + Tagline: "Restart the IPFS Tour", + }, Run: func(req cmds.Request) (interface{}, error) { path := req.Context().ConfigRoot @@ -109,7 +113,9 @@ var cmdIpfsTourRestart = &cmds.Command{ } var cmdIpfsTourList = &cmds.Command{ - Description: "Show a list of IPFS Tour topics", + Helptext: cmds.HelpText{ + Tagline: "Show a list of IPFS Tour topics", + }, Run: func(req cmds.Request) (interface{}, error) { cfg, err := req.Context().GetConfig() diff --git a/core/commands2/block.go b/core/commands2/block.go index de85ec5fd..3a5c5b225 100644 --- a/core/commands2/block.go +++ b/core/commands2/block.go @@ -80,9 +80,13 @@ It outputs to stdout, and is a base58 encoded multihash. } var blockPutCmd = &cmds.Command{ - Description: "Stores input as an IPFS block", - Help: `ipfs block put is a plumbing command for storing raw ipfs blocks. -It reads from stdin, and is a base58 encoded multihash.`, + Helptext: cmds.HelpText{ + Tagline: "Stores input as an IPFS block", + ShortDescription: ` +ipfs block put is a plumbing command for storing raw ipfs blocks. +It reads from stdin, and is a base58 encoded multihash. +`, + }, Arguments: []cmds.Argument{ cmds.FileArg("data", true, false, "The data to be stored as an IPFS block"), diff --git a/core/commands2/config.go b/core/commands2/config.go index 2b26f509a..8bbec7083 100644 --- a/core/commands2/config.go +++ b/core/commands2/config.go @@ -116,10 +116,13 @@ Set the value of the 'datastore.path' key: } var configShowCmd = &cmds.Command{ - Description: "Outputs the content of the config file", - Help: `WARNING: Your private key is stored in the config file, and it will be + Helptext: cmds.HelpText{ + Tagline: "Outputs the content of the config file", + ShortDescription: ` +WARNING: Your private key is stored in the config file, and it will be included in the output of this command. `, + }, Run: func(req cmds.Request) (interface{}, error) { filename, err := config.Filename(req.Context().ConfigRoot) @@ -132,10 +135,13 @@ included in the output of this command. } var configEditCmd = &cmds.Command{ - Description: "Opens the config file for editing in $EDITOR", - Help: `To use 'ipfs config edit', you must have the $EDITOR environment + Helptext: cmds.HelpText{ + Tagline: "Opens the config file for editing in $EDITOR", + ShortDescription: ` +To use 'ipfs config edit', you must have the $EDITOR environment variable set to your preferred text editor. `, + }, Run: func(req cmds.Request) (interface{}, error) { filename, err := config.Filename(req.Context().ConfigRoot) diff --git a/core/commands2/diag.go b/core/commands2/diag.go index d011e56b5..8fd3db284 100644 --- a/core/commands2/diag.go +++ b/core/commands2/diag.go @@ -29,7 +29,9 @@ type DiagnosticOutput struct { } var diagCmd = &cmds.Command{ - Description: "Generates diagnostic reports", + Helptext: cmds.HelpText{ + Tagline: "Generates diagnostic reports", + }, Subcommands: map[string]*cmds.Command{ "net": diagNetCmd, diff --git a/core/commands2/pin.go b/core/commands2/pin.go index 02e4d68a7..30259f489 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -10,7 +10,9 @@ import ( ) var pinCmd = &cmds.Command{ - Description: "Keeps objects stored locally", + Helptext: cmds.HelpText{ + Tagline: "Keeps objects stored locally", + }, Subcommands: map[string]*cmds.Command{ "add": addPinCmd, diff --git a/core/commands2/publish.go b/core/commands2/publish.go index f0d29d478..ab0e81536 100644 --- a/core/commands2/publish.go +++ b/core/commands2/publish.go @@ -14,8 +14,15 @@ import ( var errNotOnline = errors.New("This command must be run in online mode. Try running 'ipfs daemon' first.") var publishCmd = &cmds.Command{ - Description: "Publish an object to IPNS", - Help: `IPNS is a PKI namespace, where names are the hashes of public keys, and + Helptext: cmds.HelpText{ + Tagline: "Publish an object to IPNS", + ShortDescription: ` +IPNS is a PKI namespace, where names are the hashes of public keys, and +the private key enables publishing new (signed) values. In publish, the +default value of is your own identity public key. +`, + LongDescription: ` +IPNS is a PKI namespace, where names are the hashes of public keys, and the private key enables publishing new (signed) values. In publish, the default value of is your own identity public key. @@ -30,8 +37,8 @@ Publish a to another public key: > ipfs name publish QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy published name QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n to QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - `, + }, Arguments: []cmds.Argument{ cmds.StringArg("name", false, false, "The IPNS name to publish to. Defaults to your node's peerID"), From 646920b0dd39161af59e01bf214ad0eddc1ee1f2 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 13 Nov 2014 00:10:40 -0800 Subject: [PATCH 327/383] commands: Got rid of old helptext fields, use HelpText struct fields in helptext generator --- commands/cli/helptext.go | 60 ++++++++++------------------------------ commands/command.go | 7 ----- 2 files changed, 15 insertions(+), 52 deletions(-) diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index 84207f3d4..3c6a00473 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -130,38 +130,31 @@ func LongHelp(rootName string, root *cmds.Command, path []string, out io.Writer) pathStr += " " + strings.Join(path, " ") } - // TODO: get the fields from the HelpText struct by default (when commands are ported to use it) fields := helpFields{ Indent: indentStr, Path: pathStr, ArgUsage: usageText(cmd), - Tagline: cmd.Description, - Arguments: cmd.ArgumentHelp, - Options: cmd.OptionHelp, + Tagline: cmd.Helptext.Tagline, + Arguments: cmd.Helptext.Arguments, + Options: cmd.Helptext.Options, Synopsis: cmd.Helptext.Synopsis, - Subcommands: cmd.SubcommandHelp, - Description: cmd.Help, + Subcommands: cmd.Helptext.Subcommands, + Description: cmd.Helptext.ShortDescription, + Usage: cmd.Helptext.Usage, } - // TODO: don't do these checks, just use these fields by default (when commands get ported to it) - if len(cmd.Helptext.Tagline) > 0 { - fields.Tagline = cmd.Helptext.Tagline - } - if len(cmd.Helptext.ShortDescription) > 0 { - fields.Description = cmd.Helptext.ShortDescription - } - if len(cmd.Helptext.Usage) > 0 { - fields.Usage = cmd.Helptext.Subcommands + if len(cmd.Helptext.LongDescription) > 0 { + fields.Description = cmd.Helptext.LongDescription } // autogen fields that are empty - if len(cmd.ArgumentHelp) == 0 { + if len(fields.Arguments) == 0 { fields.Arguments = strings.Join(argumentText(cmd), "\n") } - if len(cmd.OptionHelp) == 0 { + if len(fields.Options) == 0 { fields.Options = strings.Join(optionText(cmd), "\n") } - if len(cmd.SubcommandHelp) == 0 { + if len(fields.Subcommands) == 0 { fields.Subcommands = strings.Join(subcommandText(cmd, rootName, path), "\n") } @@ -195,29 +188,10 @@ func ShortHelp(rootName string, root *cmds.Command, path []string, out io.Writer Indent: indentStr, Path: pathStr, ArgUsage: usageText(cmd), - Tagline: cmd.Description, + Tagline: cmd.Helptext.Tagline, Synopsis: cmd.Helptext.Synopsis, - Description: cmd.Help, - } - - // TODO: don't do these checks, just use these fields by default (when commands get ported to it) - if len(cmd.Helptext.Tagline) > 0 { - fields.Tagline = cmd.Helptext.Tagline - } - if len(cmd.Helptext.Arguments) > 0 { - fields.Arguments = cmd.Helptext.Arguments - } - if len(cmd.Helptext.Options) > 0 { - fields.Options = cmd.Helptext.Options - } - if len(cmd.Helptext.Subcommands) > 0 { - fields.Subcommands = cmd.Helptext.Subcommands - } - if len(cmd.Helptext.ShortDescription) > 0 { - fields.Description = cmd.Helptext.ShortDescription - } - if len(cmd.Helptext.Usage) > 0 { - fields.Usage = cmd.Helptext.Subcommands + Description: cmd.Helptext.ShortDescription, + Usage: cmd.Helptext.Usage, } // trim the extra newlines (see TrimNewlines doc) @@ -311,11 +285,7 @@ func subcommandText(cmd *cmds.Command, rootName string, path []string) []string if len(usage) > 0 { usage = " " + usage } - if len(sub.Helptext.Tagline) > 0 { - lines[i] = fmt.Sprintf("%v%v%v - %v", prefix, name, usage, sub.Helptext.Tagline) - } else { - lines[i] = fmt.Sprintf("%v%v%v - %v", prefix, name, usage, sub.Description) - } + lines[i] = fmt.Sprintf("%v%v%v - %v", prefix, name, usage, sub.Helptext.Tagline) i++ } diff --git a/commands/command.go b/commands/command.go index a31935d21..71336cc02 100644 --- a/commands/command.go +++ b/commands/command.go @@ -44,13 +44,6 @@ type HelpText struct { // Command is a runnable command, with input arguments and options (flags). // It can also have Subcommands, to group units of work into sets. type Command struct { - // TODO: remove these fields after porting commands to HelpText struct - Description string - Help string - SubcommandHelp string - OptionHelp string - ArgumentHelp string - Options []Option Arguments []Argument Run Function From 2a2ec747d5ccf287ae70eb63718ff61aa0106b99 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 13 Nov 2014 00:15:43 -0800 Subject: [PATCH 328/383] core/commands2: Removed unused options from 'config', and fixed synopsis syntax --- core/commands2/config.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/core/commands2/config.go b/core/commands2/config.go index 8bbec7083..5f7cfdd93 100644 --- a/core/commands2/config.go +++ b/core/commands2/config.go @@ -25,8 +25,8 @@ var configCmd = &cmds.Command{ Synopsis: ` ipfs config - Get value of ipfs config - Set value of to -ipfs config --show - Show config file -ipfs config --edit - Edit config file in $EDITOR +ipfs config show - Show config file +ipfs config edit - Edit config file in $EDITOR `, ShortDescription: ` ipfs config controls configuration variables. It works like 'git config'. @@ -53,10 +53,6 @@ Set the value of the 'datastore.path' key: cmds.StringArg("key", true, false, "The key of the config entry (e.g. \"Addresses.API\")"), cmds.StringArg("value", false, false, "The value to set the config entry to"), }, - Options: []cmds.Option{ - cmds.StringOption("show", "s", "Show config file"), - cmds.StringOption("edit", "e", "Edit config file in $EDITOR"), - }, Run: func(req cmds.Request) (interface{}, error) { args := req.Arguments() From 81b3680908e95d7b887250feb72b19a2dd6c2ec6 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 13 Nov 2014 00:37:48 -0800 Subject: [PATCH 329/383] cmds2: aligned subcmd text --- commands/cli/helptext.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/commands/cli/helptext.go b/commands/cli/helptext.go index 3c6a00473..9ac20c820 100644 --- a/commands/cli/helptext.go +++ b/commands/cli/helptext.go @@ -277,6 +277,7 @@ func subcommandText(cmd *cmds.Command, rootName string, path []string) []string if len(path) > 0 { prefix += " " } + subcmds := make([]*cmds.Command, len(cmd.Subcommands)) lines := make([]string, len(cmd.Subcommands)) i := 0 @@ -285,10 +286,16 @@ func subcommandText(cmd *cmds.Command, rootName string, path []string) []string if len(usage) > 0 { usage = " " + usage } - lines[i] = fmt.Sprintf("%v%v%v - %v", prefix, name, usage, sub.Helptext.Tagline) + lines[i] = prefix + name + usage + subcmds[i] = sub i++ } + lines = align(lines) + for i, sub := range subcmds { + lines[i] += " - " + sub.Helptext.Tagline + } + return lines } From c9737760495c86743eff724c061c00aa13ac4caa Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 13 Nov 2014 01:04:21 -0800 Subject: [PATCH 330/383] main: Fixed logical error in remote/local check --- cmd/ipfs2/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 1a7aefa8c..2c90cb012 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -206,7 +206,8 @@ func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { if err != nil { return nil, err } - remote := !isLocal(req.Command()) && !found || !local + + remote := !isLocal(req.Command()) && (!found || !local) log.Info("Checking if daemon is running...") if remote && daemon.Locked(req.Context().ConfigRoot) { From 3b407c705d5626e012f463f67683f68aec36b53d Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 13 Nov 2014 01:09:57 -0800 Subject: [PATCH 331/383] commands: Ensure command output is correct type (if cmd.Type is set), resolves #321 --- commands/command.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/commands/command.go b/commands/command.go index 71336cc02..2ce2cd382 100644 --- a/commands/command.go +++ b/commands/command.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io" + "reflect" "strings" u "github.com/jbenet/go-ipfs/util" @@ -64,6 +65,8 @@ var ErrNotCallable = errors.New("This command can't be called directly. Try one var ErrNoFormatter = errors.New("This command cannot be formatted to plain text") +var ErrIncorrectType = errors.New("The command returned a value with a different type than expected") + // Call invokes the command for the given Request func (c *Command) Call(req Request) Response { res := NewResponse(req) @@ -106,6 +109,17 @@ func (c *Command) Call(req Request) Response { return res } + // If the command specified an output type, ensure the actual value returned is of that type + if cmd.Type != nil { + definedType := reflect.ValueOf(cmd.Type).Type() + actualType := reflect.ValueOf(output).Type() + + if definedType != actualType { + res.SetError(ErrIncorrectType, ErrNormal) + return res + } + } + // clean up the request (close the readers, e.g. fileargs) // NOTE: this means commands can't expect to keep reading after cmd.Run returns (in a goroutine) err = req.Cleanup() From 0f7757c6fa52e30d9b66537234fc5eea13358d74 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 13 Nov 2014 00:41:53 -0800 Subject: [PATCH 332/383] subcmd2: added synopsis to bootstrap --- core/commands2/bootstrap.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/commands2/bootstrap.go b/core/commands2/bootstrap.go index c1e487b69..738dcb2a4 100644 --- a/core/commands2/bootstrap.go +++ b/core/commands2/bootstrap.go @@ -21,7 +21,13 @@ var peerOptionDesc = "A peer to add to the bootstrap list (in the format '... - Add peers to the bootstrap list +ipfs bootstrap remove ... - Removes peers from the bootstrap list +`, + ShortDescription: ` +Running 'ipfs bootstrap' with no arguments will run 'ipfs bootstrap list'. ` + bootstrapSecurityWarning, }, From 047d2e2d6287b165f689d43b284336f5726bef51 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 13 Nov 2014 01:05:44 -0800 Subject: [PATCH 333/383] cmd2: Marshaller -> Marshaler (see golang/encoding) Also: - map[cmds.EncodingType]cmds.Marshaller -> MarshalMap cc @mappum @maybebtc --- cmd/ipfs2/main.go | 2 +- commands/command.go | 18 +++++++++++------- commands/response.go | 10 +++++----- core/commands2/add.go | 2 +- core/commands2/block.go | 2 +- core/commands2/bootstrap.go | 22 +++++++++++----------- core/commands2/commands.go | 2 +- core/commands2/config.go | 2 +- core/commands2/diag.go | 2 +- core/commands2/log.go | 4 ++-- core/commands2/ls.go | 2 +- core/commands2/mount_unix.go | 2 +- core/commands2/object.go | 2 +- core/commands2/publish.go | 2 +- core/commands2/refs.go | 2 +- core/commands2/resolve.go | 2 +- core/commands2/root.go | 2 +- core/commands2/update.go | 4 ++-- core/commands2/version.go | 2 +- 19 files changed, 45 insertions(+), 41 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 2c90cb012..782491e56 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -177,7 +177,7 @@ func (i *cmdInvocation) Parse(args []string) error { // if no encoding was specified by user, default to plaintext encoding // (if command doesn't support plaintext, use JSON instead) if !i.req.Option("encoding").Found() { - if i.req.Command().Marshallers != nil && i.req.Command().Marshallers[cmds.Text] != nil { + if i.req.Command().Marshalers != nil && i.req.Command().Marshalers[cmds.Text] != nil { i.req.SetOption("encoding", cmds.Text) } else { i.req.SetOption("encoding", cmds.JSON) diff --git a/commands/command.go b/commands/command.go index 2ce2cd382..0301e9d1b 100644 --- a/commands/command.go +++ b/commands/command.go @@ -16,9 +16,13 @@ var log = u.Logger("command") // It reads from the Request, and writes results to the Response. type Function func(Request) (interface{}, error) -// Marshaller is a function that takes in a Response, and returns a marshalled []byte +// Marshaler is a function that takes in a Response, and returns a marshalled []byte // (or an error on failure) -type Marshaller func(Response) ([]byte, error) +type Marshaler func(Response) ([]byte, error) + +// MarshalerMap is a map of Marshaler functions, keyed by EncodingType +// (or an error on failure) +type MarshalerMap map[EncodingType]Marshaler // HelpText is a set of strings used to generate command help text. The help // text follows formats similar to man pages, but not exactly the same. @@ -45,11 +49,11 @@ type HelpText struct { // Command is a runnable command, with input arguments and options (flags). // It can also have Subcommands, to group units of work into sets. type Command struct { - Options []Option - Arguments []Argument - Run Function - Marshallers map[EncodingType]Marshaller - Helptext HelpText + Options []Option + Arguments []Argument + Run Function + Marshalers map[EncodingType]Marshaler + Helptext HelpText // Type describes the type of the output of the Command's Run Function. // In precise terms, the value of Type is an instance of the return type of diff --git a/commands/response.go b/commands/response.go index 74b6b5e07..7e12e5922 100644 --- a/commands/response.go +++ b/commands/response.go @@ -40,7 +40,7 @@ const ( // TODO: support more encoding types ) -var marshallers = map[EncodingType]Marshaller{ +var marshallers = map[EncodingType]Marshaler{ JSON: func(res Response) ([]byte, error) { if res.Error() != nil { return json.MarshalIndent(res.Error(), "", " ") @@ -69,7 +69,7 @@ type Response interface { Output() interface{} // Marshal marshals out the response into a buffer. It uses the EncodingType - // on the Request to chose a Marshaller (Codec). + // on the Request to chose a Marshaler (Codec). Marshal() ([]byte, error) // Gets a io.Reader that reads the marshalled output @@ -122,9 +122,9 @@ func (r *response) Marshal() ([]byte, error) { return []byte(r.Error().Error()), nil } - var marshaller Marshaller - if r.req.Command() != nil && r.req.Command().Marshallers != nil { - marshaller = r.req.Command().Marshallers[encType] + var marshaller Marshaler + if r.req.Command() != nil && r.req.Command().Marshalers != nil { + marshaller = r.req.Command().Marshalers[encType] } if marshaller == nil { var ok bool diff --git a/core/commands2/add.go b/core/commands2/add.go index 8dfd26ad2..4af5aeb3a 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -171,7 +171,7 @@ remains to be implemented. // // return &AddOutput{added}, nil }, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { val, ok := res.Output().(*AddOutput) if !ok { diff --git a/core/commands2/block.go b/core/commands2/block.go index 3a5c5b225..84a3c304e 100644 --- a/core/commands2/block.go +++ b/core/commands2/block.go @@ -121,7 +121,7 @@ It reads from stdin, and is a base58 encoded multihash. }, nil }, Type: &Block{}, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { block := res.Output().(*Block) s := fmt.Sprintf("Block added (%v bytes): %s\n", block.Length, block.Key) diff --git a/core/commands2/bootstrap.go b/core/commands2/bootstrap.go index 738dcb2a4..226b1f169 100644 --- a/core/commands2/bootstrap.go +++ b/core/commands2/bootstrap.go @@ -21,7 +21,7 @@ var peerOptionDesc = "A peer to add to the bootstrap list (in the format '... - Add peers to the bootstrap list ipfs bootstrap remove ... - Removes peers from the bootstrap list @@ -31,9 +31,9 @@ Running 'ipfs bootstrap' with no arguments will run 'ipfs bootstrap list'. ` + bootstrapSecurityWarning, }, - Run: bootstrapListCmd.Run, - Marshallers: bootstrapListCmd.Marshallers, - Type: bootstrapListCmd.Type, + Run: bootstrapListCmd.Run, + Marshalers: bootstrapListCmd.Marshalers, + Type: bootstrapListCmd.Type, Subcommands: map[string]*cmds.Command{ "list": bootstrapListCmd, @@ -77,11 +77,11 @@ in the bootstrap list). return &BootstrapOutput{added}, nil }, Type: &BootstrapOutput{}, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { v := res.Output().(*BootstrapOutput) s := fmt.Sprintf("Added %v peers to the bootstrap list:\n", len(v.Peers)) - marshalled, err := bootstrapMarshaller(res) + marshalled, err := bootstrapMarshaler(res) if err != nil { return nil, err } @@ -124,11 +124,11 @@ var bootstrapRemoveCmd = &cmds.Command{ return &BootstrapOutput{removed}, nil }, Type: &BootstrapOutput{}, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { v := res.Output().(*BootstrapOutput) s := fmt.Sprintf("Removed %v peers from the bootstrap list:\n", len(v.Peers)) - marshalled, err := bootstrapMarshaller(res) + marshalled, err := bootstrapMarshaler(res) if err != nil { return nil, err } @@ -153,12 +153,12 @@ var bootstrapListCmd = &cmds.Command{ return &BootstrapOutput{peers}, nil }, Type: &BootstrapOutput{}, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ - cmds.Text: bootstrapMarshaller, + Marshalers: cmds.MarshalerMap{ + cmds.Text: bootstrapMarshaler, }, } -func bootstrapMarshaller(res cmds.Response) ([]byte, error) { +func bootstrapMarshaler(res cmds.Response) ([]byte, error) { v, ok := res.Output().(*BootstrapOutput) if !ok { return nil, u.ErrCast() diff --git a/core/commands2/commands.go b/core/commands2/commands.go index 8ce2b0e06..63124861d 100644 --- a/core/commands2/commands.go +++ b/core/commands2/commands.go @@ -25,7 +25,7 @@ func CommandsCmd(root *cmds.Command) *cmds.Command { root := cmd2outputCmd("ipfs", root) return &root, nil }, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { v := res.Output().(*Command) var buf bytes.Buffer diff --git a/core/commands2/config.go b/core/commands2/config.go index 5f7cfdd93..8a4b1bdb6 100644 --- a/core/commands2/config.go +++ b/core/commands2/config.go @@ -80,7 +80,7 @@ Set the value of the 'datastore.path' key: return getConfig(filename, key) } }, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { if len(res.Request().Arguments()) == 2 { return nil, nil // dont output anything diff --git a/core/commands2/diag.go b/core/commands2/diag.go index 8fd3db284..8a68a02ba 100644 --- a/core/commands2/diag.go +++ b/core/commands2/diag.go @@ -85,7 +85,7 @@ connected peers and latencies between them. return &DiagnosticOutput{output}, nil }, Type: &DiagnosticOutput{}, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + Marshalers: cmds.MarshalerMap{ cmds.Text: func(r cmds.Response) ([]byte, error) { output, ok := r.Output().(*DiagnosticOutput) if !ok { diff --git a/core/commands2/log.go b/core/commands2/log.go index 157bdca27..ace008d9f 100644 --- a/core/commands2/log.go +++ b/core/commands2/log.go @@ -49,8 +49,8 @@ output of a running daemon. log.Info(s) return &MessageOutput{s}, nil }, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ - cmds.Text: MessageTextMarshaller, + Marshalers: cmds.MarshalerMap{ + cmds.Text: MessageTextMarshaler, }, Type: &MessageOutput{}, } diff --git a/core/commands2/ls.go b/core/commands2/ls.go index d948f635e..d07a207e8 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -73,7 +73,7 @@ it contains, with the following format: return &LsOutput{output}, nil }, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { s := "" output := res.Output().(*LsOutput).Objects diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go index 490bce154..bc6e62541 100644 --- a/core/commands2/mount_unix.go +++ b/core/commands2/mount_unix.go @@ -88,7 +88,7 @@ not be listable, as it is virtual. Accessing known paths directly. } }, Type: &config.Mounts{}, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { v := res.Output().(*config.Mounts) s := fmt.Sprintf("IPFS mounted at: %s\n", v.IPFS) diff --git a/core/commands2/object.go b/core/commands2/object.go index dc5be0a0f..bf4a7cec6 100644 --- a/core/commands2/object.go +++ b/core/commands2/object.go @@ -150,7 +150,7 @@ This command outputs data in the following encodings: return node, nil }, Type: &Node{}, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + Marshalers: cmds.MarshalerMap{ cmds.EncodingType("protobuf"): func(res cmds.Response) ([]byte, error) { object := res.Output().(*dag.Node) return object.Marshal() diff --git a/core/commands2/publish.go b/core/commands2/publish.go index ab0e81536..f7e76ba0a 100644 --- a/core/commands2/publish.go +++ b/core/commands2/publish.go @@ -78,7 +78,7 @@ Publish a to another public key: k := n.Identity.PrivKey() return publish(n, k, ref) }, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { v := res.Output().(*IpnsEntry) s := fmt.Sprintf("Published name %s to %s\n", v.Name, v.Value) diff --git a/core/commands2/refs.go b/core/commands2/refs.go index 00cb5e7b4..d3c243405 100644 --- a/core/commands2/refs.go +++ b/core/commands2/refs.go @@ -65,7 +65,7 @@ Note: list all refs recursively with -r. return getRefs(n, paths, unique, recursive) }, Type: &RefsOutput{}, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { output := res.Output().(*RefsOutput) s := "" diff --git a/core/commands2/resolve.go b/core/commands2/resolve.go index 0f9287303..ebd47ed96 100644 --- a/core/commands2/resolve.go +++ b/core/commands2/resolve.go @@ -73,7 +73,7 @@ Resolve te value of another name: return output, nil }, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { output := res.Output().(string) return []byte(output), nil diff --git a/core/commands2/root.go b/core/commands2/root.go index d2372dcc5..798bcd7cf 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -79,6 +79,6 @@ type MessageOutput struct { Message string } -func MessageTextMarshaller(res cmds.Response) ([]byte, error) { +func MessageTextMarshaler(res cmds.Response) ([]byte, error) { return []byte(res.Output().(*MessageOutput).Message), nil } diff --git a/core/commands2/update.go b/core/commands2/update.go index 8dbfb3e54..6a3c77a4e 100644 --- a/core/commands2/update.go +++ b/core/commands2/update.go @@ -32,7 +32,7 @@ var updateCmd = &cmds.Command{ "check": updateCheckCmd, "log": updateLogCmd, }, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { v := res.Output().(*UpdateOutput) s := "" @@ -65,7 +65,7 @@ Nothing will be downloaded or installed. return updateCheck(n) }, Type: &UpdateOutput{}, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { v := res.Output().(*UpdateOutput) s := "" diff --git a/core/commands2/version.go b/core/commands2/version.go index 575f57f6c..f9f5a33f3 100644 --- a/core/commands2/version.go +++ b/core/commands2/version.go @@ -23,7 +23,7 @@ var versionCmd = &cmds.Command{ Version: config.CurrentVersionNumber, }, nil }, - Marshallers: map[cmds.EncodingType]cmds.Marshaller{ + Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { v := res.Output().(*VersionOutput) s := "" From 89ec480ef1ec9cd80e84a84e0658e3fce77d77f1 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 13 Nov 2014 01:20:50 -0800 Subject: [PATCH 334/383] cmds2: bootstrap command fix add --- core/commands2/bootstrap.go | 74 ++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/core/commands2/bootstrap.go b/core/commands2/bootstrap.go index 226b1f169..3217cb66e 100644 --- a/core/commands2/bootstrap.go +++ b/core/commands2/bootstrap.go @@ -1,7 +1,8 @@ package commands import ( - "fmt" + "bytes" + "io" "strings" ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" @@ -38,7 +39,7 @@ Running 'ipfs bootstrap' with no arguments will run 'ipfs bootstrap list'. Subcommands: map[string]*cmds.Command{ "list": bootstrapListCmd, "add": bootstrapAddCmd, - "remove": bootstrapRemoveCmd, + "rm": bootstrapRemoveCmd, }, } @@ -79,13 +80,14 @@ in the bootstrap list). Type: &BootstrapOutput{}, Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { - v := res.Output().(*BootstrapOutput) - s := fmt.Sprintf("Added %v peers to the bootstrap list:\n", len(v.Peers)) - marshalled, err := bootstrapMarshaler(res) - if err != nil { - return nil, err + v, ok := res.Output().(*BootstrapOutput) + if !ok { + return nil, u.ErrCast() } - return append([]byte(s), marshalled...), nil + + var buf bytes.Buffer + err := bootstrapWritePeers(&buf, "added ", v.Peers) + return buf.Bytes(), err }, }, } @@ -126,13 +128,14 @@ var bootstrapRemoveCmd = &cmds.Command{ Type: &BootstrapOutput{}, Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { - v := res.Output().(*BootstrapOutput) - s := fmt.Sprintf("Removed %v peers from the bootstrap list:\n", len(v.Peers)) - marshalled, err := bootstrapMarshaler(res) - if err != nil { - return nil, err + v, ok := res.Output().(*BootstrapOutput) + if !ok { + return nil, u.ErrCast() } - return append([]byte(s), marshalled...), nil + + var buf bytes.Buffer + err := bootstrapWritePeers(&buf, "removed ", v.Peers) + return buf.Bytes(), err }, }, } @@ -164,15 +167,33 @@ func bootstrapMarshaler(res cmds.Response) ([]byte, error) { return nil, u.ErrCast() } - s := "" - for _, peer := range v.Peers { - s += fmt.Sprintf("%s/%s\n", peer.Address, peer.PeerID) - } + var buf bytes.Buffer + err := bootstrapWritePeers(&buf, "", v.Peers) + return buf.Bytes(), err +} - return []byte(s), nil +func bootstrapWritePeers(w io.Writer, prefix string, peers []*config.BootstrapPeer) error { + + for _, peer := range peers { + s := prefix + peer.Address + "/" + peer.PeerID + "\n" + _, err := w.Write([]byte(s)) + if err != nil { + return err + } + } + return nil } func bootstrapInputToPeers(input []interface{}) ([]*config.BootstrapPeer, error) { + inputAddrs := make([]string, len(input)) + for i, v := range input { + addr, ok := v.(string) + if !ok { + return nil, u.ErrCast() + } + inputAddrs[i] = addr + } + split := func(addr string) (string, string) { idx := strings.LastIndex(addr, "/") if idx == -1 { @@ -182,12 +203,7 @@ func bootstrapInputToPeers(input []interface{}) ([]*config.BootstrapPeer, error) } peers := []*config.BootstrapPeer{} - for _, v := range input { - addr, ok := v.(string) - if !ok { - return nil, u.ErrCast() - } - + for _, addr := range inputAddrs { addrS, peeridS := split(addr) // make sure addrS parses as a multiaddr. @@ -221,7 +237,7 @@ func bootstrapAdd(filename string, cfg *config.Config, peers []*config.Bootstrap for _, peer := range peers { duplicate := false for _, peer2 := range cfg.Bootstrap { - if peer.Address == peer2.Address { + if peer.Address == peer2.Address && peer.PeerID == peer2.PeerID { duplicate = true break } @@ -241,13 +257,13 @@ func bootstrapAdd(filename string, cfg *config.Config, peers []*config.Bootstrap return added, nil } -func bootstrapRemove(filename string, cfg *config.Config, peers []*config.BootstrapPeer) ([]*config.BootstrapPeer, error) { - removed := make([]*config.BootstrapPeer, 0, len(peers)) +func bootstrapRemove(filename string, cfg *config.Config, toRemove []*config.BootstrapPeer) ([]*config.BootstrapPeer, error) { + removed := make([]*config.BootstrapPeer, 0, len(toRemove)) keep := make([]*config.BootstrapPeer, 0, len(cfg.Bootstrap)) for _, peer := range cfg.Bootstrap { found := false - for _, peer2 := range peers { + for _, peer2 := range toRemove { if peer.Address == peer2.Address && peer.PeerID == peer2.PeerID { found = true removed = append(removed, peer) From a00da7b72836d723d0dc054910914790ce9eb00b Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 00:03:27 -0800 Subject: [PATCH 335/383] add debug log statement License: MIT Signed-off-by: Brian Tiger Chow --- core/commands2/block.go | 1 + 1 file changed, 1 insertion(+) diff --git a/core/commands2/block.go b/core/commands2/block.go index 84a3c304e..0379e5fcb 100644 --- a/core/commands2/block.go +++ b/core/commands2/block.go @@ -74,6 +74,7 @@ It outputs to stdout, and is a base58 encoded multihash. if err != nil { return nil, err } + log.Debugf("BlockGet key: '%q'", b.Key()) return bytes.NewReader(b.Data), nil }, From ff6c4ce6a429507a997d498ad04050bd1280fe5f Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 01:09:44 -0800 Subject: [PATCH 336/383] test(tour) ensure template renders without failure License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/tour.go | 43 +++++++++++++++++++++++++++++++----------- cmd/ipfs2/tour_test.go | 24 +++++++++++++++++++++++ 2 files changed, 56 insertions(+), 11 deletions(-) create mode 100644 cmd/ipfs2/tour_test.go diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index efca847f7..e9f3226de 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -3,6 +3,7 @@ package main import ( "bytes" "fmt" + "html/template" "io" "os" @@ -45,12 +46,17 @@ IPFS very quickly. To start, run: return nil, err } - topic := tour.TopicID(cfg.Tour.Last) + id := tour.TopicID(cfg.Tour.Last) if len(strs) > 0 { - topic = tour.TopicID(strs[0]) + id = tour.TopicID(strs[0]) } - err = tourShow(out, topic) + t, err := tourGet(id) + if err != nil { + return nil, err + } + + err = tourShow(out, t) if err != nil { return nil, err } @@ -72,14 +78,18 @@ var cmdIpfsTourNext = &cmds.Command{ return nil, err } - topic := tour.NextTopic(tour.TopicID(cfg.Tour.Last)) + id := tour.NextTopic(tour.TopicID(cfg.Tour.Last)) + topic, err := tourGet(id) + if err != nil { + return nil, err + } if err := tourShow(&w, topic); err != nil { return nil, err } // topic changed, not last. write it out. - if string(topic) != cfg.Tour.Last { - cfg.Tour.Last = string(topic) + if string(id) != cfg.Tour.Last { + cfg.Tour.Last = string(id) err := writeConfig(path, cfg) if err != nil { return nil, err @@ -147,14 +157,25 @@ func tourListCmd(w io.Writer, cfg *config.Config) { } } -func tourShow(w io.Writer, id tour.ID) error { +func tourShow(w io.Writer, t *tour.Topic) error { + tmpl := ` +Tour {{ .ID }} - {{ .Title }} + +{{ .Text }} + ` + ttempl, err := template.New("tour").Parse(tmpl) + if err != nil { + return err + } + return ttempl.Execute(w, t) +} + +func tourGet(id tour.ID) (*tour.Topic, error) { t, found := tour.Topics[id] if !found { - return fmt.Errorf("no topic with id: %s", id) + return nil, fmt.Errorf("no topic with id: %s", id) } - - fmt.Fprintf(w, "Tour %s - %s\n\n%s\n", t.ID, t.Title, t.Text) - return nil + return &t, nil } // TODO share func diff --git a/cmd/ipfs2/tour_test.go b/cmd/ipfs2/tour_test.go new file mode 100644 index 000000000..ae6089e56 --- /dev/null +++ b/cmd/ipfs2/tour_test.go @@ -0,0 +1,24 @@ +package main + +import ( + "bytes" + "testing" + + "github.com/jbenet/go-ipfs/tour" +) + +func TestParseTourTemplate(t *testing.T) { + topic := &tour.Topic{ + ID: "42", + Title: "IPFS CLI test files", + Text: `Welcome to the IPFS test files + This is where we test our beautiful command line interfaces + `, + } + var buf bytes.Buffer + err := tourShow(&buf, topic) + if err != nil { + t.Fatal(err) + } + t.Log(buf.String()) +} From 815efdb12582d4fbf80846d97d850a21ad093a27 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 13 Nov 2014 01:36:17 -0800 Subject: [PATCH 337/383] daemon: Added a ShortDescription --- cmd/ipfs2/daemon.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cmd/ipfs2/daemon.go b/cmd/ipfs2/daemon.go index 3a463d90a..51c1cde49 100644 --- a/cmd/ipfs2/daemon.go +++ b/cmd/ipfs2/daemon.go @@ -16,7 +16,14 @@ import ( var daemonCmd = &cmds.Command{ Helptext: cmds.HelpText{ - Tagline: "run a network-connected ipfs node", // TODO adjust copy + Tagline: "Run a network-connected IPFS node", + ShortDescription: ` +'ipfs daemon' runs a persistent IPFS daemon that can serve commands +over the network. Most applications that use IPFS will do so by +communicating with a daemon over the HTTP API. While the daemon is +running, calls to 'ipfs' commands will be sent over the network to +the daemon. +`, }, Options: []cmds.Option{}, From e35453c50d2cc27f098219e0577a1d969b1a4509 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 13 Nov 2014 02:04:52 -0800 Subject: [PATCH 338/383] mount: Cleaned up option descriptions --- core/commands2/mount_unix.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go index bc6e62541..016263294 100644 --- a/core/commands2/mount_unix.go +++ b/core/commands2/mount_unix.go @@ -29,10 +29,10 @@ not be listable, as it is virtual. Accessing known paths directly. Options: []cmds.Option{ // TODO longform - cmds.StringOption("f", "The path where IPFS should be mounted\n(default is '/ipfs')"), + cmds.StringOption("f", "The path where IPFS should be mounted"), // TODO longform - cmds.StringOption("n", "The path where IPNS should be mounted\n(default is '/ipns')"), + cmds.StringOption("n", "The path where IPNS should be mounted"), }, Run: func(req cmds.Request) (interface{}, error) { cfg, err := req.Context().GetConfig() From 120ead26f4c31ffa9059571ac4e74686407ffb9d Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 13 Nov 2014 02:22:44 -0800 Subject: [PATCH 339/383] resolve: Added a TODO --- core/commands2/resolve.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/commands2/resolve.go b/core/commands2/resolve.go index ebd47ed96..db67cfc86 100644 --- a/core/commands2/resolve.go +++ b/core/commands2/resolve.go @@ -71,6 +71,8 @@ Resolve te value of another name: return nil, err } + // TODO: better errors (in the case of not finding the name, we get "failed to find any peer in table") + return output, nil }, Marshalers: cmds.MarshalerMap{ From db361d94034daeae882d0bcf3bca615b5dd8bb8f Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 13 Nov 2014 02:44:43 -0800 Subject: [PATCH 340/383] commands/cli: Made Parse return an error if request has unrecognized options --- cmd/ipfs2/main.go | 2 +- commands/cli/parse.go | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 782491e56..454eb48b9 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -71,7 +71,7 @@ func main() { // BEFORE handling the parse error, if we have enough information // AND the user requested help, print it out and exit - if invoc.cmd != nil { + if invoc.req != nil { longH, shortH, err := invoc.requestedHelp() if err != nil { printErr(err) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index b84ecca76..2940f7268 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -37,6 +37,14 @@ func Parse(input []string, root *cmds.Command) (cmds.Request, *cmds.Command, []s return nil, cmd, path, err } + // check to make sure there aren't any undefined options + for k := range opts { + if _, found := optDefs[k]; !found { + err = fmt.Errorf("Unrecognized option: -%s", k) + return nil, cmd, path, err + } + } + req := cmds.NewRequest(path, opts, args, cmd, optDefs) err = cmd.CheckArguments(req) From 0a93f9d7640d4f6c461b05aefb1415c34169208e Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 01:51:16 -0800 Subject: [PATCH 341/383] style(tour) newline at the end License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/tour.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index e9f3226de..bbc1ed1c1 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -162,7 +162,8 @@ func tourShow(w io.Writer, t *tour.Topic) error { Tour {{ .ID }} - {{ .Title }} {{ .Text }} - ` + +` ttempl, err := template.New("tour").Parse(tmpl) if err != nil { return err From c5e75f91a6a108dd35eaf37d431aeb0099bab936 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 02:28:49 -0800 Subject: [PATCH 342/383] tests(2/main) errClient Discovered this quirk about interfaces. @whyrusleeping @mappum @jbenet License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/main.go | 14 ++++++++++---- cmd/ipfs2/main_test.go | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 cmd/ipfs2/main_test.go diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 454eb48b9..db3a5c53b 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -263,12 +263,18 @@ func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { } func isClientError(err error) bool { + + // Somewhat suprisingly, the pointer cast fails to recognize commands.Error + // passed as values, so we check both. + // cast to cmds.Error - cmdErr, ok := err.(*cmds.Error) - if !ok { - return false + switch e := err.(type) { + case *cmds.Error: + return e.Code == cmds.ErrClient + case cmds.Error: + return e.Code == cmds.ErrClient } - return cmdErr.Code == cmds.ErrClient + return false } func getConfigRoot(req cmds.Request) (string, error) { diff --git a/cmd/ipfs2/main_test.go b/cmd/ipfs2/main_test.go new file mode 100644 index 000000000..efbb1b620 --- /dev/null +++ b/cmd/ipfs2/main_test.go @@ -0,0 +1,17 @@ +package main + +import ( + "testing" + + "github.com/jbenet/go-ipfs/commands" +) + +func TestIsCientErr(t *testing.T) { + t.Log("Catch both pointers and values") + if !isClientError(commands.Error{Code: commands.ErrClient}) { + t.Errorf("misidentified value") + } + if !isClientError(&commands.Error{Code: commands.ErrClient}) { + t.Errorf("misidentified pointer") + } +} From ca2828f33c457fa105e890f8738049d92893bfae Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 02:34:04 -0800 Subject: [PATCH 343/383] feat(commands) add ClientError(msg) helper and use it to return a fancy error to the client in the tour @jbenet this exists now License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/tour.go | 2 +- commands/command.go | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index bbc1ed1c1..da0d6f422 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -53,7 +53,7 @@ IPFS very quickly. To start, run: t, err := tourGet(id) if err != nil { - return nil, err + return nil, cmds.ClientError(err.Error()) } err = tourShow(out, t) diff --git a/commands/command.go b/commands/command.go index 0301e9d1b..5e031338e 100644 --- a/commands/command.go +++ b/commands/command.go @@ -65,9 +65,9 @@ type Command struct { } // ErrNotCallable signals a command that cannot be called. -var ErrNotCallable = errors.New("This command can't be called directly. Try one of its subcommands.") +var ErrNotCallable = ClientError("This command can't be called directly. Try one of its subcommands.") -var ErrNoFormatter = errors.New("This command cannot be formatted to plain text") +var ErrNoFormatter = ClientError("This command cannot be formatted to plain text") var ErrIncorrectType = errors.New("The command returned a value with a different type than expected") @@ -276,3 +276,7 @@ func checkArgValue(v interface{}, def Argument) error { return nil } + +func ClientError(msg string) error { + return &Error{Code: ErrClient, Message: msg} +} From ef0826acd621a65be8ccb4cb1977b83d1b615e4d Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 03:43:07 -0800 Subject: [PATCH 344/383] fix(commands/err) I didn't know there were dragons here. When casting errors we've gotta be careful. Apparently both values and pointers satisfy the error interface. Type checking for one doesn't catch the other. cc @whyrusleeping @mappum @jbenet License: MIT Signed-off-by: Brian Tiger Chow --- commands/command.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/commands/command.go b/commands/command.go index 5e031338e..383e9f217 100644 --- a/commands/command.go +++ b/commands/command.go @@ -103,11 +103,12 @@ func (c *Command) Call(req Request) Response { if err != nil { // if returned error is a commands.Error, use its error code // otherwise, just default the code to ErrNormal - var e Error - e, ok := err.(Error) - if ok { + switch e := err.(type) { + case *Error: res.SetError(e, e.Code) - } else { + case Error: + res.SetError(e, e.Code) + default: res.SetError(err, ErrNormal) } return res From bb8d4ebd6ba6e2004c3b03512dc57ff6c5d9677a Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 13 Nov 2014 03:42:55 -0800 Subject: [PATCH 345/383] cmds2: cmdDetailsMap --- cmd/ipfs2/ipfs.go | 32 ++++++++++++++++++++++++++++---- cmd/ipfs2/tour.go | 2 +- core/commands2/bootstrap.go | 6 +++--- core/commands2/diag.go | 2 +- core/commands2/log.go | 2 +- core/commands2/root.go | 13 ++++++++----- core/commands2/update.go | 2 +- core/commands2/version.go | 2 +- 8 files changed, 44 insertions(+), 17 deletions(-) diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go index a149e7eb1..e12d60376 100644 --- a/cmd/ipfs2/ipfs.go +++ b/cmd/ipfs2/ipfs.go @@ -13,13 +13,16 @@ var Root = &cmds.Command{ Helptext: commands.Root.Helptext, } +// commandsClientCmd is the "ipfs commands" command for local cli +var commandsClientCmd = commands.CommandsCmd(Root) + // Commands in localCommands should always be run locally (even if daemon is running). // They can override subcommands in commands.Root by defining a subcommand with the same name. var localCommands = map[string]*cmds.Command{ - "daemon": daemonCmd, // TODO name - "init": initCmd, // TODO name - "tour": cmdTour, - "commands": commands.CommandsCmd(Root), + "daemon": daemonCmd, + "init": initCmd, + "tour": tourCmd, + "commands": commandsClientCmd, } var localMap = make(map[*cmds.Command]bool) @@ -45,3 +48,24 @@ func isLocal(cmd *cmds.Command) bool { _, found := localMap[cmd] return found } + +type cmdDetails struct { + cannotRunOnClient bool + cannotRunOnDaemon bool + doesNotUseRepo bool +} + +// "What is this madness!?" you ask. Our commands have the unfortunate problem of +// not being able to run on all the same contexts. This map describes these +// properties so that other code can make decisions about whether to invoke a +// command or return an error to the user. +var cmdDetailsMap = map[*cmds.Command]cmdDetails{ + initCmd: cmdDetails{cannotRunOnDaemon: true, doesNotUseRepo: true}, + daemonCmd: cmdDetails{cannotRunOnDaemon: true}, + commandsClientCmd: cmdDetails{doesNotUseRepo: true}, + commands.CommandsDaemonCmd: cmdDetails{doesNotUseRepo: true}, + commands.DiagCmd: cmdDetails{cannotRunOnClient: true}, + commands.VersionCmd: cmdDetails{doesNotUseRepo: true}, + commands.UpdateCmd: cmdDetails{cannotRunOnDaemon: true}, + commands.LogCmd: cmdDetails{cannotRunOnClient: true}, +} diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index da0d6f422..6baa4c5c3 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -13,7 +13,7 @@ import ( tour "github.com/jbenet/go-ipfs/tour" ) -var cmdTour = &cmds.Command{ +var tourCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "An introduction to IPFS", ShortDescription: ` diff --git a/core/commands2/bootstrap.go b/core/commands2/bootstrap.go index 3217cb66e..825310c8e 100644 --- a/core/commands2/bootstrap.go +++ b/core/commands2/bootstrap.go @@ -37,9 +37,9 @@ Running 'ipfs bootstrap' with no arguments will run 'ipfs bootstrap list'. Type: bootstrapListCmd.Type, Subcommands: map[string]*cmds.Command{ - "list": bootstrapListCmd, - "add": bootstrapAddCmd, - "rm": bootstrapRemoveCmd, + "list": bootstrapListCmd, + "add": bootstrapAddCmd, + "rm": bootstrapRemoveCmd, }, } diff --git a/core/commands2/diag.go b/core/commands2/diag.go index 8a68a02ba..764ac3f8b 100644 --- a/core/commands2/diag.go +++ b/core/commands2/diag.go @@ -28,7 +28,7 @@ type DiagnosticOutput struct { Peers []DiagnosticPeer } -var diagCmd = &cmds.Command{ +var DiagCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Generates diagnostic reports", }, diff --git a/core/commands2/log.go b/core/commands2/log.go index ace008d9f..1c821dbdc 100644 --- a/core/commands2/log.go +++ b/core/commands2/log.go @@ -13,7 +13,7 @@ import ( // we convert it at this step. var logAllKeyword = "all" -var logCmd = &cmds.Command{ +var LogCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Change the logging level", ShortDescription: ` diff --git a/core/commands2/root.go b/core/commands2/root.go index 798bcd7cf..bcf58f565 100644 --- a/core/commands2/root.go +++ b/core/commands2/root.go @@ -51,21 +51,24 @@ Plumbing commands: }, } +// commandsDaemonCmd is the "ipfs commands" command for daemon +var CommandsDaemonCmd = CommandsCmd(Root) + var rootSubcommands = map[string]*cmds.Command{ "cat": catCmd, "ls": lsCmd, - "commands": CommandsCmd(Root), + "commands": CommandsDaemonCmd, "name": nameCmd, "add": addCmd, - "log": logCmd, - "diag": diagCmd, + "log": LogCmd, + "diag": DiagCmd, "pin": pinCmd, - "version": versionCmd, + "version": VersionCmd, "config": configCmd, "bootstrap": bootstrapCmd, "mount": mountCmd, "block": blockCmd, - "update": updateCmd, + "update": UpdateCmd, "object": objectCmd, "refs": refsCmd, } diff --git a/core/commands2/update.go b/core/commands2/update.go index 6a3c77a4e..5e14a47e3 100644 --- a/core/commands2/update.go +++ b/core/commands2/update.go @@ -14,7 +14,7 @@ type UpdateOutput struct { NewVersion string } -var updateCmd = &cmds.Command{ +var UpdateCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Downloads and installs updates for IPFS", ShortDescription: "ipfs update is a utility command used to check for updates and apply them.", diff --git a/core/commands2/version.go b/core/commands2/version.go index f9f5a33f3..ce58fb4a5 100644 --- a/core/commands2/version.go +++ b/core/commands2/version.go @@ -9,7 +9,7 @@ type VersionOutput struct { Version string } -var versionCmd = &cmds.Command{ +var VersionCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Outputs the current version of IPFS", ShortDescription: "Returns the version number of IPFS and exits.", From afa5eedb44f9226ddba8bef70e107b9e84cc84e8 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 13 Nov 2014 04:18:18 -0800 Subject: [PATCH 346/383] cmds2: commandShouldRunOnDaemon This commit adds the pretty-complicated decision function to check whether a command should run on the daemon. @maybebtc @mappum double check the logic? --- cmd/ipfs2/main.go | 57 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index db3a5c53b..800fe6d3c 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -202,15 +202,12 @@ func (i *cmdInvocation) requestedHelp() (short bool, long bool, err error) { func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { var res cmds.Response - local, found, err := req.Option("local").Bool() + useDaemon, err := commandShouldRunOnDaemon(req, root) if err != nil { return nil, err } - remote := !isLocal(req.Command()) && (!found || !local) - - log.Info("Checking if daemon is running...") - if remote && daemon.Locked(req.Context().ConfigRoot) { + if useDaemon { cfg, err := req.Context().GetConfig() if err != nil { @@ -222,6 +219,7 @@ func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { return nil, err } + log.Infof("Executing command on daemon running at %s", addr) _, host, err := manet.DialArgs(addr) if err != nil { return nil, err @@ -235,7 +233,7 @@ func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { } } else { - log.Info("Executing command locally: daemon not running") + log.Info("Executing command locally") // this sets up the function that will initialize the node // this is so that we can construct the node lazily. @@ -262,6 +260,53 @@ func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { return res, nil } +func commandShouldRunOnDaemon(req cmds.Request, root *cmds.Command) (bool, error) { + path := req.Path() + // root command. + if len(path) < 1 { + return false, nil + } + + cmd, found := root.Subcommands[path[0]] + if !found { + return false, fmt.Errorf("subcommand %s should be in root", path[0]) + } + + details, found := cmdDetailsMap[cmd] + if !found { + details = cmdDetails{} // defaults + } + + if details.cannotRunOnClient && details.cannotRunOnDaemon { + return false, fmt.Errorf("command disabled: %s", path[0]) + } + + if details.doesNotUseRepo && !details.cannotRunOnClient { + return false, nil + } + + // at this point need to know whether daemon is running. we defer + // to this point so that some commands dont open files unnecessarily. + daemonLocked := daemon.Locked(req.Context().ConfigRoot) + log.Info("Daemon is running.") + + if daemonLocked { + + if details.cannotRunOnDaemon { + e := "ipfs daemon is running. please stop it to run this command" + return false, cmds.ClientError(e) + } + + return true, nil + } + + if details.cannotRunOnClient { + return false, cmds.ClientError("must run on the ipfs daemon") + } + + return false, nil +} + func isClientError(err error) bool { // Somewhat suprisingly, the pointer cast fails to recognize commands.Error From 5c4fa5a7834e4742b4d8f42732200ab1738a64db Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 15:20:18 -0800 Subject: [PATCH 347/383] feat(tour) show list of topics when user tries to view topic that doesn't exist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit eg. ``` ipfs2 (cmd-ref-part2) λ. go build ./...; ./ipfs2 tour 0 Tour 0 - Hello Mars Hello Mars ipfs2 (cmd-ref-part2) λ. go build ./...; ./ipfs2 tour 10 ERROR no topic with id: 10 TOPICS 0 - Hello Mars 0.1 - Hello Mars 2 ``` --- cmd/ipfs2/tour.go | 129 +++++++++++++++++++++++++++++++---------- cmd/ipfs2/tour_test.go | 51 +++++++++++++++- 2 files changed, 148 insertions(+), 32 deletions(-) diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index 6baa4c5c3..ed9cd43f7 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -11,8 +11,12 @@ import ( config "github.com/jbenet/go-ipfs/config" internal "github.com/jbenet/go-ipfs/core/commands2/internal" tour "github.com/jbenet/go-ipfs/tour" + "github.com/jbenet/go-ipfs/util" ) +// TODO the parent function now uses tourOutput. Migrate the children to also +// use the tourOutput struct + var tourCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "An introduction to IPFS", @@ -33,38 +37,101 @@ IPFS very quickly. To start, run: "next": cmdIpfsTourNext, "restart": cmdIpfsTourRestart, }, - Run: func(req cmds.Request) (interface{}, error) { - - out := new(bytes.Buffer) - cfg, err := req.Context().GetConfig() - if err != nil { - return nil, err - } - - strs, err := internal.CastToStrings(req.Arguments()) - if err != nil { - return nil, err - } - - id := tour.TopicID(cfg.Tour.Last) - if len(strs) > 0 { - id = tour.TopicID(strs[0]) - } - - t, err := tourGet(id) - if err != nil { - return nil, cmds.ClientError(err.Error()) - } - - err = tourShow(out, t) - if err != nil { - return nil, err - } - - return out, nil + Run: tourRunFunc, + Marshalers: cmds.MarshalerMap{ + cmds.Text: tourTextMarshaler, }, + Type: &tourOutput{}, } +// tourOutput is a union type. It either contains a Topic or it contains the +// list of Topics and an Error. +type tourOutput struct { + Topic *tour.Topic + + Topics []tour.Topic + Error error +} + +func tourTextMarshaler(r cmds.Response) ([]byte, error) { + output, ok := r.Output().(*tourOutput) + if !ok { + return nil, util.ErrCast() + } + // can be listing when error + var buf bytes.Buffer + err := printTourOutput(&buf, output) + if err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +func printTourOutput(w io.Writer, output *tourOutput) error { + tmpl := `{{ if .Error }} +ERROR + {{ .Error }} +TOPICS + {{ range $topic := .Topics }} + {{ $topic.ID }} - {{ $topic.Title }} {{ end }} +{{ else if .Topic }} +Tour {{ .Topic.ID }} - {{ .Topic.Title }} + +{{ .Topic.Text }} +{{ end }} +` + tourTmpl, err := template.New("tour").Parse(tmpl) + if err != nil { + return err + } + return tourTmpl.Execute(w, output) +} + +func tourRunFunc(req cmds.Request) (interface{}, error) { + + cfg, err := req.Context().GetConfig() + if err != nil { + return nil, err + } + + strs, err := internal.CastToStrings(req.Arguments()) + if err != nil { + return nil, err + } + + id := tour.TopicID(cfg.Tour.Last) + if len(strs) > 0 { + id = tour.TopicID(strs[0]) + } + + t, err := tourGet(id) + if err != nil { + + // If no topic exists for this id, we handle this error right here. + // To help the user achieve the task, we construct a response + // comprised of... + // 1) a simple error message + // 2) the full list of topics + + output := &tourOutput{ + Error: err, + } + for _, id := range tour.IDs { + t, ok := tour.Topics[id] + if !ok { + return nil, err + } + output.Topics = append(output.Topics, t) + } + + return output, nil + // return nil, cmds.ClientError(err.Error()) + } + + return &tourOutput{Topic: t}, nil +} + +// TODO use tourOutput like parent command var cmdIpfsTourNext = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Show the next IPFS Tour topic", @@ -97,7 +164,7 @@ var cmdIpfsTourNext = &cmds.Command{ } w.WriteTo(os.Stdout) // TODO write to res.SetValue - return nil, nil + return w, nil }, } @@ -122,6 +189,7 @@ var cmdIpfsTourRestart = &cmds.Command{ }, } +// TODO use tourOutput like parent command var cmdIpfsTourList = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Show a list of IPFS Tour topics", @@ -171,6 +239,7 @@ Tour {{ .ID }} - {{ .Title }} return ttempl.Execute(w, t) } +// tourGet returns an error if topic does not exist func tourGet(id tour.ID) (*tour.Topic, error) { t, found := tour.Topics[id] if !found { diff --git a/cmd/ipfs2/tour_test.go b/cmd/ipfs2/tour_test.go index ae6089e56..d6ee060b3 100644 --- a/cmd/ipfs2/tour_test.go +++ b/cmd/ipfs2/tour_test.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "errors" "testing" "github.com/jbenet/go-ipfs/tour" @@ -11,8 +12,9 @@ func TestParseTourTemplate(t *testing.T) { topic := &tour.Topic{ ID: "42", Title: "IPFS CLI test files", - Text: `Welcome to the IPFS test files - This is where we test our beautiful command line interfaces + Text: ` +Welcome to the IPFS test files +This is where we test our beautiful command line interfaces `, } var buf bytes.Buffer @@ -22,3 +24,48 @@ func TestParseTourTemplate(t *testing.T) { } t.Log(buf.String()) } + +func TestRenderTourOutputList(t *testing.T) { + + t.Log(`Ensure we can successfully print the tour output when there's an + error and list of tour topics`) + listOutput := &tourOutput{ + Error: errors.New("Topic 42 does not exist"), + Topics: []tour.Topic{ + tour.Topic{ + ID: "41", + Title: "Being one shy of the mark", + Text: "Poor thing.", + }, + tour.Topic{ + ID: "44", + Title: "Two shy of the mark", + Text: "Oh no.", + }, + }, + } + + var list bytes.Buffer + if err := printTourOutput(&list, listOutput); err != nil { + t.Fatal(err) + } + t.Log(list.String()) +} + +func TestRenderTourOutputSingle(t *testing.T) { + t.Log(` + When there's just a single topic in the output, ensure we can render the + template`) + singleOutput := &tourOutput{ + Topic: &tour.Topic{ + ID: "42", + Title: "Informative!", + Text: "Compelling!", + }, + } + var single bytes.Buffer + if err := printTourOutput(&single, singleOutput); err != nil { + t.Fatal(err) + } + t.Log(single.String()) +} From c99e9e6000f2b73db8ea24f7f28ce46fbe1d669b Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 15:31:15 -0800 Subject: [PATCH 348/383] feat(details) add funcs to negate negations not immediately useful, but nice to have tagging you to make sure i didn't make a mistake here @jbenet License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/ipfs.go | 4 ++++ cmd/ipfs2/main.go | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go index e12d60376..402ce55a7 100644 --- a/cmd/ipfs2/ipfs.go +++ b/cmd/ipfs2/ipfs.go @@ -55,6 +55,10 @@ type cmdDetails struct { doesNotUseRepo bool } +func (d *cmdDetails) canRunOnClient() bool { return !d.cannotRunOnClient } +func (d *cmdDetails) canRunOnDaemon() bool { return !d.cannotRunOnDaemon } +func (d *cmdDetails) usesRepo() bool { return !d.doesNotUseRepo } + // "What is this madness!?" you ask. Our commands have the unfortunate problem of // not being able to run on all the same contexts. This map describes these // properties so that other code can make decisions about whether to invoke a diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 800fe6d3c..f394ec980 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -281,7 +281,7 @@ func commandShouldRunOnDaemon(req cmds.Request, root *cmds.Command) (bool, error return false, fmt.Errorf("command disabled: %s", path[0]) } - if details.doesNotUseRepo && !details.cannotRunOnClient { + if details.doesNotUseRepo && details.canRunOnClient() { return false, nil } From 35da357dc56a6688286c5f6feb3df689cfbbdc3d Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 20:56:50 -0800 Subject: [PATCH 349/383] fix(tour) patch up and verify tour output License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/tour.go | 54 +++++++++++++++++------------------------------ 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index ed9cd43f7..6ded02ee4 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -47,6 +47,8 @@ IPFS very quickly. To start, run: // tourOutput is a union type. It either contains a Topic or it contains the // list of Topics and an Error. type tourOutput struct { + Last tour.ID + Topic *tour.Topic Topics []tour.Topic @@ -68,23 +70,14 @@ func tourTextMarshaler(r cmds.Response) ([]byte, error) { } func printTourOutput(w io.Writer, output *tourOutput) error { - tmpl := `{{ if .Error }} -ERROR - {{ .Error }} -TOPICS - {{ range $topic := .Topics }} - {{ $topic.ID }} - {{ $topic.Title }} {{ end }} -{{ else if .Topic }} -Tour {{ .Topic.ID }} - {{ .Topic.Title }} - -{{ .Topic.Text }} -{{ end }} -` - tourTmpl, err := template.New("tour").Parse(tmpl) - if err != nil { - return err + if output.Error != nil { + fmt.Fprintln(w, "ERROR") + fmt.Fprintln(w, output.Error.Error()) + fmt.Fprintln(w, "") + fprintTourList(w, output.Last) + return nil // TODO err } - return tourTmpl.Execute(w, output) + return fprintTourShow(w, output.Topic) } func tourRunFunc(req cmds.Request) (interface{}, error) { @@ -115,23 +108,15 @@ func tourRunFunc(req cmds.Request) (interface{}, error) { output := &tourOutput{ Error: err, - } - for _, id := range tour.IDs { - t, ok := tour.Topics[id] - if !ok { - return nil, err - } - output.Topics = append(output.Topics, t) + Last: tour.TopicID(cfg.Tour.Last), } return output, nil - // return nil, cmds.ClientError(err.Error()) } return &tourOutput{Topic: t}, nil } -// TODO use tourOutput like parent command var cmdIpfsTourNext = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Show the next IPFS Tour topic", @@ -150,7 +135,7 @@ var cmdIpfsTourNext = &cmds.Command{ if err != nil { return nil, err } - if err := tourShow(&w, topic); err != nil { + if err := fprintTourShow(&w, topic); err != nil { return nil, err } @@ -163,8 +148,8 @@ var cmdIpfsTourNext = &cmds.Command{ } } - w.WriteTo(os.Stdout) // TODO write to res.SetValue - return w, nil + w.WriteTo(os.Stdout) + return nil, nil }, } @@ -189,7 +174,6 @@ var cmdIpfsTourRestart = &cmds.Command{ }, } -// TODO use tourOutput like parent command var cmdIpfsTourList = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Show a list of IPFS Tour topics", @@ -202,15 +186,13 @@ var cmdIpfsTourList = &cmds.Command{ } var w bytes.Buffer - tourListCmd(&w, cfg) + fprintTourList(&w, tour.TopicID(cfg.Tour.Last)) w.WriteTo(os.Stdout) // TODO use res.SetOutput(output) return nil, nil }, } -func tourListCmd(w io.Writer, cfg *config.Config) { - - lastid := tour.TopicID(cfg.Tour.Last) +func fprintTourList(w io.Writer, lastid tour.ID) { for _, id := range tour.IDs { c := ' ' switch { @@ -225,7 +207,8 @@ func tourListCmd(w io.Writer, cfg *config.Config) { } } -func tourShow(w io.Writer, t *tour.Topic) error { +// fprintTourShow writes a text-formatted topic to the writer +func fprintTourShow(w io.Writer, t *tour.Topic) error { tmpl := ` Tour {{ .ID }} - {{ .Title }} @@ -239,7 +222,8 @@ Tour {{ .ID }} - {{ .Title }} return ttempl.Execute(w, t) } -// tourGet returns an error if topic does not exist +// tourGet returns the topic given its ID. Returns an error if topic does not +// exist. func tourGet(id tour.ID) (*tour.Topic, error) { t, found := tour.Topics[id] if !found { From f1c20b4e3a0fb7123bbffeb0a9a847de5aa0ac81 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 21:05:18 -0800 Subject: [PATCH 350/383] don't use the marshaler License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/tour.go | 56 +++++++----------------------------------- cmd/ipfs2/tour_test.go | 2 +- 2 files changed, 10 insertions(+), 48 deletions(-) diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index 6ded02ee4..447413690 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -11,7 +11,6 @@ import ( config "github.com/jbenet/go-ipfs/config" internal "github.com/jbenet/go-ipfs/core/commands2/internal" tour "github.com/jbenet/go-ipfs/tour" - "github.com/jbenet/go-ipfs/util" ) // TODO the parent function now uses tourOutput. Migrate the children to also @@ -38,46 +37,6 @@ IPFS very quickly. To start, run: "restart": cmdIpfsTourRestart, }, Run: tourRunFunc, - Marshalers: cmds.MarshalerMap{ - cmds.Text: tourTextMarshaler, - }, - Type: &tourOutput{}, -} - -// tourOutput is a union type. It either contains a Topic or it contains the -// list of Topics and an Error. -type tourOutput struct { - Last tour.ID - - Topic *tour.Topic - - Topics []tour.Topic - Error error -} - -func tourTextMarshaler(r cmds.Response) ([]byte, error) { - output, ok := r.Output().(*tourOutput) - if !ok { - return nil, util.ErrCast() - } - // can be listing when error - var buf bytes.Buffer - err := printTourOutput(&buf, output) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -func printTourOutput(w io.Writer, output *tourOutput) error { - if output.Error != nil { - fmt.Fprintln(w, "ERROR") - fmt.Fprintln(w, output.Error.Error()) - fmt.Fprintln(w, "") - fprintTourList(w, output.Last) - return nil // TODO err - } - return fprintTourShow(w, output.Topic) } func tourRunFunc(req cmds.Request) (interface{}, error) { @@ -97,6 +56,8 @@ func tourRunFunc(req cmds.Request) (interface{}, error) { id = tour.TopicID(strs[0]) } + var w bytes.Buffer + defer w.WriteTo(os.Stdout) t, err := tourGet(id) if err != nil { @@ -106,15 +67,16 @@ func tourRunFunc(req cmds.Request) (interface{}, error) { // 1) a simple error message // 2) the full list of topics - output := &tourOutput{ - Error: err, - Last: tour.TopicID(cfg.Tour.Last), - } + fmt.Fprintln(&w, "ERROR") + fmt.Fprintln(&w, err) + fmt.Fprintln(&w, "") + fprintTourList(&w, tour.TopicID(cfg.Tour.Last)) - return output, nil + return nil, nil } - return &tourOutput{Topic: t}, nil + fprintTourShow(&w, t) + return nil, nil } var cmdIpfsTourNext = &cmds.Command{ diff --git a/cmd/ipfs2/tour_test.go b/cmd/ipfs2/tour_test.go index d6ee060b3..3298ca88d 100644 --- a/cmd/ipfs2/tour_test.go +++ b/cmd/ipfs2/tour_test.go @@ -18,7 +18,7 @@ This is where we test our beautiful command line interfaces `, } var buf bytes.Buffer - err := tourShow(&buf, topic) + err := fprintTourShow(&buf, topic) if err != nil { t.Fatal(err) } From 1065ae45aa5c9c3c3688deb8106d9deca720f431 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 21:08:16 -0800 Subject: [PATCH 351/383] s/number/id "the id of the topic you'd like to tour" License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/tour.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index 447413690..c0ea9c048 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -29,7 +29,7 @@ IPFS very quickly. To start, run: }, Arguments: []cmds.Argument{ - cmds.StringArg("number", false, false, "The number of the topic you would like to tour"), + cmds.StringArg("id", false, false, "The id of the topic you would like to tour"), }, Subcommands: map[string]*cmds.Command{ "list": cmdIpfsTourList, From 476aab955260c5f25d0e67d2851f2652d01cea96 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 21:09:08 -0800 Subject: [PATCH 352/383] rm unused tests License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/tour_test.go | 46 ------------------------------------------ 1 file changed, 46 deletions(-) diff --git a/cmd/ipfs2/tour_test.go b/cmd/ipfs2/tour_test.go index 3298ca88d..9a11cc619 100644 --- a/cmd/ipfs2/tour_test.go +++ b/cmd/ipfs2/tour_test.go @@ -2,7 +2,6 @@ package main import ( "bytes" - "errors" "testing" "github.com/jbenet/go-ipfs/tour" @@ -24,48 +23,3 @@ This is where we test our beautiful command line interfaces } t.Log(buf.String()) } - -func TestRenderTourOutputList(t *testing.T) { - - t.Log(`Ensure we can successfully print the tour output when there's an - error and list of tour topics`) - listOutput := &tourOutput{ - Error: errors.New("Topic 42 does not exist"), - Topics: []tour.Topic{ - tour.Topic{ - ID: "41", - Title: "Being one shy of the mark", - Text: "Poor thing.", - }, - tour.Topic{ - ID: "44", - Title: "Two shy of the mark", - Text: "Oh no.", - }, - }, - } - - var list bytes.Buffer - if err := printTourOutput(&list, listOutput); err != nil { - t.Fatal(err) - } - t.Log(list.String()) -} - -func TestRenderTourOutputSingle(t *testing.T) { - t.Log(` - When there's just a single topic in the output, ensure we can render the - template`) - singleOutput := &tourOutput{ - Topic: &tour.Topic{ - ID: "42", - Title: "Informative!", - Text: "Compelling!", - }, - } - var single bytes.Buffer - if err := printTourOutput(&single, singleOutput); err != nil { - t.Fatal(err) - } - t.Log(single.String()) -} From 65eb1d86fa8ed7662c7ddd5fb3ad6fd923fa7b1f Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 21:11:51 -0800 Subject: [PATCH 353/383] rm todos License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/tour.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cmd/ipfs2/tour.go b/cmd/ipfs2/tour.go index c0ea9c048..94cd9aaf6 100644 --- a/cmd/ipfs2/tour.go +++ b/cmd/ipfs2/tour.go @@ -13,9 +13,6 @@ import ( tour "github.com/jbenet/go-ipfs/tour" ) -// TODO the parent function now uses tourOutput. Migrate the children to also -// use the tourOutput struct - var tourCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "An introduction to IPFS", @@ -149,7 +146,7 @@ var cmdIpfsTourList = &cmds.Command{ var w bytes.Buffer fprintTourList(&w, tour.TopicID(cfg.Tour.Last)) - w.WriteTo(os.Stdout) // TODO use res.SetOutput(output) + w.WriteTo(os.Stdout) return nil, nil }, } From 80dcc5d237e6b3aa13b11c381e90ca1f966168d3 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 21:30:21 -0800 Subject: [PATCH 354/383] fix(2/version) License: MIT Signed-off-by: Brian Tiger Chow --- core/commands2/version.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/core/commands2/version.go b/core/commands2/version.go index ce58fb4a5..eeae821ec 100644 --- a/core/commands2/version.go +++ b/core/commands2/version.go @@ -1,6 +1,8 @@ package commands import ( + "fmt" + cmds "github.com/jbenet/go-ipfs/commands" config "github.com/jbenet/go-ipfs/config" ) @@ -11,12 +13,16 @@ type VersionOutput struct { var VersionCmd = &cmds.Command{ Helptext: cmds.HelpText{ - Tagline: "Outputs the current version of IPFS", - ShortDescription: "Returns the version number of IPFS and exits.", + Tagline: "Shows ipfs version information", + ShortDescription: "Shows ipfs version information", + LongDescription: `ipfs version - Show ipfs version information. + + Returns the current version of ipfs and exits. + `, }, Options: []cmds.Option{ - cmds.BoolOption("number", "n", "Only output the version number"), + cmds.BoolOption("number", "n", "Only show the version number"), }, Run: func(req cmds.Request) (interface{}, error) { return &VersionOutput{ @@ -26,21 +32,15 @@ var VersionCmd = &cmds.Command{ Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { v := res.Output().(*VersionOutput) - s := "" number, found, err := res.Request().Option("number").Bool() if err != nil { return nil, err } - if !found { - number = false + if found && number { + return []byte(fmt.Sprintln(v.Version)), nil } - - if !number { - s += "ipfs version " - } - s += v.Version - return []byte(s), nil + return []byte(fmt.Sprintf("ipfs version %s\n", v.Version)), nil }, }, Type: &VersionOutput{}, From e00830332f6b6ed3443f79a76cadc678a325352d Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 13 Nov 2014 20:28:12 -0800 Subject: [PATCH 355/383] cmds2: name synopsis. --- core/commands2/name.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/commands2/name.go b/core/commands2/name.go index 8b30d5c15..a50d02d43 100644 --- a/core/commands2/name.go +++ b/core/commands2/name.go @@ -10,6 +10,10 @@ type IpnsEntry struct { var nameCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "IPFS namespace (IPNS) tool", + Synopsis: ` +ipfs name publish [] - Publish an object to IPNS +ipfs name resolve [] - Gets the value currently published at an IPNS name +`, ShortDescription: ` IPNS is a PKI namespace, where names are the hashes of public keys, and the private key enables publishing new (signed) values. In both publish From aab1a31b1b8aab36f5104e3993608e5e7c843e50 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 13 Nov 2014 21:18:12 -0800 Subject: [PATCH 356/383] cmds2: add fix, was not adding dirs --- core/commands2/add.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/core/commands2/add.go b/core/commands2/add.go index 4af5aeb3a..fc1bd993b 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -114,6 +114,10 @@ remains to be implemented. } log.Infof("adding dir: %s", name) + if err := addNode(n, tree); err != nil { + return nil, err + } + if err := addDagnode(name, tree); err != nil { return nil, err } @@ -203,12 +207,6 @@ func add(n *core.IpfsNode, readers []io.Reader) ([]*dag.Node, error) { if err != nil { return nil, err } - - err = addNode(n, node) - if err != nil { - return nil, err - } - dagnodes = append(dagnodes, node) } From 1348af01b9165d819c09459a0512dfd531030af8 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 13 Nov 2014 22:06:03 -0800 Subject: [PATCH 357/383] cmd2: version test --- core/commands2/version.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/commands2/version.go b/core/commands2/version.go index eeae821ec..047c36d56 100644 --- a/core/commands2/version.go +++ b/core/commands2/version.go @@ -14,11 +14,7 @@ type VersionOutput struct { var VersionCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Shows ipfs version information", - ShortDescription: "Shows ipfs version information", - LongDescription: `ipfs version - Show ipfs version information. - - Returns the current version of ipfs and exits. - `, + ShortDescription: "Returns the current version of ipfs and exits.", }, Options: []cmds.Option{ From 0eeed7cc68caafc8cede3168e7c10fd20b73c315 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 22:47:58 -0800 Subject: [PATCH 358/383] add string method to command details License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/ipfs.go | 7 +++++++ cmd/ipfs2/main.go | 1 + 2 files changed, 8 insertions(+) diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go index 402ce55a7..d6f256d54 100644 --- a/cmd/ipfs2/ipfs.go +++ b/cmd/ipfs2/ipfs.go @@ -1,6 +1,8 @@ package main import ( + "fmt" + cmds "github.com/jbenet/go-ipfs/commands" commands "github.com/jbenet/go-ipfs/core/commands2" ) @@ -55,6 +57,11 @@ type cmdDetails struct { doesNotUseRepo bool } +func (d *cmdDetails) String() string { + return fmt.Sprintf("on client? %t, on daemon? %t, uses repo? %t", + d.canRunOnClient(), d.canRunOnDaemon(), d.usesRepo()) +} + func (d *cmdDetails) canRunOnClient() bool { return !d.cannotRunOnClient } func (d *cmdDetails) canRunOnDaemon() bool { return !d.cannotRunOnDaemon } func (d *cmdDetails) usesRepo() bool { return !d.doesNotUseRepo } diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index f394ec980..0742bf47c 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -276,6 +276,7 @@ func commandShouldRunOnDaemon(req cmds.Request, root *cmds.Command) (bool, error if !found { details = cmdDetails{} // defaults } + log.Debugf("cmd perms for +%v: %s", path, details.String()) if details.cannotRunOnClient && details.cannotRunOnDaemon { return false, fmt.Errorf("command disabled: %s", path[0]) From e0ba14c0ebc0629360f1d0dff7600e4da23113ed Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Thu, 13 Nov 2014 22:53:14 -0800 Subject: [PATCH 359/383] cmds2: use cmdDetails on level cmds --- cmd/ipfs2/ipfs.go | 2 ++ cmd/ipfs2/main.go | 19 ++++++++++++------- core/commands2/update.go | 8 ++++---- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/cmd/ipfs2/ipfs.go b/cmd/ipfs2/ipfs.go index d6f256d54..5f349c19c 100644 --- a/cmd/ipfs2/ipfs.go +++ b/cmd/ipfs2/ipfs.go @@ -78,5 +78,7 @@ var cmdDetailsMap = map[*cmds.Command]cmdDetails{ commands.DiagCmd: cmdDetails{cannotRunOnClient: true}, commands.VersionCmd: cmdDetails{doesNotUseRepo: true}, commands.UpdateCmd: cmdDetails{cannotRunOnDaemon: true}, + commands.UpdateCheckCmd: cmdDetails{}, + commands.UpdateLogCmd: cmdDetails{}, commands.LogCmd: cmdDetails{cannotRunOnClient: true}, } diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 0742bf47c..8fb33f86e 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -267,14 +267,19 @@ func commandShouldRunOnDaemon(req cmds.Request, root *cmds.Command) (bool, error return false, nil } - cmd, found := root.Subcommands[path[0]] - if !found { - return false, fmt.Errorf("subcommand %s should be in root", path[0]) - } + var details cmdDetails + // find the last command in path that has a cmdDetailsMap entry + cmd := root + for _, cmp := range path { + var found bool + cmd, found = cmd.Subcommands[cmp] + if !found { + return false, fmt.Errorf("subcommand %s should be in root", cmp) + } - details, found := cmdDetailsMap[cmd] - if !found { - details = cmdDetails{} // defaults + if cmdDetails, found := cmdDetailsMap[cmd]; found { + details = cmdDetails + } } log.Debugf("cmd perms for +%v: %s", path, details.String()) diff --git a/core/commands2/update.go b/core/commands2/update.go index 5e14a47e3..83af002d9 100644 --- a/core/commands2/update.go +++ b/core/commands2/update.go @@ -29,8 +29,8 @@ var UpdateCmd = &cmds.Command{ }, Type: &UpdateOutput{}, Subcommands: map[string]*cmds.Command{ - "check": updateCheckCmd, - "log": updateLogCmd, + "check": UpdateCheckCmd, + "log": UpdateLogCmd, }, Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { @@ -47,7 +47,7 @@ var UpdateCmd = &cmds.Command{ }, } -var updateCheckCmd = &cmds.Command{ +var UpdateCheckCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Checks if updates are available", ShortDescription: ` @@ -80,7 +80,7 @@ Nothing will be downloaded or installed. }, } -var updateLogCmd = &cmds.Command{ +var UpdateLogCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "List the changelog for the latest versions of IPFS", ShortDescription: "This command is not yet implemented.", From 8b07b35467da1536770aefd737e49d9d10592583 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 23:18:59 -0800 Subject: [PATCH 360/383] ignore binary License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/ipfs2/.gitignore b/cmd/ipfs2/.gitignore index 1cc9fdff7..263031f7f 100644 --- a/cmd/ipfs2/.gitignore +++ b/cmd/ipfs2/.gitignore @@ -1,2 +1,3 @@ ./ipfs ./ipfs.exe +./ipfs2 From 92c168b03eaf1481814d52c3be0006037cd6d4f9 Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 23:19:24 -0800 Subject: [PATCH 361/383] fix(2/update) newlines License: MIT Signed-off-by: Brian Tiger Chow --- core/commands2/update.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/commands2/update.go b/core/commands2/update.go index 83af002d9..589f699a0 100644 --- a/core/commands2/update.go +++ b/core/commands2/update.go @@ -37,10 +37,10 @@ var UpdateCmd = &cmds.Command{ v := res.Output().(*UpdateOutput) s := "" if v.NewVersion != v.OldVersion { - s = fmt.Sprintf("Successfully updated to IPFS version '%s' (from '%s')", + s = fmt.Sprintf("Successfully updated to IPFS version '%s' (from '%s')\n", v.NewVersion, v.OldVersion) } else { - s = fmt.Sprintf("Already updated to latest version ('%s')", v.NewVersion) + s = fmt.Sprintf("Already updated to latest version ('%s')\n", v.NewVersion) } return []byte(s), nil }, @@ -70,10 +70,10 @@ Nothing will be downloaded or installed. v := res.Output().(*UpdateOutput) s := "" if v.NewVersion != v.OldVersion { - s = fmt.Sprintf("A new version of IPFS is available ('%s', currently running '%s')", + s = fmt.Sprintf("A new version of IPFS is available ('%s', currently running '%s')\n", v.NewVersion, v.OldVersion) } else { - s = fmt.Sprintf("Already updated to latest version ('%s')", v.NewVersion) + s = fmt.Sprintf("Already updated to latest version ('%s')\n", v.NewVersion) } return []byte(s), nil }, From d842c3c98f4c21be9a215d4fdefe262c1c7e93cb Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Thu, 13 Nov 2014 23:53:41 -0800 Subject: [PATCH 362/383] fix(2/update) insert line break License: MIT Signed-off-by: Brian Tiger Chow --- core/commands2/update.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/core/commands2/update.go b/core/commands2/update.go index 589f699a0..81aad84e8 100644 --- a/core/commands2/update.go +++ b/core/commands2/update.go @@ -49,12 +49,8 @@ var UpdateCmd = &cmds.Command{ var UpdateCheckCmd = &cmds.Command{ Helptext: cmds.HelpText{ - Tagline: "Checks if updates are available", - ShortDescription: ` -'ipfs update check' checks if any updates are available for IPFS. - -Nothing will be downloaded or installed. -`, + Tagline: "Checks if updates are available", + ShortDescription: "'ipfs update check' checks if any updates are available for IPFS.\nNothing will be downloaded or installed.", }, Run: func(req cmds.Request) (interface{}, error) { From 4eaf38c6bae2fe00ff2c8644a9c8251293f8ff63 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 13 Nov 2014 23:23:53 -0800 Subject: [PATCH 363/383] object: Added a synopsis --- core/commands2/object.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/core/commands2/object.go b/core/commands2/object.go index bf4a7cec6..b42bff5a5 100644 --- a/core/commands2/object.go +++ b/core/commands2/object.go @@ -25,8 +25,16 @@ type Node struct { var objectCmd = &cmds.Command{ Helptext: cmds.HelpText{ - Tagline: "Interact with ipfs objects", - ShortDescription: "'ipfs object' is a plumbing command used to manipulate DAG objects directly.", + Tagline: "Interact with ipfs objects", + ShortDescription: ` +'ipfs object' is a plumbing command used to manipulate DAG objects +directly.`, + Synopsis: ` +ipfs object get - Get the DAG node named by +ipfs object put - Stores input, outputs its key +ipfs object data - Outputs raw bytes in an object +ipfs object links - Outputs links pointed to by object +`, }, Subcommands: map[string]*cmds.Command{ From 537f94a3184ce4b752cc179a29d5a95bf66a231f Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 13 Nov 2014 23:52:43 -0800 Subject: [PATCH 364/383] object get: Fixed protobuf marshaling --- core/commands2/object.go | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/core/commands2/object.go b/core/commands2/object.go index b42bff5a5..60f04ba08 100644 --- a/core/commands2/object.go +++ b/core/commands2/object.go @@ -7,6 +7,8 @@ import ( "io" "io/ioutil" + mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash" + cmds "github.com/jbenet/go-ipfs/commands" core "github.com/jbenet/go-ipfs/core" dag "github.com/jbenet/go-ipfs/merkledag" @@ -108,23 +110,23 @@ var objectGetCmd = &cmds.Command{ Tagline: "Get and serialize the DAG node named by ", ShortDescription: ` 'ipfs object get' is a plumbing command for retreiving DAG nodes. -It serializes the DAG node to the format specified by the "--encoding" flag. -It outputs to stdout, and is a base58 encoded multihash. +It serializes the DAG node to the format specified by the "--encoding" +flag. It outputs to stdout, and is a base58 encoded multihash. `, LongDescription: ` 'ipfs object get' is a plumbing command for retreiving DAG nodes. -It serializes the DAG node to the format specified by the "--encoding" flag. -It outputs to stdout, and is a base58 encoded multihash. +It serializes the DAG node to the format specified by the "--encoding" +flag. It outputs to stdout, and is a base58 encoded multihash. This command outputs data in the following encodings: * "protobuf" * "json" * "xml" -(Specified by the "--encoding" or "-enc" flags)`, +(Specified by the "--encoding" or "-enc" flag)`, }, Arguments: []cmds.Argument{ - cmds.StringArg("key", true, false, "Key of the object to retrieve\n(in base58-encoded multihash format)"), + cmds.StringArg("key", true, false, "Key of the object to retrieve (in base58-encoded multihash format)"), }, Run: func(req cmds.Request) (interface{}, error) { n, err := req.Context().GetNode() @@ -160,7 +162,24 @@ This command outputs data in the following encodings: Type: &Node{}, Marshalers: cmds.MarshalerMap{ cmds.EncodingType("protobuf"): func(res cmds.Response) ([]byte, error) { - object := res.Output().(*dag.Node) + node := res.Output().(*Node) + + // convert the Node object into a real dag.Node + object := new(dag.Node) + object.Data = node.Data + object.Links = make([]*dag.Link, len(node.Links)) + for i, link := range node.Links { + hash, err := mh.FromB58String(link.Hash) + if err != nil { + return nil, err + } + object.Links[i] = &dag.Link{ + Name: link.Name, + Size: link.Size, + Hash: hash, + } + } + return object.Marshal() }, }, From eb696d9ac79a3e1dddd7096c83a93d4ac22088ba Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 13 Nov 2014 23:57:45 -0800 Subject: [PATCH 365/383] object put: Formatted helptext --- core/commands2/object.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/commands2/object.go b/core/commands2/object.go index 60f04ba08..f72eeb484 100644 --- a/core/commands2/object.go +++ b/core/commands2/object.go @@ -198,13 +198,13 @@ It reads from stdin, and the output is a base58 encoded multihash. Data should be in the format specified by . may be one of the following: - * "protobuf" - * "json" + * "protobuf" + * "json" `, }, Arguments: []cmds.Argument{ - cmds.FileArg("data", true, false, "Data to be stored as a DAG object\nMust be encoded as specified in "), + cmds.FileArg("data", true, false, "Data to be stored as a DAG object"), cmds.StringArg("encoding", true, false, "Encoding type of , either \"protobuf\" or \"json\""), }, Run: func(req cmds.Request) (interface{}, error) { From b4de6cce9a2b048972ae6ca084b40d78dbfdb816 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 14 Nov 2014 00:11:39 -0800 Subject: [PATCH 366/383] object put: Fixed putting objects with JSON encoding --- core/commands2/object.go | 51 ++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/core/commands2/object.go b/core/commands2/object.go index f72eeb484..ef4242ce9 100644 --- a/core/commands2/object.go +++ b/core/commands2/object.go @@ -163,23 +163,10 @@ This command outputs data in the following encodings: Marshalers: cmds.MarshalerMap{ cmds.EncodingType("protobuf"): func(res cmds.Response) ([]byte, error) { node := res.Output().(*Node) - - // convert the Node object into a real dag.Node - object := new(dag.Node) - object.Data = node.Data - object.Links = make([]*dag.Link, len(node.Links)) - for i, link := range node.Links { - hash, err := mh.FromB58String(link.Hash) - if err != nil { - return nil, err - } - object.Links[i] = &dag.Link{ - Name: link.Name, - Size: link.Size, - Hash: hash, - } + object, err := deserializeNode(node) + if err != nil { + return nil, err } - return object.Marshal() }, }, @@ -292,8 +279,16 @@ func objectPut(n *core.IpfsNode, input io.Reader, encoding string) (*Object, err switch getObjectEnc(encoding) { case objectEncodingJSON: - dagnode = new(dag.Node) - err = json.Unmarshal(data, dagnode) + node := new(Node) + err = json.Unmarshal(data, node) + if err != nil { + return nil, err + } + + dagnode, err = deserializeNode(node) + if err != nil { + return nil, err + } case objectEncodingProtobuf: dagnode, err = dag.Decoded(data) @@ -356,3 +351,23 @@ func getOutput(dagnode *dag.Node) (*Object, error) { return output, nil } + +// converts the Node object into a real dag.Node +func deserializeNode(node *Node) (*dag.Node, error) { + dagnode := new(dag.Node) + dagnode.Data = node.Data + dagnode.Links = make([]*dag.Link, len(node.Links)) + for i, link := range node.Links { + hash, err := mh.FromB58String(link.Hash) + if err != nil { + return nil, err + } + dagnode.Links[i] = &dag.Link{ + Name: link.Name, + Size: link.Size, + Hash: hash, + } + } + + return dagnode, nil +} From fceb55ef1ea0e6fb3c56d4406ab8047727d0b3b6 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 14 Nov 2014 00:07:46 -0800 Subject: [PATCH 367/383] object put: Added plaintext marshaler --- core/commands2/object.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/commands2/object.go b/core/commands2/object.go index ef4242ce9..63ceb903b 100644 --- a/core/commands2/object.go +++ b/core/commands2/object.go @@ -221,6 +221,12 @@ Data should be in the format specified by . return output, nil }, + Marshalers: cmds.MarshalerMap{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + object := res.Output().(*Object) + return []byte(object.Hash), nil + }, + }, Type: &Object{}, } From 9d299636ad6752e6fcf1794ff391c6bcc7cef831 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 14 Nov 2014 00:24:38 -0800 Subject: [PATCH 368/383] object put: Made output (almost) match original output (The file path is omitted, but since only one object can be added at a time, I think this is ok) --- core/commands2/object.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/commands2/object.go b/core/commands2/object.go index 63ceb903b..c4206d9f4 100644 --- a/core/commands2/object.go +++ b/core/commands2/object.go @@ -224,7 +224,7 @@ Data should be in the format specified by . Marshalers: cmds.MarshalerMap{ cmds.Text: func(res cmds.Response) ([]byte, error) { object := res.Output().(*Object) - return []byte(object.Hash), nil + return []byte("added " + object.Hash), nil }, }, Type: &Object{}, From 7bd7624ccc9a5dacfb85e25477ed8650c7312d50 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 14 Nov 2014 00:34:19 -0800 Subject: [PATCH 369/383] object data: Moved some helptext into LongDescription --- core/commands2/object.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/commands2/object.go b/core/commands2/object.go index c4206d9f4..fdfc4dd2d 100644 --- a/core/commands2/object.go +++ b/core/commands2/object.go @@ -51,8 +51,14 @@ var objectDataCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Outputs the raw bytes in an IPFS object", ShortDescription: ` -ipfs data is a plumbing command for retreiving the raw bytes stored in a DAG node. -It outputs to stdout, and is a base58 encoded multihash. +ipfs data is a plumbing command for retreiving the raw bytes stored in +a DAG node. It outputs to stdout, and is a base58 encoded +multihash. +`, + LongDescription: ` +ipfs data is a plumbing command for retreiving the raw bytes stored in +a DAG node. It outputs to stdout, and is a base58 encoded +multihash. Note that the "--encoding" option does not affect the output, since the output is the raw data of the object. From b3da13a4afb97c97f0acb1bea7954260b20e7721 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 14 Nov 2014 00:47:19 -0800 Subject: [PATCH 370/383] object links: Added text marshaling --- core/commands2/ls.go | 13 ++++++++----- core/commands2/object.go | 7 +++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/core/commands2/ls.go b/core/commands2/ls.go index d07a207e8..c849825de 100644 --- a/core/commands2/ls.go +++ b/core/commands2/ls.go @@ -82,11 +82,7 @@ it contains, with the following format: if len(output) > 1 { s += fmt.Sprintf("%s:\n", object.Hash) } - - for _, link := range object.Links { - s += fmt.Sprintf("%s %v %s\n", link.Hash, link.Size, link.Name) - } - + s += marshalLinks(object.Links) if len(output) > 1 { s += "\n" } @@ -97,3 +93,10 @@ it contains, with the following format: }, Type: &LsOutput{}, } + +func marshalLinks(links []Link) (s string) { + for _, link := range links { + s += fmt.Sprintf("%s %v %s\n", link.Hash, link.Size, link.Name) + } + return s +} diff --git a/core/commands2/object.go b/core/commands2/object.go index fdfc4dd2d..8263bdc20 100644 --- a/core/commands2/object.go +++ b/core/commands2/object.go @@ -108,6 +108,13 @@ It outputs to stdout, and is a base58 encoded multihash. return objectLinks(n, key) }, + Marshalers: cmds.MarshalerMap{ + cmds.Text: func(res cmds.Response) ([]byte, error) { + object := res.Output().(*Object) + marshalled := marshalLinks(object.Links) + return []byte(marshalled), nil + }, + }, Type: &Object{}, } From 5e598f9a52d82a51d19155336ca5aa0fe7f3c822 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 14 Nov 2014 01:29:35 -0800 Subject: [PATCH 371/383] cmds2: mount exmplae --- core/commands2/mount_unix.go | 49 +++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go index 016263294..bf44033a0 100644 --- a/core/commands2/mount_unix.go +++ b/core/commands2/mount_unix.go @@ -21,9 +21,52 @@ var mountCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Mounts IPFS to the filesystem (read-only)", ShortDescription: ` -Mount ipfs at a read-only mountpoint on the OS. All ipfs objects -will be accessible under that directory. Note that the root will -not be listable, as it is virtual. Accessing known paths directly. +Mount ipfs at a read-only mountpoint on the OS (default: /ipfs and /ipns). +All ipfs objects will be accessible under that directory. Note that the +root will not be listable, as it is virtual. Access known paths directly. + +You may kave to create /ipfs and /ipfs before using 'ipfs mount': + +> sudo mkdir /ipfs /ipns +> sudo chown ` + "`" + `whoami` + "`" + ` /ipfs /ipns +> ipfs mount +`, + LongDescription: ` +Mount ipfs at a read-only mountpoint on the OS (default: /ipfs and /ipns). +All ipfs objects will be accessible under that directory. Note that the +root will not be listable, as it is virtual. Access known paths directly. + +> sudo mkdir /ipfs /ipns +> sudo chown ` + "`" + `whoami` + "`" + ` /ipfs /ipns +> ipfs mount + +EXAMPLE: + +# setup +> mkdir foo +> echo "baz" > foo/bar +> ipfs add -r foo +added QmWLdkp93sNxGRjnFHPaYg8tCQ35NBY3XPn6KiETd3Z4WR foo/bar +added QmSh5e7S6fdcu75LAbXNZAFY2nGyZUJXyLCJDvn2zRkWyC foo +> ipfs ls QmSh5e7S6fdcu75LAbXNZAFY2nGyZUJXyLCJDvn2zRkWyC +QmWLdkp93sNxGRjnFHPaYg8tCQ35NBY3XPn6KiETd3Z4WR 12 bar +> ipfs cat QmWLdkp93sNxGRjnFHPaYg8tCQ35NBY3XPn6KiETd3Z4WR +baz + +# mount +> ipfs daemon & +> ipfs mount +IPFS mounted at: /ipfs +IPNS mounted at: /ipns +> cd /ipfs/QmSh5e7S6fdcu75LAbXNZAFY2nGyZUJXyLCJDvn2zRkWyC +> ls +bar +> cat bar +baz +> cat /ipfs/QmSh5e7S6fdcu75LAbXNZAFY2nGyZUJXyLCJDvn2zRkWyC/bar +baz +> cat /ipfs/QmWLdkp93sNxGRjnFHPaYg8tCQ35NBY3XPn6KiETd3Z4WR +baz `, }, From d05661548cf7b8b026d1bd927f0c07b08e49925a Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 14 Nov 2014 00:50:35 -0800 Subject: [PATCH 372/383] object links: Fixed description --- core/commands2/object.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/commands2/object.go b/core/commands2/object.go index 8263bdc20..fe3925de4 100644 --- a/core/commands2/object.go +++ b/core/commands2/object.go @@ -87,8 +87,9 @@ var objectLinksCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Outputs the links pointed to by the specified object", ShortDescription: ` -'ipfs block get' is a plumbing command for retreiving raw IPFS blocks. -It outputs to stdout, and is a base58 encoded multihash. +'ipfs object links' is a plumbing command for retreiving the links from +a DAG node. It outputs to stdout, and is a base58 encoded +multihash. `, }, From 231960e726db244fbbbd25070a4cd79e5de08c3d Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 14 Nov 2014 01:58:28 -0800 Subject: [PATCH 373/383] commands/http: Fixed client erroring on nil command output --- commands/http/client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/http/client.go b/commands/http/client.go index 09cc48639..ddf4e5d80 100644 --- a/commands/http/client.go +++ b/commands/http/client.go @@ -157,7 +157,7 @@ func getResponse(httpRes *http.Response, req cmds.Request) (cmds.Response, error } else { v := req.Command().Type err = dec.Decode(&v) - if err != nil { + if err != nil && err != io.EOF { return nil, err } From 97d2f48b7cdae7220eb2f1457e9749bf78f95f2f Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 14 Nov 2014 02:15:24 -0800 Subject: [PATCH 374/383] cmds2: change add arg name --- core/commands2/add.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/commands2/add.go b/core/commands2/add.go index fc1bd993b..85386a87a 100644 --- a/core/commands2/add.go +++ b/core/commands2/add.go @@ -43,7 +43,7 @@ remains to be implemented. cmds.BoolOption("recursive", "r", "Must be specified when adding directories"), }, Arguments: []cmds.Argument{ - cmds.StringArg("file", true, true, "The path to a file to be added to IPFS"), + cmds.StringArg("path", true, true, "The path to a file to be added to IPFS"), }, Run: func(req cmds.Request) (interface{}, error) { added := &AddOutput{} From 00b19f88761a4267cd9a96adb49655bb762d8906 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 13 Nov 2014 21:05:50 -0800 Subject: [PATCH 375/383] commands/cli: Take an optional Stdin value in Parse (read as a reader argument or string argument) --- commands/cli/parse.go | 102 ++++++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 38 deletions(-) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index 2940f7268..197082597 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -1,6 +1,7 @@ package cli import ( + "bytes" "errors" "fmt" "os" @@ -15,7 +16,7 @@ var ErrInvalidSubcmd = errors.New("subcommand not found") // Parse parses the input commandline string (cmd, flags, and args). // returns the corresponding command Request object. // Parse will search each root to find the one that best matches the requested subcommand. -func Parse(input []string, root *cmds.Command) (cmds.Request, *cmds.Command, []string, error) { +func Parse(input []string, stdin *os.File, root *cmds.Command) (cmds.Request, *cmds.Command, []string, error) { // use the root that matches the longest path (most accurately matches request) path, input, cmd := parsePath(input, root) opts, stringArgs, err := parseOptions(input) @@ -27,7 +28,7 @@ func Parse(input []string, root *cmds.Command) (cmds.Request, *cmds.Command, []s return nil, nil, path, ErrInvalidSubcmd } - args, err := parseArgs(stringArgs, cmd.Arguments) + args, err := parseArgs(stringArgs, stdin, cmd.Arguments) if err != nil { return nil, cmd, path, err } @@ -116,7 +117,21 @@ func parseOptions(input []string) (map[string]interface{}, []string, error) { return opts, args, nil } -func parseArgs(stringArgs []string, arguments []cmds.Argument) ([]interface{}, error) { +func parseArgs(stringArgs []string, stdin *os.File, arguments []cmds.Argument) ([]interface{}, error) { + // check if stdin is coming from terminal or is being piped in + if stdin != nil { + stat, err := stdin.Stat() + if err != nil { + return nil, err + } + + // if stdin isn't a CharDevice, set it to nil + // (this means it is coming from terminal and we want to ignore it) + if (stat.Mode() & os.ModeCharDevice) != 0 { + stdin = nil + } + } + // count required argument definitions lenRequired := 0 for _, argDef := range arguments { @@ -125,56 +140,67 @@ func parseArgs(stringArgs []string, arguments []cmds.Argument) ([]interface{}, e } } - args := make([]interface{}, len(stringArgs)) + valCount := len(stringArgs) + if stdin != nil { + valCount += 1 + } + + args := make([]interface{}, 0, valCount) + + argDefIndex := 0 // the index of the current argument definition + for i := 0; i < valCount; i++ { + // get the argument definiton (should be arguments[argDefIndex], + // but if argDefIndex > len(arguments) we use the last argument definition) + var argDef cmds.Argument + if argDefIndex < len(arguments) { + argDef = arguments[argDefIndex] + } else { + argDef = arguments[len(arguments)-1] + } - valueIndex := 0 // the index of the current stringArgs value - for _, argDef := range arguments { // skip optional argument definitions if there aren't sufficient remaining values - if len(stringArgs)-valueIndex <= lenRequired && !argDef.Required { + if valCount-i <= lenRequired && !argDef.Required { continue } else if argDef.Required { lenRequired-- } - if valueIndex >= len(stringArgs) { - break - } + if argDef.Type == cmds.ArgString { + if stdin == nil { + // add string values + args = append(args, stringArgs[0]) + stringArgs = stringArgs[1:] - if argDef.Variadic { - for _, arg := range stringArgs[valueIndex:] { - value, err := argValue(argDef, arg) + } else { + // if we have a stdin, read it in and use the data as a string value + var buf bytes.Buffer + _, err := buf.ReadFrom(stdin) if err != nil { return nil, err } - args[valueIndex] = value - valueIndex++ + args = append(args, buf.String()) + stdin = nil } - } else { - var err error - value, err := argValue(argDef, stringArgs[valueIndex]) - if err != nil { - return nil, err + + } else if argDef.Type == cmds.ArgFile { + if stdin == nil { + // treat stringArg values as file paths + file, err := os.Open(stringArgs[0]) + if err != nil { + return nil, err + } + args = append(args, file) + stringArgs = stringArgs[1:] + + } else { + // if we have a stdin, use that as a reader + args = append(args, stdin) + stdin = nil } - args[valueIndex] = value - valueIndex++ } + + argDefIndex++ } return args, nil } - -func argValue(argDef cmds.Argument, value string) (interface{}, error) { - if argDef.Type == cmds.ArgString { - return value, nil - - } else { - // NB At the time of this commit, file cleanup is performed when - // Requests are cleaned up. TODO try to perform open and close at the - // same level of abstraction (or at least in the same package!) - in, err := os.Open(value) - if err != nil { - return nil, err - } - return in, nil - } -} From 1abe97617e042f8b9935f56c3f22cd5d23db4470 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 13 Nov 2014 21:06:53 -0800 Subject: [PATCH 376/383] main: Pass Stdin to CLI request parser --- cmd/ipfs2/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index 8fb33f86e..f045aa2f8 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -159,7 +159,7 @@ func (i *cmdInvocation) Run() (output io.Reader, err error) { func (i *cmdInvocation) Parse(args []string) error { var err error - i.req, i.cmd, i.path, err = cmdsCli.Parse(args, Root) + i.req, i.cmd, i.path, err = cmdsCli.Parse(args, os.Stdin, Root) if err != nil { return err } From 9370740db00802f91e5faafba79968609d0c26d4 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Thu, 13 Nov 2014 22:50:27 -0800 Subject: [PATCH 377/383] commands/cli: Fixed bug when parsing args for a command that doesn't have any argument definitions --- commands/cli/parse.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/cli/parse.go b/commands/cli/parse.go index 197082597..875360d09 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -154,7 +154,7 @@ func parseArgs(stringArgs []string, stdin *os.File, arguments []cmds.Argument) ( var argDef cmds.Argument if argDefIndex < len(arguments) { argDef = arguments[argDefIndex] - } else { + } else if len(arguments) > 0 { argDef = arguments[len(arguments)-1] } From 2b6b6fac00197323f172bd07e1e928f05f1f318d Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 14 Nov 2014 02:22:58 -0800 Subject: [PATCH 378/383] commands: Added a flag to enable stdin arguments --- commands/argument.go | 16 +++++++++++----- commands/cli/parse.go | 4 ++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/commands/argument.go b/commands/argument.go index ac407aba6..fb4b74b74 100644 --- a/commands/argument.go +++ b/commands/argument.go @@ -8,11 +8,12 @@ const ( ) type Argument struct { - Name string - Type ArgumentType - Required bool - Variadic bool - Description string + Name string + Type ArgumentType + Required bool + Variadic bool + SupportsStdin bool + Description string } func StringArg(name string, required, variadic bool, description string) Argument { @@ -34,3 +35,8 @@ func FileArg(name string, required, variadic bool, description string) Argument Description: description, } } + +func (a Argument) EnableStdin() Argument { + a.SupportsStdin = true + return a +} diff --git a/commands/cli/parse.go b/commands/cli/parse.go index 875360d09..17b5dab16 100644 --- a/commands/cli/parse.go +++ b/commands/cli/parse.go @@ -171,7 +171,7 @@ func parseArgs(stringArgs []string, stdin *os.File, arguments []cmds.Argument) ( args = append(args, stringArgs[0]) stringArgs = stringArgs[1:] - } else { + } else if argDef.SupportsStdin { // if we have a stdin, read it in and use the data as a string value var buf bytes.Buffer _, err := buf.ReadFrom(stdin) @@ -192,7 +192,7 @@ func parseArgs(stringArgs []string, stdin *os.File, arguments []cmds.Argument) ( args = append(args, file) stringArgs = stringArgs[1:] - } else { + } else if argDef.SupportsStdin { // if we have a stdin, use that as a reader args = append(args, stdin) stdin = nil From e4b630289acaf3d66d6a96c415ade91fa94973f4 Mon Sep 17 00:00:00 2001 From: Matt Bell Date: Fri, 14 Nov 2014 02:23:32 -0800 Subject: [PATCH 379/383] block put: Support stdin input on 'block put' --- core/commands2/block.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/commands2/block.go b/core/commands2/block.go index 0379e5fcb..2582fc55e 100644 --- a/core/commands2/block.go +++ b/core/commands2/block.go @@ -90,7 +90,7 @@ It reads from stdin, and is a base58 encoded multihash. }, Arguments: []cmds.Argument{ - cmds.FileArg("data", true, false, "The data to be stored as an IPFS block"), + cmds.FileArg("data", true, false, "The data to be stored as an IPFS block").EnableStdin(), }, Run: func(req cmds.Request) (interface{}, error) { n, err := req.Context().GetNode() From fa8fc1aefc2e6ad5f8dc3faaee8adfc2709343e0 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 14 Nov 2014 02:38:39 -0800 Subject: [PATCH 380/383] cmds2: check for updates --- cmd/ipfs2/main.go | 20 +++++++++++++------- config/version.go | 2 +- updates/updates.go | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/cmd/ipfs2/main.go b/cmd/ipfs2/main.go index f045aa2f8..4bbe1da92 100644 --- a/cmd/ipfs2/main.go +++ b/cmd/ipfs2/main.go @@ -15,9 +15,10 @@ import ( cmds "github.com/jbenet/go-ipfs/commands" cmdsCli "github.com/jbenet/go-ipfs/commands/cli" cmdsHttp "github.com/jbenet/go-ipfs/commands/http" - "github.com/jbenet/go-ipfs/config" - "github.com/jbenet/go-ipfs/core" + config "github.com/jbenet/go-ipfs/config" + core "github.com/jbenet/go-ipfs/core" daemon "github.com/jbenet/go-ipfs/daemon2" + updates "github.com/jbenet/go-ipfs/updates" u "github.com/jbenet/go-ipfs/util" ) @@ -207,12 +208,12 @@ func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { return nil, err } - if useDaemon { + cfg, err := req.Context().GetConfig() + if err != nil { + return nil, err + } - cfg, err := req.Context().GetConfig() - if err != nil { - return nil, err - } + if useDaemon { addr, err := ma.NewMultiaddr(cfg.Addresses.API) if err != nil { @@ -235,6 +236,11 @@ func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) { } else { log.Info("Executing command locally") + // Check for updates and potentially install one. + if err := updates.CliCheckForUpdates(cfg, req.Context().ConfigRoot); err != nil { + return nil, err + } + // this sets up the function that will initialize the node // this is so that we can construct the node lazily. ctx := req.Context() diff --git a/config/version.go b/config/version.go index 8a659cd68..62cacfa49 100644 --- a/config/version.go +++ b/config/version.go @@ -8,7 +8,7 @@ import ( ) // CurrentVersionNumber is the current application's version literal -const CurrentVersionNumber = "0.1.6" +const CurrentVersionNumber = "0.1.7" // Version regulates checking if the most recent version is run type Version struct { diff --git a/updates/updates.go b/updates/updates.go index e7b6054d9..604a38d81 100644 --- a/updates/updates.go +++ b/updates/updates.go @@ -200,7 +200,7 @@ func CliCheckForUpdates(cfg *config.Config, confFile string) error { // if config says not to, don't check for updates if !cfg.Version.ShouldCheckForUpdate() { - log.Info("update checking disabled.") + log.Info("update check skipped.") return nil } From f30161b063feabb422056edfaedf8dfdda8e37e1 Mon Sep 17 00:00:00 2001 From: Juan Batiz-Benet Date: Fri, 14 Nov 2014 03:09:26 -0800 Subject: [PATCH 381/383] cmds: nicer error on no mountpoint --- core/commands2/mount_unix.go | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/core/commands2/mount_unix.go b/core/commands2/mount_unix.go index bf44033a0..f55b87aa9 100644 --- a/core/commands2/mount_unix.go +++ b/core/commands2/mount_unix.go @@ -4,10 +4,11 @@ package commands import ( "fmt" + "strings" "time" cmds "github.com/jbenet/go-ipfs/commands" - "github.com/jbenet/go-ipfs/config" + config "github.com/jbenet/go-ipfs/config" core "github.com/jbenet/go-ipfs/core" ipns "github.com/jbenet/go-ipfs/fuse/ipns" rofs "github.com/jbenet/go-ipfs/fuse/readonly" @@ -17,6 +18,9 @@ import ( // TODO is this non-deterministic? const mountTimeout = time.Second +// fuseNoDirectory used to check the returning fuse error +const fuseNoDirectory = "fusermount: failed to access mountpoint" + var mountCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Mounts IPFS to the filesystem (read-only)", @@ -25,7 +29,7 @@ Mount ipfs at a read-only mountpoint on the OS (default: /ipfs and /ipns). All ipfs objects will be accessible under that directory. Note that the root will not be listable, as it is virtual. Access known paths directly. -You may kave to create /ipfs and /ipfs before using 'ipfs mount': +You may have to create /ipfs and /ipfs before using 'ipfs mount': > sudo mkdir /ipfs /ipns > sudo chown ` + "`" + `whoami` + "`" + ` /ipfs /ipns @@ -36,6 +40,8 @@ Mount ipfs at a read-only mountpoint on the OS (default: /ipfs and /ipns). All ipfs objects will be accessible under that directory. Note that the root will not be listable, as it is virtual. Access known paths directly. +You may have to create /ipfs and /ipfs before using 'ipfs mount': + > sudo mkdir /ipfs /ipns > sudo chown ` + "`" + `whoami` + "`" + ` /ipfs /ipns > ipfs mount @@ -117,12 +123,22 @@ baz nsdone := mountIpns(node, nsdir, fsdir) + fmtFuseErr := func(err error) error { + s := err.Error() + if strings.Contains(s, fuseNoDirectory) { + s = strings.Replace(s, `fusermount: "fusermount:`, "", -1) + s = strings.Replace(s, `\n", exit status 1`, "", -1) + return cmds.ClientError(s) + } + return err + } + // wait until mounts return an error (or timeout if successful) select { case err := <-fsdone: - return nil, err + return nil, fmtFuseErr(err) case err := <-nsdone: - return nil, err + return nil, fmtFuseErr(err) // mounted successfully, we timed out with no errors case <-time.After(mountTimeout): From a0dab3a29ace830aa3f3d5334dfe5a35b8170deb Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 14 Nov 2014 00:50:37 -0800 Subject: [PATCH 382/383] docs(2/pin) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ipfs1 docs read: ``` btc λ. ipfs pin ipfs pin - Commands: add pin an ipfs object to local storage. rm unpin an ipfs object from local storage. Use "pin help " for more information about a command. ``` License: MIT Signed-off-by: Brian Tiger Chow --- core/commands2/pin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/commands2/pin.go b/core/commands2/pin.go index 30259f489..3790f5167 100644 --- a/core/commands2/pin.go +++ b/core/commands2/pin.go @@ -11,7 +11,7 @@ import ( var pinCmd = &cmds.Command{ Helptext: cmds.HelpText{ - Tagline: "Keeps objects stored locally", + Tagline: "Pin (and unpin) objects to local storage", }, Subcommands: map[string]*cmds.Command{ From ee378e2fbd20ecd100dc90e256d6d464f88977ae Mon Sep 17 00:00:00 2001 From: Brian Tiger Chow Date: Fri, 14 Nov 2014 03:31:06 -0800 Subject: [PATCH 383/383] fix(test) tour content struct now exists License: MIT Signed-off-by: Brian Tiger Chow --- cmd/ipfs2/tour_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd/ipfs2/tour_test.go b/cmd/ipfs2/tour_test.go index 9a11cc619..35e802278 100644 --- a/cmd/ipfs2/tour_test.go +++ b/cmd/ipfs2/tour_test.go @@ -9,12 +9,14 @@ import ( func TestParseTourTemplate(t *testing.T) { topic := &tour.Topic{ - ID: "42", - Title: "IPFS CLI test files", - Text: ` + ID: "42", + Content: tour.Content{ + Title: "IPFS CLI test files", + Text: ` Welcome to the IPFS test files This is where we test our beautiful command line interfaces `, + }, } var buf bytes.Buffer err := fprintTourShow(&buf, topic)