Merge "veyron/tools/mgmt/node: node manager cmd-line tool (in progress)"
diff --git a/tools/mgmt/node/impl.go b/tools/mgmt/node/impl.go
new file mode 100644
index 0000000..b538977
--- /dev/null
+++ b/tools/mgmt/node/impl.go
@@ -0,0 +1,86 @@
+package main
+
+import (
+	"fmt"
+
+	"veyron.io/veyron/veyron2/ipc"
+	"veyron.io/veyron/veyron2/naming"
+	"veyron.io/veyron/veyron2/rt"
+	"veyron.io/veyron/veyron2/security"
+	"veyron.io/veyron/veyron2/services/mgmt/node"
+
+	"veyron.io/veyron/veyron/lib/cmdline"
+)
+
+var cmdInstall = &cmdline.Command{
+	Run:      runInstall,
+	Name:     "install",
+	Short:    "Install the given application.",
+	Long:     "Install the given application.",
+	ArgsName: "<node> <application>",
+	ArgsLong: `
+<node> is the veyron object name of the node manager's app service.
+<application> is the veyron object name of the application.`,
+}
+
+func runInstall(cmd *cmdline.Command, args []string) error {
+	if expected, got := 2, len(args); expected != got {
+		return cmd.UsageErrorf("install: incorrect number of arguments, expected %d, got %d", expected, got)
+	}
+	nodeName, appName := args[0], args[1]
+	appID, err := node.ApplicationClient(nodeName).Install(rt.R().NewContext(), appName)
+	if err != nil {
+		return fmt.Errorf("Install failed: %v", err)
+	}
+	fmt.Fprintf(cmd.Stdout(), "Successfully installed: %q\n", naming.Join(nodeName, appID))
+	return nil
+}
+
+var cmdStart = &cmdline.Command{
+	Run:      runStart,
+	Name:     "start",
+	Short:    "Start an instance of the given application.",
+	Long:     "Start an instance of the given application.",
+	ArgsName: "<application installation> <grant extension>",
+	ArgsLong: `
+<application installation> is the veyron object name of the
+application installation from which to start an instance.
+<grant extension> is used to extend the default blessing of the
+current principal when blessing the app instance.`,
+}
+
+type granter struct {
+	ipc.CallOpt
+	p         security.Principal
+	extension string
+}
+
+func (g *granter) Grant(other security.Blessings) (security.Blessings, error) {
+	return g.p.Bless(other.PublicKey(), g.p.BlessingStore().Default(), g.extension, security.UnconstrainedUse())
+}
+
+func runStart(cmd *cmdline.Command, args []string) error {
+	if expected, got := 2, len(args); expected != got {
+		return cmd.UsageErrorf("start: incorrect number of arguments, expected %d, got %d", expected, got)
+	}
+	appInstallation, grant := args[0], args[1]
+	appInstanceIDs, err := node.ApplicationClient(appInstallation).Start(rt.R().NewContext(), &granter{p: rt.R().Principal(), extension: grant})
+	if err != nil {
+		return fmt.Errorf("Start failed: %v", err)
+	}
+	for _, id := range appInstanceIDs {
+		fmt.Fprintf(cmd.Stdout(), "Successfully started: %q\n", naming.Join(appInstallation, id))
+	}
+	return nil
+}
+
+func root() *cmdline.Command {
+	return &cmdline.Command{
+		Name:  "node",
+		Short: "Tool for interacting with the veyron node manager",
+		Long: `
+The node tool facilitates interaction with the veyron node manager.
+`,
+		Children: []*cmdline.Command{cmdInstall, cmdStart},
+	}
+}
diff --git a/tools/mgmt/node/main.go b/tools/mgmt/node/main.go
new file mode 100644
index 0000000..b4e497c
--- /dev/null
+++ b/tools/mgmt/node/main.go
@@ -0,0 +1,12 @@
+package main
+
+import (
+	"veyron.io/veyron/veyron2/rt"
+
+	_ "veyron.io/veyron/veyron/profiles"
+)
+
+func main() {
+	defer rt.Init().Cleanup()
+	root().Main()
+}