Merge "javascript/api/src/naming: Add a mounttable client library."
diff --git a/runtimes/google/vsync/kvdb.go b/runtimes/google/vsync/kvdb.go
index 04d18cb..7efefde 100644
--- a/runtimes/google/vsync/kvdb.go
+++ b/runtimes/google/vsync/kvdb.go
@@ -148,7 +148,7 @@
return nil, nil, fmt.Errorf("invalid DB filename %s", filename)
}
- fdesc, err := ioutil.TempFile("/tmp", prefix)
+ fdesc, err := ioutil.TempFile("", prefix)
if err != nil {
return nil, nil, err
}
diff --git a/runtimes/google/vsync/vsyncd/main.go b/runtimes/google/vsync/vsyncd/main.go
index fd12a7a..a2ca5a9 100644
--- a/runtimes/google/vsync/vsyncd/main.go
+++ b/runtimes/google/vsync/vsyncd/main.go
@@ -3,6 +3,7 @@
import (
"flag"
+ "os"
"veyron/runtimes/google/vsync"
"veyron2/ipc"
@@ -16,7 +17,7 @@
peerDeviceIDs := flag.String("peerids", "",
"comma separated list of deviceids of the vsync peer")
devid := flag.String("devid", "", "Device ID")
- storePath := flag.String("store", "/tmp/", "path to store files")
+ storePath := flag.String("store", os.TempDir(), "path to store files")
vstoreEndpoint := flag.String("vstore", "", "endpoint of the local Veyron store")
// TODO(rthellend): Remove the address flag when the config manager is working.
address := flag.String("address", ":0", "address to listen on")
diff --git a/services/mgmt/application/application/impl/impl.go b/services/mgmt/application/application/impl/impl.go
new file mode 100644
index 0000000..61c4a92
--- /dev/null
+++ b/services/mgmt/application/application/impl/impl.go
@@ -0,0 +1,213 @@
+package impl
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "strings"
+
+ "veyron/lib/cmdline"
+ iapp "veyron/services/mgmt/application"
+
+ "veyron2/services/mgmt/application"
+)
+
+func getEnvelopeJSON(app iapp.Repository, profiles string) ([]byte, error) {
+ env, err := app.Match(strings.Split(profiles, ","))
+ if err != nil {
+ env = application.Envelope{}
+ }
+ j, err := json.MarshalIndent(env, "", " ")
+ if err != nil {
+ return nil, fmt.Errorf("json: %v", err)
+ }
+ return j, nil
+}
+
+func putEnvelopeJSON(app iapp.Repository, profiles string, j []byte) error {
+ var env application.Envelope
+ if err := json.Unmarshal(j, &env); err != nil {
+ return fmt.Errorf("json: %v", err)
+ }
+ if err := app.Put(strings.Split(profiles, ","), env); err != nil {
+ return err
+ }
+ return nil
+}
+
+func promptUser(cmd *cmdline.Command, msg string) string {
+ fmt.Fprint(cmd.Stdout(), msg)
+ var answer string
+ if _, err := fmt.Scanf("%s", &answer); err != nil {
+ return ""
+ }
+ return answer
+}
+
+var cmdMatch = &cmdline.Command{
+ Run: runMatch,
+ Name: "match",
+ Short: "Shows the first matching envelope that matches the given profiles.",
+ Long: "Shows the first matching envelope that matches the given profiles.",
+ ArgsName: "<application> <profiles>",
+ ArgsLong: `
+<application> is the full name of the application.
+<profiles> is a comma-separated list of profiles.`,
+}
+
+func runMatch(cmd *cmdline.Command, args []string) error {
+ if expected, got := 2, len(args); expected != got {
+ return cmd.Errorf("match: incorrect number of arguments, expected %d, got %d", expected, got)
+ }
+ app, err := iapp.BindRepository(args[0])
+ if err != nil {
+ return fmt.Errorf("bind error: %v", err)
+ }
+ j, err := getEnvelopeJSON(app, args[1])
+ if err != nil {
+ return err
+ }
+ fmt.Fprintln(cmd.Stdout(), string(j))
+ return nil
+}
+
+var cmdPut = &cmdline.Command{
+ Run: runPut,
+ Name: "put",
+ Short: "Add the given envelope to the application for the given profiles.",
+ Long: "Add the given envelope to the application for the given profiles.",
+ ArgsName: "<application> <profiles> <envelope>",
+ ArgsLong: `
+<application> is the full name of the application.
+<profiles> is a comma-separated list of profiles.
+<envelope> is the file that contains a JSON-encoded envelope.`,
+}
+
+func runPut(cmd *cmdline.Command, args []string) error {
+ if expected, got := 3, len(args); expected != got {
+ return cmd.Errorf("put: incorrect number of arguments, expected %d, got %d", expected, got)
+ }
+ app, err := iapp.BindRepository(args[0])
+ if err != nil {
+ return fmt.Errorf("bind error: %v", err)
+ }
+ j, err := ioutil.ReadFile(args[2])
+ if err != nil {
+ return fmt.Errorf("read file %s: %v", args[2], err)
+ }
+ if err = putEnvelopeJSON(app, args[1], j); err != nil {
+ return err
+ }
+ fmt.Fprintln(cmd.Stdout(), "Application updated successfully.")
+ return nil
+}
+
+var cmdRemove = &cmdline.Command{
+ Run: runRemove,
+ Name: "remove",
+ Short: "removes the application envelope for the given profile.",
+ Long: "removes the application envelope for the given profile.",
+ ArgsName: "<application> <profile>",
+ ArgsLong: `
+<application> is the full name of the application.
+<profile> is a profile.`,
+}
+
+func runRemove(cmd *cmdline.Command, args []string) error {
+ if expected, got := 2, len(args); expected != got {
+ return cmd.Errorf("remove: incorrect number of arguments, expected %d, got %d", expected, got)
+ }
+ app, err := iapp.BindRepository(args[0])
+ if err != nil {
+ return fmt.Errorf("bind error: %v", err)
+ }
+ if err = app.Remove(args[1]); err != nil {
+ return err
+ }
+ fmt.Fprintln(cmd.Stdout(), "Application envelope removed successfully.")
+ return nil
+}
+
+var cmdEdit = &cmdline.Command{
+ Run: runEdit,
+ Name: "edit",
+ Short: "edits the application envelope for the given profile.",
+ Long: "edits the application envelope for the given profile.",
+ ArgsName: "<application> <profile>",
+ ArgsLong: `
+<application> is the full name of the application.
+<profile> is a profile.`,
+}
+
+func runEdit(cmd *cmdline.Command, args []string) error {
+ if expected, got := 2, len(args); expected != got {
+ return cmd.Errorf("edit: incorrect number of arguments, expected %d, got %d", expected, got)
+ }
+ app, err := iapp.BindRepository(args[0])
+ if err != nil {
+ return fmt.Errorf("bind error: %v", err)
+ }
+ f, err := ioutil.TempFile("", "application-edit-")
+ if err != nil {
+ return fmt.Errorf("bind error: %v", err)
+ }
+ fileName := f.Name()
+ f.Close()
+ defer os.Remove(fileName)
+
+ envData, err := getEnvelopeJSON(app, args[1])
+ if err != nil {
+ return err
+ }
+ if err = ioutil.WriteFile(fileName, envData, os.FileMode(0644)); err != nil {
+ return err
+ }
+ editor := os.Getenv("EDITOR")
+ if len(editor) == 0 {
+ editor = "nano"
+ }
+ for {
+ c := exec.Command("sh", "-c", fmt.Sprintf("%s %s", editor, fileName))
+ c.Stdin = os.Stdin
+ c.Stdout = os.Stdout
+ c.Stderr = os.Stderr
+ if err := c.Run(); err != nil {
+ return fmt.Errorf("failed to run %s %s", editor, fileName)
+ }
+ newData, err := ioutil.ReadFile(fileName)
+ if err != nil {
+ fmt.Fprintf(cmd.Stdout(), "Error: %v\n", err)
+ if ans := promptUser(cmd, "Try again? [y/N] "); strings.ToUpper(ans) == "Y" {
+ continue
+ }
+ return errors.New("aborted")
+ }
+ if bytes.Compare(envData, newData) == 0 {
+ fmt.Fprintln(cmd.Stdout(), "Nothing changed")
+ return nil
+ }
+ if err = putEnvelopeJSON(app, args[1], newData); err != nil {
+ fmt.Fprintf(cmd.Stdout(), "Error: %v\n", err)
+ if ans := promptUser(cmd, "Try again? [y/N] "); strings.ToUpper(ans) == "Y" {
+ continue
+ }
+ return errors.New("aborted")
+ }
+ break
+ }
+ fmt.Fprintln(cmd.Stdout(), "Application envelope updated successfully.")
+ return nil
+}
+
+func Root() *cmdline.Command {
+ return &cmdline.Command{
+ Name: "application",
+ Short: "Command-line tool for interacting with the Veyron application manager",
+ Long: "Command-line tool for interacting with the Veyron application manager",
+ Children: []*cmdline.Command{cmdMatch, cmdPut, cmdRemove, cmdEdit},
+ }
+}
diff --git a/services/mgmt/application/application/impl/impl_test.go b/services/mgmt/application/application/impl/impl_test.go
new file mode 100644
index 0000000..5963fee
--- /dev/null
+++ b/services/mgmt/application/application/impl/impl_test.go
@@ -0,0 +1,171 @@
+package impl_test
+
+import (
+ "bytes"
+ "io/ioutil"
+ "os"
+ "strings"
+ "testing"
+
+ iapp "veyron/services/mgmt/application"
+ "veyron/services/mgmt/application/application/impl"
+
+ "veyron2"
+ "veyron2/ipc"
+ "veyron2/naming"
+ "veyron2/rt"
+ "veyron2/security"
+ "veyron2/services/mgmt/application"
+ "veyron2/vlog"
+)
+
+var (
+ envelope = application.Envelope{
+ Args: []string{"arg1", "arg2", "arg3"},
+ Binary: "/path/to/binary",
+ Env: []string{"env1", "env2", "env3"},
+ }
+ jsonEnv = `{
+ "Args": [
+ "arg1",
+ "arg2",
+ "arg3"
+ ],
+ "Binary": "/path/to/binary",
+ "Env": [
+ "env1",
+ "env2",
+ "env3"
+ ]
+}`
+)
+
+type server struct {
+ suffix string
+}
+
+func (s *server) Match(_ ipc.Context, profiles []string) (application.Envelope, error) {
+ vlog.VI(2).Infof("%v.Match(%v) was called", s.suffix, profiles)
+ return envelope, nil
+}
+
+func (s *server) Put(_ ipc.Context, profiles []string, env application.Envelope) error {
+ vlog.VI(2).Infof("%v.Put(%v, %v) was called", s.suffix, profiles, env)
+ return nil
+}
+
+func (s *server) Remove(_ ipc.Context, profile string) error {
+ vlog.VI(2).Infof("%v.Remove(%v) was called", s.suffix, profile)
+ return nil
+}
+
+type dispatcher struct {
+}
+
+func NewDispatcher() *dispatcher {
+ return &dispatcher{}
+}
+
+func (d *dispatcher) Lookup(suffix string) (ipc.Invoker, security.Authorizer, error) {
+ invoker := ipc.ReflectInvoker(iapp.NewServerRepository(&server{suffix: suffix}))
+ return invoker, nil, nil
+}
+
+func startServer(t *testing.T, r veyron2.Runtime) (ipc.Server, naming.Endpoint, error) {
+ dispatcher := NewDispatcher()
+ server, err := r.NewServer()
+ if err != nil {
+ t.Errorf("NewServer failed: %v", err)
+ return nil, nil, err
+ }
+ if err := server.Register("", dispatcher); err != nil {
+ t.Errorf("Register failed: %v", err)
+ return nil, nil, err
+ }
+ endpoint, err := server.Listen("tcp", "localhost:0")
+ if err != nil {
+ t.Errorf("Listen failed: %v", err)
+ return nil, nil, err
+ }
+ return server, endpoint, nil
+}
+
+func stopServer(t *testing.T, server ipc.Server) {
+ if err := server.Stop(); err != nil {
+ t.Errorf("server.Stop failed: %v", err)
+ }
+}
+
+func TestApplicationClient(t *testing.T) {
+ runtime := rt.Init()
+ server, endpoint, err := startServer(t, runtime)
+ if err != nil {
+ return
+ }
+ defer stopServer(t, server)
+ // Setup the command-line.
+ cmd := impl.Root()
+ var stdout, stderr bytes.Buffer
+ cmd.Init(nil, &stdout, &stderr)
+ appName := naming.JoinAddressName(endpoint.String(), "myapp/1")
+ profile := "myprofile"
+
+ // Test the 'Match' command.
+ if err := cmd.Execute([]string{"match", appName, profile}); err != nil {
+ t.Fatalf("%v", err)
+ }
+ if expected, got := jsonEnv, strings.TrimSpace(stdout.String()); got != expected {
+ t.Errorf("Unexpected output from match. Got %q, expected %q", got, expected)
+ }
+ stdout.Reset()
+
+ // Test the 'put' command.
+ f, err := ioutil.TempFile("", "test")
+ if err != nil {
+ t.Fatalf("%v", err)
+ }
+ fileName := f.Name()
+ defer os.Remove(fileName)
+ if _, err = f.Write([]byte(jsonEnv)); err != nil {
+ t.Fatalf("%v", err)
+ }
+ if err = f.Close(); err != nil {
+ t.Fatalf("%v", err)
+ }
+ if err := cmd.Execute([]string{"put", appName, profile, fileName}); err != nil {
+ t.Fatalf("%v", err)
+ }
+ if expected, got := "Application updated successfully.", strings.TrimSpace(stdout.String()); got != expected {
+ t.Errorf("Unexpected output from put. Got %q, expected %q", got, expected)
+ }
+ stdout.Reset()
+
+ // Test the 'remove' command.
+ if err := cmd.Execute([]string{"remove", appName, profile}); err != nil {
+ t.Fatalf("%v", err)
+ }
+ if expected, got := "Application envelope removed successfully.", strings.TrimSpace(stdout.String()); got != expected {
+ t.Errorf("Unexpected output from remove. Got %q, expected %q", got, expected)
+ }
+ stdout.Reset()
+
+ // Test the 'edit' command. (nothing changed)
+ os.Setenv("EDITOR", "true")
+ if err := cmd.Execute([]string{"edit", appName, profile}); err != nil {
+ t.Fatalf("%v", err)
+ }
+ if expected, got := "Nothing changed", strings.TrimSpace(stdout.String()); got != expected {
+ t.Errorf("Unexpected output from edit. Got %q, expected %q", got, expected)
+ }
+ stdout.Reset()
+
+ // Test the 'edit' command.
+ os.Setenv("EDITOR", "sed -i 's/arg1/arg111/'")
+ if err := cmd.Execute([]string{"edit", appName, profile}); err != nil {
+ t.Fatalf("%v", err)
+ }
+ if expected, got := "Application envelope updated successfully.", strings.TrimSpace(stdout.String()); got != expected {
+ t.Errorf("Unexpected output from edit. Got %q, expected %q", got, expected)
+ }
+ stdout.Reset()
+}
diff --git a/services/mgmt/application/application/main.go b/services/mgmt/application/application/main.go
new file mode 100644
index 0000000..7aa74a1
--- /dev/null
+++ b/services/mgmt/application/application/main.go
@@ -0,0 +1,12 @@
+package main
+
+import (
+ "veyron/services/mgmt/application/application/impl"
+
+ "veyron2/rt"
+)
+
+func main() {
+ defer rt.Init().Shutdown()
+ impl.Root().Main()
+}