veyron/lib/modules/core: add some initial core modules for naming.
Change-Id: I37edd9686b8fb52deafb835bd4b2f8ec7ada4565
diff --git a/lib/modules/core/core.go b/lib/modules/core/core.go
new file mode 100644
index 0000000..40da4c7
--- /dev/null
+++ b/lib/modules/core/core.go
@@ -0,0 +1,25 @@
+// Package core provides modules.Shell instances with commands preinstalled for
+// common core services such as naming, security etc.
+package core
+
+import "veyron/lib/modules"
+
+const (
+ RootMTCommand = "root"
+ MTCommand = "mt"
+ LSCommand = "ls"
+ LSExternalCommand = "lse"
+ MountCommand = "mount"
+)
+
+func NewShell() *modules.Shell {
+ shell := modules.NewShell()
+ shell.AddSubprocess(RootMTCommand, "")
+ shell.AddSubprocess(MTCommand, `<mount point>
+ reads NAMESPACE_ROOT from its environment and mounts a new mount table at <mount point>`)
+ shell.AddFunction(LSCommand, ls, `<glob>...
+ issues glob requests using the current processes namespace library`)
+ shell.AddSubprocess(LSExternalCommand, `<glob>...
+ runs a subprocess to issue glob requests using the subprocesses namespace library`)
+ return shell
+}
diff --git a/lib/modules/core/core_test.go b/lib/modules/core/core_test.go
new file mode 100644
index 0000000..161890a
--- /dev/null
+++ b/lib/modules/core/core_test.go
@@ -0,0 +1,151 @@
+package core_test
+
+import (
+ "os"
+ "reflect"
+ "sort"
+ "testing"
+ "time"
+
+ "veyron2/rt"
+
+ "veyron/lib/expect"
+ "veyron/lib/modules"
+ "veyron/lib/modules/core"
+)
+
+func TestCommands(t *testing.T) {
+ shell := core.NewShell()
+ defer shell.Cleanup(os.Stderr)
+ for _, c := range []string{core.RootMTCommand, core.MTCommand} {
+ if len(shell.Help(c)) == 0 {
+ t.Fatalf("missing command %q", c)
+ }
+ }
+}
+
+func init() {
+ rt.Init()
+}
+
+func TestRoot(t *testing.T) {
+ shell := core.NewShell()
+ defer shell.Cleanup(os.Stderr)
+
+ root, err := shell.Start(core.RootMTCommand)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ s := expect.NewSession(t, root.Stdout(), time.Second)
+ s.ExpectVar("MT_NAME")
+ s.ExpectVar("PID")
+ root.Stdin().Close()
+ s.Expect("done")
+}
+
+func getMatchingMountpoint(r [][]string) string {
+ if len(r) != 1 {
+ return ""
+ }
+ shortest := ""
+ for _, p := range r[0][1:] {
+ if len(p) > 0 {
+ if len(shortest) == 0 {
+ shortest = p
+ }
+ if len(shortest) > 0 && len(p) < len(shortest) {
+ shortest = p
+ }
+ }
+ }
+ return shortest
+}
+
+func TestMountTableAndGlob(t *testing.T) {
+ shell := core.NewShell()
+ if testing.Verbose() {
+ defer shell.Cleanup(os.Stderr)
+ } else {
+ defer shell.Cleanup(nil)
+ }
+
+ // Start root mount table
+ root, err := shell.Start(core.RootMTCommand)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ rootSession := expect.NewSession(t, root.Stdout(), time.Second)
+ rootName := rootSession.ExpectVar("MT_NAME")
+ shell.SetVar("NAMESPACE_ROOT", rootName)
+
+ if t.Failed() {
+ return
+ }
+ mountPoints := []string{"a", "b", "c"}
+
+ // Start 3 mount tables
+ for _, mp := range mountPoints {
+ _, err := shell.Start(core.MTCommand, mp)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ }
+
+ ls, err := shell.Start(core.LSCommand, rootName+"/...")
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ lsSession := expect.NewSession(t, ls.Stdout(), time.Second)
+ lsSession.Expect(rootName)
+
+ // Look for names that correspond to the mountpoints above (i.e, a, b or c)
+ pattern := ""
+ for _, n := range mountPoints {
+ pattern = pattern + "(" + rootName + "/(" + n + ")$)|"
+ }
+ pattern = pattern[:len(pattern)-1]
+
+ found := []string{}
+ found = append(found, getMatchingMountpoint(lsSession.ExpectRE(pattern, 1)))
+ found = append(found, getMatchingMountpoint(lsSession.ExpectRE(pattern, 1)))
+ found = append(found, getMatchingMountpoint(lsSession.ExpectRE(pattern, 1)))
+ sort.Strings(found)
+ sort.Strings(mountPoints)
+ if got, want := found, mountPoints; !reflect.DeepEqual(got, want) {
+ t.Errorf("got %v, want %v", got, want)
+ }
+
+ // Run the ls command in a subprocess, with NAMESPACE_ROOT as set above.
+ lse, err := shell.Start(core.LSExternalCommand, "...")
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ lseSession := expect.NewSession(t, lse.Stdout(), time.Second)
+
+ pattern = ""
+ for _, n := range mountPoints {
+ // Since the LSExternalCommand runs in a subprocess with NAMESPACE_ROOT
+ // set to the name of the root mount table it sees the relative name
+ // format of the mounted mount tables.
+ pattern = pattern + "(^" + n + "$)|"
+ }
+ pattern = pattern[:len(pattern)-1]
+ found = []string{}
+ found = append(found, getMatchingMountpoint(lseSession.ExpectRE(pattern, 1)))
+ found = append(found, getMatchingMountpoint(lseSession.ExpectRE(pattern, 1)))
+ found = append(found, getMatchingMountpoint(lseSession.ExpectRE(pattern, 1)))
+ sort.Strings(found)
+ sort.Strings(mountPoints)
+ if got, want := found, mountPoints; !reflect.DeepEqual(got, want) {
+ t.Errorf("got %v, want %v", got, want)
+ }
+}
+
+func TestHelperProcess(t *testing.T) {
+ if !modules.IsTestHelperProcess() {
+ return
+ }
+ if err := modules.Dispatch(); err != nil {
+ t.Fatalf("failed: %v", err)
+ }
+}
diff --git a/lib/modules/core/mounttable.go b/lib/modules/core/mounttable.go
new file mode 100644
index 0000000..b7862f5
--- /dev/null
+++ b/lib/modules/core/mounttable.go
@@ -0,0 +1,98 @@
+package core
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "strings"
+
+ "veyron2"
+ "veyron2/naming"
+ "veyron2/rt"
+
+ "veyron/lib/modules"
+ mounttable "veyron/services/mounttable/lib"
+)
+
+func init() {
+ modules.RegisterChild(RootMTCommand, rootMountTable)
+ modules.RegisterChild(MTCommand, mountTable)
+ modules.RegisterChild(LSExternalCommand, ls)
+}
+
+func mountTable(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+ if len(args) != 1 {
+ return fmt.Errorf("expected exactly one argument: <mount point>")
+ }
+ return runMT(false, stdin, stdout, stderr, env, args...)
+}
+
+func rootMountTable(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+ if len(args) != 0 {
+ return fmt.Errorf("expected no arguments")
+ }
+ return runMT(true, stdin, stdout, stderr, env, args...)
+}
+
+func runMT(root bool, stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+ r := rt.Init()
+ server, err := r.NewServer(veyron2.ServesMountTableOpt(true))
+ if err != nil {
+ return fmt.Errorf("root failed: %v", err)
+ }
+ mp := ""
+ if !root {
+ mp = args[0]
+ }
+ mt, err := mounttable.NewMountTable("")
+ if err != nil {
+ return fmt.Errorf("mounttable.NewMountTable failed: %s", err)
+ }
+ ep, err := server.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ return fmt.Errorf("server.Listen failed: %s", err)
+ }
+ if err := server.Serve(mp, mt); err != nil {
+ return fmt.Errorf("root failed: %s", err)
+ }
+ name := naming.JoinAddressName(ep.String(), "")
+ fmt.Fprintf(os.Stderr, "Serving MountTable on %q", name)
+
+ fmt.Printf("MT_NAME=%s\n", name)
+ fmt.Printf("PID=%d\n", os.Getpid())
+ modules.WaitForEOF(stdin)
+ fmt.Println("done\n")
+ return nil
+}
+
+func ls(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+ details := false
+ if len(args) > 0 && args[0] == "-l" {
+ details = true
+ args = args[1:]
+ }
+
+ ns := rt.R().Namespace()
+ for _, pattern := range args {
+ ch, err := ns.Glob(rt.R().NewContext(), pattern)
+ if err != nil {
+ return err
+ }
+ for n := range ch {
+ if details {
+ fmt.Fprintf(stdout, "%s [", n.Name)
+ t := ""
+ for _, s := range n.Servers {
+ t += fmt.Sprintf("%s:%ss, ", s.Server, s.TTL)
+ }
+ t = strings.TrimSuffix(t, ", ")
+ fmt.Fprintf(stdout, "%s]\n", t)
+ } else {
+ if len(n.Name) > 0 {
+ fmt.Fprintf(stdout, "%s\n", n.Name)
+ }
+ }
+ }
+ }
+ return nil
+}
diff --git a/lib/modules/modules_test.go b/lib/modules/modules_test.go
index 81739b7..7414358 100644
--- a/lib/modules/modules_test.go
+++ b/lib/modules/modules_test.go
@@ -23,8 +23,7 @@
fmt.Fprintf(stderr, "missing %s\n", a)
}
}
- buf := [1]byte{0x0}
- stdin.Read(buf[:])
+ modules.WaitForEOF(stdin)
fmt.Fprintf(stdout, "done\n")
return nil
}
diff --git a/lib/modules/shell.go b/lib/modules/shell.go
index 09941b8..4df120b 100644
--- a/lib/modules/shell.go
+++ b/lib/modules/shell.go
@@ -39,6 +39,7 @@
"bufio"
"fmt"
"io"
+ "strings"
"sync"
"veyron2/vlog"
@@ -104,10 +105,10 @@
sh.mu.Lock()
defer sh.mu.Unlock()
h := ""
- for _, c := range sh.cmds {
- h += c.help
+ for n, _ := range sh.cmds {
+ h += n + ", "
}
- return h
+ return strings.TrimRight(h, ", ")
}
// Help returns the help message for the specified command.
@@ -115,7 +116,7 @@
sh.mu.Lock()
defer sh.mu.Unlock()
if c := sh.cmds[command]; c != nil {
- return c.help
+ return command + ": " + c.help
}
return ""
}
@@ -236,3 +237,12 @@
type command interface {
start(sh *Shell, args ...string) (Handle, error)
}
+
+func WaitForEOF(stdin io.Reader) {
+ buf := [1024]byte{}
+ for {
+ if _, err := stdin.Read(buf[:]); err == io.EOF {
+ return
+ }
+ }
+}