Merge ""veyron/services/identity: TBR: Enable revocation now that third party caveat issue is resolved."
diff --git a/lib/exec/exec_test.go b/lib/exec/exec_test.go
index f34b280..a013d55 100644
--- a/lib/exec/exec_test.go
+++ b/lib/exec/exec_test.go
@@ -410,6 +410,29 @@
clean(t, ph)
}
+func TestWaitAndCleanRace(t *testing.T) {
+ name := "TestWaitAndCleanRace"
+ cmd := helperCommand("testReadySlow")
+ tk := timekeeper.NewManualTime()
+ ph := vexec.NewParentHandle(cmd, vexec.TimeKeeperOpt{tk})
+ err := ph.Start()
+ if err != nil {
+ t.Fatalf("%s: start: %v", name, err)
+ }
+ go func() {
+ // Wait for the ph.Wait below to block, then advance the time
+ // s.t. the Wait times out.
+ <-tk.Requests()
+ tk.AdvanceTime(2 * time.Second)
+ }()
+ if got, want := ph.Wait(time.Second), vexec.ErrTimeout; got == nil || got.Error() != want.Error() {
+ t.Fatalf("Wait returned %v, wanted %v instead", got, want)
+ }
+ if got, want := ph.Clean(), "signal: killed"; got == nil || got.Error() != want {
+ t.Fatalf("Wait returned %v, wanted %v instead", got, want)
+ }
+}
+
func verifyNoExecVariable() {
version := os.Getenv(vexec.VersionVariable)
if len(version) != 0 {
diff --git a/lib/exec/parent.go b/lib/exec/parent.go
index 8520b4a..9d98c15 100644
--- a/lib/exec/parent.go
+++ b/lib/exec/parent.go
@@ -8,6 +8,7 @@
"os"
"os/exec"
"strings"
+ "sync"
"syscall"
"time"
@@ -30,6 +31,9 @@
statusRead *os.File
statusWrite *os.File
tk timekeeper.TimeKeeper
+ waitDone bool
+ waitErr error
+ waitLock sync.Mutex
}
// ParentHandleOpt is an option for NewParentHandle.
@@ -202,13 +206,28 @@
panic("unreachable")
}
+// wait performs the Wait on the underlying command under lock, and only once
+// (subsequent wait calls block until the Wait is finished). It's ok to call
+// wait multiple times, and in parallel. The error from the initial Wait is
+// cached and returned for all subsequent calls.
+func (p *ParentHandle) wait() error {
+ p.waitLock.Lock()
+ defer p.waitLock.Unlock()
+ if p.waitDone {
+ return p.waitErr
+ }
+ p.waitErr = p.c.Wait()
+ p.waitDone = true
+ return p.waitErr
+}
+
// Wait will wait for the child process to terminate of its own accord.
// It returns nil if the process exited cleanly with an exit status of 0,
// any other exit code or error will result in an appropriate error return
func (p *ParentHandle) Wait(timeout time.Duration) error {
c := make(chan error, 1)
go func() {
- c <- p.c.Wait()
+ c <- p.wait()
close(c)
}()
// If timeout is zero time.After will panic; we handle zero specially
@@ -257,7 +276,7 @@
if err := p.Kill(); err != nil {
return err
}
- return p.c.Wait()
+ return p.wait()
}
func encodeString(w io.Writer, data string) error {
diff --git a/lib/expect/expect.go b/lib/expect/expect.go
index b9b8311..afed004 100644
--- a/lib/expect/expect.go
+++ b/lib/expect/expect.go
@@ -242,7 +242,7 @@
return [][]string{}
}
l, m, err := s.expectRE(pattern, n)
- s.log(err, "ExpectVar: %s", l)
+ s.log(err, "ExpectRE: %s", l)
if err != nil {
s.error(err)
return [][]string{}
diff --git a/lib/filelocker/locker_test.go b/lib/filelocker/locker_test.go
index f3ce658..fc23e17 100644
--- a/lib/filelocker/locker_test.go
+++ b/lib/filelocker/locker_test.go
@@ -63,7 +63,10 @@
filepath := newFile()
defer os.Remove(filepath)
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(os.Stderr, os.Stderr)
h, err := sh.Start("testLockChild", nil, filepath)
if err != nil {
@@ -133,7 +136,10 @@
filepath := newFile()
defer os.Remove(filepath)
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(os.Stderr, os.Stderr)
h, err := sh.Start("testLockChild", nil, filepath)
if err != nil {
diff --git a/lib/modules/core/core_test.go b/lib/modules/core/core_test.go
index 5f72ee9..86bbb0e 100644
--- a/lib/modules/core/core_test.go
+++ b/lib/modules/core/core_test.go
@@ -23,8 +23,8 @@
)
func TestCommands(t *testing.T) {
- sh := modules.NewShell()
- defer sh.Cleanup(nil, os.Stderr)
+ sh, fn := newShell(t)
+ defer fn()
for _, c := range []string{core.RootMTCommand, core.MTCommand} {
if len(sh.Help(c)) == 0 {
t.Fatalf("missing command %q", c)
@@ -37,8 +37,11 @@
rt.Init()
}
-func newShell() (*modules.Shell, func()) {
- sh := modules.NewShell()
+func newShell(t *testing.T) (*modules.Shell, func()) {
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
return sh, func() {
if testing.Verbose() {
vlog.Infof("------ cleanup ------")
@@ -55,7 +58,7 @@
}
func TestRoot(t *testing.T) {
- sh, fn := newShell()
+ sh, fn := newShell(t)
defer fn()
root, err := sh.Start(core.RootMTCommand, nil, testArgs()...)
if err != nil {
@@ -128,7 +131,7 @@
}
func TestMountTableAndGlob(t *testing.T) {
- sh, fn := newShell()
+ sh, fn := newShell(t)
defer fn()
mountPoints := []string{"a", "b", "c", "d", "e"}
@@ -201,7 +204,7 @@
}
func TestEcho(t *testing.T) {
- sh, fn := newShell()
+ sh, fn := newShell(t)
defer fn()
srv, err := sh.Start(core.EchoServerCommand, nil, testArgs("test", "")...)
@@ -223,7 +226,7 @@
}
func TestResolve(t *testing.T) {
- sh, fn := newShell()
+ sh, fn := newShell(t)
defer fn()
mountPoints := []string{"a", "b"}
diff --git a/lib/modules/core/mounttable.go b/lib/modules/core/mounttable.go
index 51373a6..bee0e5d 100644
--- a/lib/modules/core/mounttable.go
+++ b/lib/modules/core/mounttable.go
@@ -120,7 +120,7 @@
return nil
}
-type resolver func(ctx context.T, name string) (names []string, err error)
+type resolver func(ctx context.T, name string, opts ...naming.ResolveOpt) (names []string, err error)
func resolve(fn resolver, stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
if err := checkArgs(args[1:], 1, "<name>"); err != nil {
@@ -148,9 +148,5 @@
}
func setNamespaceRoots(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
- ns := rt.R().Namespace()
- if err := checkArgs(args, -1, "<name>..."); err != nil {
- return err
- }
- return ns.SetRoots(args[1:]...)
+ return rt.R().Namespace().SetRoots(args[1:]...)
}
diff --git a/lib/modules/core/proxy.go b/lib/modules/core/proxy.go
index 3f83ece..4cba574 100644
--- a/lib/modules/core/proxy.go
+++ b/lib/modules/core/proxy.go
@@ -3,6 +3,7 @@
import (
"fmt"
"io"
+ "strings"
"time"
"veyron.io/veyron/veyron2/naming"
@@ -48,6 +49,12 @@
defer pub.WaitForStop()
defer pub.Stop()
pub.AddServer(pname, false)
+ // If the protocol is tcp we need to also publish the websocket endpoint.
+ // TODO(bjornick): Remove this hack before we launch.
+ if strings.HasPrefix(proxy.Endpoint().Addr().Network(), "tcp") {
+ wsEP := strings.Replace(pname, "@"+proxy.Endpoint().Addr().Network()+"@", "@ws@", 1)
+ pub.AddServer(wsEP, false)
+ }
for _, name := range args {
pub.AddName(name)
}
diff --git a/lib/modules/exec.go b/lib/modules/exec.go
index d656d0a..74acf2f 100644
--- a/lib/modules/exec.go
+++ b/lib/modules/exec.go
@@ -24,6 +24,7 @@
stderr *os.File
stdout io.ReadCloser
stdin io.WriteCloser
+ procErrCh chan error
}
func testFlags() []string {
@@ -44,13 +45,15 @@
// use supplied command value for subprocesses
fl = append(fl, "--test.timeout="+timeout.Value.String())
} else {
- // translate default value into 1m for subproccesses
- fl = append(fl, "--test.timeout=1m")
+ // translate default value into 3m for subproccesses. The
+ // default of 10m is too long to wait in order to find out that
+ // our subprocess is wedged.
+ fl = append(fl, "--test.timeout=3m")
}
return fl
}
-// IsTestHelperProces returns true if it is called in via
+// IsTestHelperProcess returns true if it is called in via
// -run=TestHelperProcess which normally only ever happens for subprocesses
// run from tests.
func IsTestHelperProcess() bool {
@@ -62,7 +65,7 @@
}
func newExecHandle(name string) command {
- return &execHandle{name: name, entryPoint: shellEntryPoint + "=" + name}
+ return &execHandle{name: name, entryPoint: shellEntryPoint + "=" + name, procErrCh: make(chan error, 1)}
}
func (eh *execHandle) Stdout() io.Reader {
@@ -112,15 +115,19 @@
newargs, newenv := eh.envelope(sh, env, args[1:]...)
cmd := exec.Command(os.Args[0], newargs[1:]...)
cmd.Env = newenv
- stderr, err := newLogfile(strings.TrimLeft(eh.name, "-\n\t "))
+ stderr, err := newLogfile("stderr", eh.name)
if err != nil {
return nil, err
}
cmd.Stderr = stderr
- stdout, err := cmd.StdoutPipe()
- if err != nil {
- return nil, err
- }
+ // We use a custom queue-based Writer implementation for stdout to
+ // decouple the consumers of eh.stdout from the file where the child
+ // sends its output. This avoids data races between closing the file
+ // and reading from it (since cmd.Wait will wait for the all readers to
+ // be done before closing it). It also enables Shutdown to drain stdout
+ // while respecting the timeout.
+ stdout := newRW()
+ cmd.Stdout = stdout
stdin, err := cmd.StdinPipe()
if err != nil {
return nil, err
@@ -139,6 +146,15 @@
}
vlog.VI(1).Infof("Started: %q, pid %d", eh.name, cmd.Process.Pid)
err = handle.WaitForReady(sh.startTimeout)
+ go func() {
+ eh.procErrCh <- eh.handle.Wait(0)
+ // It's now safe to close eh.stdout, since Wait only returns
+ // once all writes from the pipe to the stdout Writer have
+ // completed. Closing eh.stdout lets consumers of stdout wrap
+ // up (they'll receive EOF).
+ eh.stdout.Close()
+ }()
+
return eh, err
}
@@ -150,39 +166,36 @@
eh.mu.Lock()
defer eh.mu.Unlock()
vlog.VI(1).Infof("Shutdown: %q", eh.name)
+ defer vlog.VI(1).Infof("Shutdown: %q [DONE]", eh.name)
eh.stdin.Close()
- logFile := eh.stderr.Name()
defer eh.sh.Forget(eh)
- defer func() {
- os.Remove(logFile)
- }()
-
- // TODO(cnicolaou): make this configurable
- timeout := 10 * time.Second
- if stdout == nil && stderr == nil {
- return eh.handle.Wait(timeout)
- }
-
+ waitStdout := make(chan struct{})
if stdout != nil {
- // Read from stdin before waiting for the child process to ensure
- // that we get to read all of its output.
- readTo(eh.stdout, stdout)
+ // Drain stdout.
+ go func() {
+ io.Copy(stdout, eh.stdout)
+ close(waitStdout)
+ }()
+ } else {
+ close(waitStdout)
}
- procErr := eh.handle.Wait(timeout)
-
- // Stderr is buffered to a file, so we can safely read it after we
- // wait for the process.
- eh.stderr.Close()
- if stderr != nil {
- stderrFile, err := os.Open(logFile)
- if err != nil {
- vlog.VI(1).Infof("failed to open %q: %s\n", logFile, err)
- return procErr
- }
- readTo(stderrFile, stderr)
- stderrFile.Close()
+ var procErr error
+ select {
+ case procErr = <-eh.procErrCh:
+ // The child has exited already.
+ case <-time.After(eh.sh.waitTimeout):
+ // Time out waiting for child to exit.
+ procErr = vexec.ErrTimeout
+ // Force close stdout to unblock any readers of stdout
+ // (including the drain loop started above).
+ eh.stdout.Close()
}
+ <-waitStdout
+
+ // Transcribe stderr.
+ outputFromFile(eh.stderr, stderr)
+
return procErr
}
diff --git a/lib/modules/func.go b/lib/modules/func.go
index 54ce341..8fd0523 100644
--- a/lib/modules/func.go
+++ b/lib/modules/func.go
@@ -65,7 +65,7 @@
return nil, err
}
}
- stderr, err := newLogfile(args[0])
+ stderr, err := newLogfile("stderr", args[0])
if err != nil {
return nil, err
}
@@ -111,7 +111,9 @@
fh.mu.Unlock()
// Read stdout until EOF to ensure that we read all of it.
- readTo(stdout, stdout_w)
+ if stdout_w != nil {
+ io.Copy(stdout_w, stdout)
+ }
fh.wg.Wait()
fh.mu.Lock()
@@ -120,12 +122,13 @@
// Safe to close stderr now.
stderr.Close()
- stderr, err := os.Open(stderr.Name())
- if err == nil {
- readTo(stderr, stderr_w)
- stderr.Close()
- } else {
- fmt.Fprintf(os.Stderr, "failed to open %q: %s\n", stderr.Name(), err)
+ if stderr_w != nil {
+ if stderr, err := os.Open(stderr.Name()); err == nil {
+ io.Copy(stderr_w, stderr)
+ stderr.Close()
+ } else {
+ fmt.Fprintf(os.Stderr, "failed to open %q: %s\n", stderr.Name(), err)
+ }
}
fh.mu.Lock()
diff --git a/lib/modules/modules_internal_test.go b/lib/modules/modules_internal_test.go
index d0112e6..b8229e3 100644
--- a/lib/modules/modules_internal_test.go
+++ b/lib/modules/modules_internal_test.go
@@ -13,7 +13,7 @@
return fmt.Errorf("no args")
}
for _, a := range args {
- fmt.Println(a)
+ fmt.Fprintln(stdout, a)
}
return nil
}
@@ -26,7 +26,10 @@
}
func TestState(t *testing.T) {
- sh := NewShell()
+ sh, err := NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
RegisterFunction("echoff", "[args]*", Echo)
assertNumHandles(t, sh, 0)
diff --git a/lib/modules/modules_test.go b/lib/modules/modules_test.go
index 0852a00..cee9708 100644
--- a/lib/modules/modules_test.go
+++ b/lib/modules/modules_test.go
@@ -3,31 +3,46 @@
import (
"bufio"
"bytes"
+ "errors"
"fmt"
"io"
"os"
"reflect"
"sort"
"strings"
+ "syscall"
"testing"
"time"
"veyron.io/veyron/veyron/lib/exec"
+ "veyron.io/veyron/veyron/lib/flags/consts"
"veyron.io/veyron/veyron/lib/modules"
"veyron.io/veyron/veyron/lib/testutil"
+ tsecurity "veyron.io/veyron/veyron/lib/testutil/security"
+ _ "veyron.io/veyron/veyron/profiles"
+ vsecurity "veyron.io/veyron/veyron/security"
+
+ "veyron.io/veyron/veyron2/security"
)
+const credentialsEnvPrefix = "\"" + consts.VeyronCredentials + "="
+
func init() {
testutil.Init()
modules.RegisterChild("envtest", "envtest: <variables to print>...", PrintFromEnv)
modules.RegisterChild("printenv", "printenv", PrintEnv)
modules.RegisterChild("echos", "[args]*", Echo)
modules.RegisterChild("errortestChild", "", ErrorMain)
+ modules.RegisterChild("ignores_stdin", "", ignoresStdin)
modules.RegisterFunction("envtestf", "envtest: <variables to print>...", PrintFromEnv)
modules.RegisterFunction("echof", "[args]*", Echo)
modules.RegisterFunction("errortestFunc", "", ErrorMain)
+}
+func ignoresStdin(io.Reader, io.Writer, io.Writer, map[string]string, ...string) error {
+ <-time.After(time.Minute)
+ return nil
}
func Echo(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
@@ -118,7 +133,10 @@
}
func TestChild(t *testing.T) {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(nil, nil)
key, val := "simpleVar", "foo & bar"
sh.SetVar(key, val)
@@ -126,19 +144,25 @@
}
func TestChildNoRegistration(t *testing.T) {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(os.Stderr, os.Stderr)
key, val := "simpleVar", "foo & bar"
sh.SetVar(key, val)
testCommand(t, sh, "envtest", key, val)
- _, err := sh.Start("non-existent-command", nil, "random", "args")
+ _, err = sh.Start("non-existent-command", nil, "random", "args")
if err == nil {
t.Fatalf("expected error")
}
}
func TestFunction(t *testing.T) {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(nil, nil)
key, val := "simpleVar", "foo & bar & baz"
sh.SetVar(key, val)
@@ -146,7 +170,10 @@
}
func TestErrorChild(t *testing.T) {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(nil, nil)
h, err := sh.Start("errortestChild", nil)
if err != nil {
@@ -183,15 +210,86 @@
}
func TestShutdownSubprocess(t *testing.T) {
- testShutdown(t, modules.NewShell(), "echos", false)
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ testShutdown(t, sh, "echos", false)
+}
+
+// TestShutdownSubprocessIgnoresStdin verifies that Shutdown doesn't wait
+// forever if a child does not die upon closing stdin; but instead times out and
+// returns an appropriate error.
+func TestShutdownSubprocessIgnoresStdin(t *testing.T) {
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ sh.SetWaitTimeout(time.Second)
+ h, err := sh.Start("ignores_stdin", nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ var stdoutBuf, stderrBuf bytes.Buffer
+ if err := sh.Cleanup(&stdoutBuf, &stderrBuf); err == nil || err.Error() != exec.ErrTimeout.Error() {
+ t.Errorf("unexpected error in Cleanup: got %v, want %v", err, exec.ErrTimeout)
+ }
+ if err := syscall.Kill(h.Pid(), syscall.SIGINT); err != nil {
+ t.Errorf("Kill failed: %v", err)
+ }
+}
+
+// TestStdoutRace exemplifies a potential race between reading from child's
+// stdout and closing stdout in Wait (called by Shutdown).
+//
+// NOTE: triggering the actual --race failure is hard, even if the
+// implementation inappropriately sets stdout to the file that is to be closed
+// in Wait.
+func TestStdoutRace(t *testing.T) {
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ sh.SetWaitTimeout(time.Second)
+ h, err := sh.Start("ignores_stdin", nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ ch := make(chan error, 1)
+ go func() {
+ buf := make([]byte, 5)
+ // This will block since the child is not writing anything on
+ // stdout.
+ _, err := h.Stdout().Read(buf)
+ ch <- err
+ }()
+ // Give the goroutine above a chance to run, so that we're blocked on
+ // stdout.Read.
+ <-time.After(time.Second)
+ // Cleanup should close stdout, and unblock the goroutine.
+ sh.Cleanup(nil, nil)
+ if got, want := <-ch, io.EOF; got != want {
+ t.Errorf("Expected %v, got %v instead", want, got)
+ }
+
+ if err := syscall.Kill(h.Pid(), syscall.SIGINT); err != nil {
+ t.Errorf("Kill failed: %v", err)
+ }
}
func TestShutdownFunction(t *testing.T) {
- testShutdown(t, modules.NewShell(), "echof", true)
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ testShutdown(t, sh, "echof", true)
}
func TestErrorFunc(t *testing.T) {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(nil, nil)
h, err := sh.Start("errortestFunc", nil)
if err != nil {
@@ -212,7 +310,10 @@
}
func TestEnvelope(t *testing.T) {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(nil, nil)
sh.SetVar("a", "1")
sh.SetVar("b", "2")
@@ -244,29 +345,154 @@
}
}
+ foundVeyronCredentials := false
for _, want := range shEnv {
+ if strings.HasPrefix(want, credentialsEnvPrefix) {
+ foundVeyronCredentials = true
+ continue
+ }
if !find(want, childEnv) {
t.Errorf("failed to find %s in %#v", want, childEnv)
}
}
+ if !foundVeyronCredentials {
+ t.Errorf("%v environment variable not set in command envelope", consts.VeyronCredentials)
+ }
+
+ foundVeyronCredentials = false
for _, want := range childEnv {
if want == "\""+exec.VersionVariable+"=\"" {
continue
}
+ if strings.HasPrefix(want, credentialsEnvPrefix) {
+ foundVeyronCredentials = true
+ continue
+ }
if !find(want, shEnv) {
t.Errorf("failed to find %s in %#v", want, shEnv)
}
}
+ if !foundVeyronCredentials {
+ t.Errorf("%v environment variable not set for command", consts.VeyronCredentials)
+ }
+}
+
+func TestEnvCredentials(t *testing.T) {
+ startChildAndGetCredentials := func(sh *modules.Shell, env []string) string {
+ h, err := sh.Start("printenv", env)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ defer h.Shutdown(os.Stderr, os.Stderr)
+ scanner := bufio.NewScanner(h.Stdout())
+ for scanner.Scan() {
+ o := scanner.Text()
+ if strings.HasPrefix(o, credentialsEnvPrefix) {
+ return strings.TrimSuffix(strings.TrimPrefix(o, credentialsEnvPrefix), "\"")
+ }
+ }
+ return ""
+ }
+ validateCredentials := func(dir string, wantBlessing string) error {
+ if len(dir) == 0 {
+ return errors.New("credentials directory not found")
+ }
+ p, err := vsecurity.LoadPersistentPrincipal(dir, nil)
+ if err != nil {
+ return err
+ }
+ def := p.BlessingStore().Default()
+ if blessingsSharedWithAll := p.BlessingStore().ForPeer("random_peer"); !reflect.DeepEqual(blessingsSharedWithAll, def) {
+ return fmt.Errorf("Blessing shared with all peers: %v is different from default Blessings: %v", blessingsSharedWithAll, def)
+ }
+ if got := def.ForContext(security.NewContext(&security.ContextParams{LocalPrincipal: p})); len(got) != 1 || got[0] != wantBlessing {
+ return fmt.Errorf("got blessings: %v, want exactly one blessing: %v", got, wantBlessing)
+ }
+ return nil
+ }
+
+ // Test child credentials when runtime is not initialized and VeyronCredentials
+ // is not set.
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ defer sh.Cleanup(nil, nil)
+ if err := validateCredentials(startChildAndGetCredentials(sh, nil), "test-shell/child"); err != nil {
+ t.Fatal(err)
+ }
+
+ // Test that no two children have the same credentials directory.
+ if cred1, cred2 := startChildAndGetCredentials(sh, nil), startChildAndGetCredentials(sh, nil); cred1 == cred2 {
+ t.Fatalf("The same credentials directory %v was set for two children", cred1)
+ }
+
+ // Test child credentials when VeyronCredentials are set.
+ root := tsecurity.NewPrincipal("root")
+ old := os.Getenv(consts.VeyronCredentials)
+ defer os.Setenv(consts.VeyronCredentials, old)
+ dir := tsecurity.NewVeyronCredentials(root, "os")
+ defer os.RemoveAll(dir)
+ if err := os.Setenv(consts.VeyronCredentials, dir); err != nil {
+ t.Fatal(err)
+ }
+ sh, err = modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ if err := validateCredentials(startChildAndGetCredentials(sh, nil), "root/os/child"); err != nil {
+ t.Fatal(err)
+ }
+ sh.Cleanup(nil, nil)
+
+ // Test that os VeyronCredentials are not deleted by the Cleanup.
+ if finfo, err := os.Stat(dir); err != nil || !finfo.IsDir() {
+ t.Fatalf("%q is not a directory", dir)
+ }
+
+ // Test that VeyronCredentials specified on the shell override the OS ones.
+ dir = tsecurity.NewVeyronCredentials(root, "shell")
+ defer os.RemoveAll(dir)
+ sh, err = modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ sh.SetVar(consts.VeyronCredentials, dir)
+ if err := validateCredentials(startChildAndGetCredentials(sh, nil), "root/shell/child"); err != nil {
+ t.Fatal(err)
+ }
+ sh.ClearVar(consts.VeyronCredentials)
+ if credentials := startChildAndGetCredentials(sh, nil); len(credentials) != 0 {
+ t.Fatalf("found credentials %v set for child even when shell credentials were cleared", credentials)
+ }
+ anotherDir := tsecurity.NewVeyronCredentials(root, "anotherShell")
+ defer os.RemoveAll(anotherDir)
+ sh.SetVar(consts.VeyronCredentials, anotherDir)
+ if err := validateCredentials(startChildAndGetCredentials(sh, nil), "root/anotherShell/child"); err != nil {
+ t.Fatal(err)
+ }
+
+ // Test that VeyronCredentials specified as a parameter overrides the OS and
+ // shell ones.
+ dir = tsecurity.NewVeyronCredentials(root, "param")
+ defer os.RemoveAll(dir)
+ env := []string{consts.VeyronCredentials + "=" + dir}
+ if err := validateCredentials(startChildAndGetCredentials(sh, env), "root/param"); err != nil {
+ t.Fatal(err)
+ }
+
}
func TestEnvMerge(t *testing.T) {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(nil, nil)
sh.SetVar("a", "1")
os.Setenv("a", "wrong, should be 1")
sh.SetVar("b", "2 also wrong")
os.Setenv("b", "wrong, should be 2")
-
h, err := sh.Start("printenv", []string{"b=2"})
if err != nil {
t.Fatalf("unexpected error: %s", err)
diff --git a/lib/modules/only_for_test.go b/lib/modules/only_for_test.go
new file mode 100644
index 0000000..8f3143a
--- /dev/null
+++ b/lib/modules/only_for_test.go
@@ -0,0 +1,8 @@
+package modules
+
+import "io"
+
+// NewRW exposes newRW for unit tests.
+func NewRW() io.ReadWriteCloser {
+ return newRW()
+}
diff --git a/lib/modules/queue_rw.go b/lib/modules/queue_rw.go
new file mode 100644
index 0000000..b4c83e9
--- /dev/null
+++ b/lib/modules/queue_rw.go
@@ -0,0 +1,58 @@
+package modules
+
+import (
+ "io"
+ "sync"
+
+ // TODO(caprita): Move upcqueue into veyron/lib.
+ "veyron.io/veyron/veyron/runtimes/google/lib/upcqueue"
+)
+
+// queueRW implements a ReadWriteCloser backed by an unbounded in-memory
+// producer-consumer queue.
+type queueRW struct {
+ sync.Mutex
+ q *upcqueue.T
+ buf []byte
+ buffered int
+ cancel chan struct{}
+}
+
+func newRW() io.ReadWriteCloser {
+ return &queueRW{q: upcqueue.New(), cancel: make(chan struct{})}
+}
+
+func (q *queueRW) Close() error {
+ // We use an empty message to signal EOF to the reader.
+ _, err := q.Write([]byte{})
+ return err
+}
+
+func (q *queueRW) Read(p []byte) (n int, err error) {
+ q.Lock()
+ defer q.Unlock()
+ for q.buffered == 0 {
+ elem, err := q.q.Get(q.cancel)
+ if err != nil {
+ return 0, io.EOF
+ }
+ s := elem.(string)
+ b := []byte(s)
+ if len(b) == 0 {
+ close(q.cancel)
+ return 0, io.EOF
+ }
+ q.buf, q.buffered = b, len(b)
+ }
+ copied := copy(p, q.buf[:q.buffered])
+ q.buf = q.buf[copied:]
+ q.buffered -= copied
+ return copied, nil
+}
+
+func (q *queueRW) Write(p []byte) (n int, err error) {
+ if err := q.q.Put(string(p)); err != nil {
+ return 0, err
+ }
+ return len(p), nil
+}
diff --git a/lib/modules/queue_rw_test.go b/lib/modules/queue_rw_test.go
new file mode 100644
index 0000000..735c301
--- /dev/null
+++ b/lib/modules/queue_rw_test.go
@@ -0,0 +1,60 @@
+package modules_test
+
+import (
+ "bytes"
+ "io"
+ "testing"
+
+ "veyron.io/veyron/veyron/lib/modules"
+ "veyron.io/veyron/veyron/lib/testutil"
+)
+
+func init() {
+ if !modules.IsModulesProcess() {
+ testutil.Init()
+ }
+}
+func TestQueueRW(t *testing.T) {
+ q := modules.NewRW()
+ size := testutil.Rand.Intn(1000)
+ data := testutil.RandomBytes(size)
+ begin := 0
+ for {
+ end := begin + testutil.Rand.Intn(100) + 1
+ if end > len(data) {
+ end = len(data)
+ }
+ n, err := q.Write(data[begin:end])
+ if err != nil {
+ t.Fatalf("Write failed: %v", err)
+ }
+ begin = begin + n
+ if begin == len(data) {
+ break
+ }
+ }
+ // This marks EOF.
+ if _, err := q.Write([]byte{}); err != nil {
+ t.Fatalf("err %v", err)
+ }
+ readData := make([]byte, 0, size)
+ for {
+ buf := make([]byte, testutil.Rand.Intn(100)+1)
+ n, err := q.Read(buf)
+ if n > 0 {
+ readData = append(readData, buf[:n]...)
+ }
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ t.Fatalf("Read failed: %v", err)
+ }
+ }
+ if size != len(readData) {
+ t.Fatalf("Mismatching data size: %d != %d", size, len(readData))
+ }
+ if !bytes.Equal(data, readData) {
+ t.Fatalf("Diffing data:\n%v\n%v", data, readData)
+ }
+}
diff --git a/lib/modules/shell.go b/lib/modules/shell.go
index 9e4c3b9..8d4324b 100644
--- a/lib/modules/shell.go
+++ b/lib/modules/shell.go
@@ -29,74 +29,181 @@
// In particular stdin, stdout and stderr are provided as parameters, as is
// a map representation of the shell's environment.
//
-// If a Shell is created within a unit test then it will automatically
-// generate a security ID, write it to a file and set the appropriate
-// environment variable to refer to it.
+// Every Shell created by NewShell is initialized with its VeyronCredentials
+// environment variable set. The variable is set to the os's VeyronCredentials
+// if that is set, otherwise to a freshly created credentials directory. The
+// shell's VeyronCredentials can be set and cleared using the SetVar and
+// ClearVar methods respectively.
+//
+// By default, the VeyronCredentials for each command Start-ed by the shell are
+// set to a freshly created credentials directory that is blessed by the shell's
+// credentials (i.e., if shell's credentials have not been cleared). Thus, each
+// child of the shell gets its own credentials directory with a blessing from the
+// shell of the form
+// <shell's default blessing>/child
+// These default credentials provided by the shell to each command can be
+// overridden by specifying VeyronCredentials in the environment provided as a
+// parameter to the Start method.
package modules
import (
- "flag"
"fmt"
"io"
"io/ioutil"
+ "math/rand"
"os"
"sync"
"time"
+ "veyron.io/veyron/veyron2/security"
+
"veyron.io/veyron/veyron/lib/exec"
"veyron.io/veyron/veyron/lib/flags/consts"
+ vsecurity "veyron.io/veyron/veyron/security"
+)
+
+const (
+ shellBlessingExtension = "test-shell"
+ childBlessingExtension = "child"
)
// Shell represents the context within which commands are run.
type Shell struct {
- mu sync.Mutex
- env map[string]string
- handles map[Handle]struct{}
- credDir string
- startTimeout time.Duration
- config exec.Config
+ mu sync.Mutex
+ env map[string]string
+ handles map[Handle]struct{}
+ // tmpCredDirs are the temporary directories created by this
+ // shell. These must be removed when the shell is cleaned up.
+ tempCredDirs []string
+ startTimeout, waitTimeout time.Duration
+ config exec.Config
+ principal security.Principal
+ blessing security.Blessings
}
-// NewShell creates a new instance of Shell. If this new instance is is a test
-// and no credentials have been configured in the environment via
-// consts.VeyronCredentials then CreateAndUseNewCredentials will be used to
-// configure a new ID for the shell and its children.
-func NewShell() *Shell {
+// NewShell creates a new instance of Shell. It will also add a blessing
+// to the supplied principal and ensure that any child processes are
+// created with principals that are in turn blessed with it. A typical
+// use case is to pass in the runtime's principal and hence allow the
+// process hosting the shell to interact with its children and vice
+// versa. If a nil principal is passed in then NewShell will create a new
+// principal that shares a blessing with all of its children, in this mode,
+// any child processes can interact with each other but not with the parent.
+func NewShell(p security.Principal) (*Shell, error) {
sh := &Shell{
env: make(map[string]string),
handles: make(map[Handle]struct{}),
startTimeout: time.Minute,
+ waitTimeout: 10 * time.Second,
config: exec.NewConfig(),
+ principal: p,
}
- if flag.Lookup("test.run") != nil && os.Getenv(consts.VeyronCredentials) == "" {
- if err := sh.CreateAndUseNewCredentials(); err != nil {
- // TODO(cnicolaou): return an error rather than panic.
- panic(err)
+ if p == nil {
+ if err := sh.initShellCredentials(); err != nil {
+ return nil, err
}
+ return sh, nil
}
- return sh
+ gen := rand.New(rand.NewSource(time.Now().UnixNano()))
+ // Use a unique blessing tree per shell.
+ blessingName := fmt.Sprintf("%s-%d", shellBlessingExtension, gen.Int63())
+ blessing, err := p.BlessSelf(blessingName)
+ if err != nil {
+ return nil, err
+ }
+ sh.blessing = blessing
+ if _, err := p.BlessingStore().Set(blessing, security.BlessingPattern(blessingName+"/"+childBlessingExtension+"/...")); err != nil {
+ return nil, err
+ }
+ if err := p.AddToRoots(blessing); err != nil {
+ return nil, err
+ }
+ // Our blessing store now contains a blessing with our unique prefix
+ // and the principal has that blessing's root added to its trusted
+ // list so that it will accept blessings derived from it.
+ return sh, nil
}
-// CreateAndUseNewCredentials setups a new credentials directory and then
-// configures the shell and all of its children to use to it.
-//
-// TODO(cnicolaou): this should use the principal already setup
-// with the runtime if the runtime has been initialized, if not,
-// it should create a new principal. As of now, this approach only works
-// for child processes that talk to each other, but not to the parent
-// process that started them since it's running with a different set of
-// credentials setup elsewhere. When this change is made it should
-// be possible to remove creating credentials in many unit tests.
-func (sh *Shell) CreateAndUseNewCredentials() error {
- dir, err := ioutil.TempDir("", "veyron_credentials")
+func (sh *Shell) initShellCredentials() error {
+ sh.mu.Lock()
+ defer sh.mu.Unlock()
+ if dir := os.Getenv(consts.VeyronCredentials); len(dir) != 0 {
+ sh.env[consts.VeyronCredentials] = dir
+ return nil
+ }
+
+ dir, err := ioutil.TempDir("", "shell_credentials")
if err != nil {
return err
}
- sh.credDir = dir
- sh.SetVar(consts.VeyronCredentials, sh.credDir)
+ sh.env[consts.VeyronCredentials] = dir
+ sh.tempCredDirs = append(sh.tempCredDirs, dir)
return nil
}
+func (sh *Shell) getChildCredentials(shellCredDir string) (string, error) {
+ root := sh.principal
+ rootBlessing := sh.blessing
+ if root == nil {
+ r, err := principalFromDir(shellCredDir)
+ if err != nil {
+ return "", err
+ }
+ root = r
+ rootBlessing = root.BlessingStore().Default()
+ }
+
+ dir, err := ioutil.TempDir("", "shell_child_credentials")
+ if err != nil {
+ return "", err
+ }
+ // Create a principal and default blessing for the child that is
+ // derived from the blessing created for this shell. This can
+ // be used by the parent to invoke RPCs on any children and for the
+ // children to invoke RPCs on each other.
+ p, err := vsecurity.CreatePersistentPrincipal(dir, nil)
+ if err != nil {
+ return "", err
+ }
+ blessingForChild, err := root.Bless(p.PublicKey(), rootBlessing, childBlessingExtension, security.UnconstrainedUse())
+ if err != nil {
+ return "", err
+ }
+ if err := p.BlessingStore().SetDefault(blessingForChild); err != nil {
+ return "", err
+ }
+ if _, err := p.BlessingStore().Set(blessingForChild, security.AllPrincipals); err != nil {
+ return "", err
+ }
+ if err := p.AddToRoots(blessingForChild); err != nil {
+ return "", err
+ }
+
+ if sh.blessing != nil {
+ // Create a second blessing for the child, that will be accepted
+ // by the parent, should the child choose to invoke RPCs on the parent.
+ blessingFromChild, err := root.Bless(p.PublicKey(), root.BlessingStore().Default(), childBlessingExtension, security.UnconstrainedUse())
+ if err != nil {
+ return "", err
+ }
+ // We store this blessing as the one to use with a pattern that matches
+ // the root's name.
+ // TODO(cnicolaou,caprita): at some point there will be a nicer API
+ // for getting the name of a blessing.
+ ctx := security.NewContext(&security.ContextParams{LocalPrincipal: root})
+ rootName := root.BlessingStore().Default().ForContext(ctx)[0]
+ if _, err := p.BlessingStore().Set(blessingFromChild, security.BlessingPattern(rootName)); err != nil {
+ return "", err
+ }
+
+ if err := p.AddToRoots(blessingFromChild); err != nil {
+ return "", err
+ }
+ }
+ sh.tempCredDirs = append(sh.tempCredDirs, dir)
+ return dir, nil
+}
+
type Main func(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error
// String returns a string representation of the Shell, which is a
@@ -110,10 +217,20 @@
return registry.help(command)
}
-// Start starts the specified command, it returns a Handle which can be used
-// for interacting with that command. The OS and shell environment variables
-// are merged with the ones supplied as a parameter; the parameter specified
-// ones override the Shell and the Shell ones override the OS ones.
+// Start starts the specified command, it returns a Handle which can be
+// used for interacting with that command.
+//
+// The environment variables for the command are set by merging variables
+// from the OS environment, those in this Shell and those provided as a
+// parameter to it. In general, it prefers values from its parameter over
+// those from the Shell, over those from the OS. However, the VeyronCredentials
+// environment variable is handled specially.
+//
+// If the VeyronCredentials environment variable is set in 'env' then that
+// is the value that gets used. If the shell's VeyronCredentials are set then
+// VeyronCredentials for the command are set to a freshly created directory
+// specifying a principal blessed by the shell's credentials. In all other
+// cases VeyronCredentials for the command remains unset.
//
// The Shell tracks all of the Handles that it creates so that it can shut
// them down when asked to. The returned Handle may be non-nil even when an
@@ -123,7 +240,10 @@
// Commands must have already been registered using RegisterFunction
// or RegisterChild.
func (sh *Shell) Start(name string, env []string, args ...string) (Handle, error) {
- cenv := sh.MergedEnv(env)
+ cenv, err := sh.setupCommandEnv(env)
+ if err != nil {
+ return nil, err
+ }
cmd := registry.getCommand(name)
if cmd == nil {
return nil, fmt.Errorf("%s: not registered", name)
@@ -146,15 +266,28 @@
sh.startTimeout = d
}
+// SetWaitTimeout sets the timeout for waiting on subcommands to complete.
+func (sh *Shell) SetWaitTimeout(d time.Duration) {
+ sh.waitTimeout = d
+}
+
// CommandEnvelope returns the command line and environment that would be
// used for running the subprocess or function if it were started with the
// specifed arguments.
+//
+// This method is not idempotent as the directory pointed to by the
+// VeyronCredentials environment variable may be freshly created with
+// each invocation.
func (sh *Shell) CommandEnvelope(name string, env []string, args ...string) ([]string, []string) {
cmd := registry.getCommand(name)
if cmd == nil {
return []string{}, []string{}
}
- return cmd.factory().envelope(sh, sh.MergedEnv(env), args...)
+ menv, err := sh.setupCommandEnv(env)
+ if err != nil {
+ return []string{}, []string{}
+ }
+ return cmd.factory().envelope(sh, menv, args...)
}
// Forget tells the Shell to stop tracking the supplied Handle. This is
@@ -190,6 +323,11 @@
}
// SetVar sets the value to be associated with key.
+//
+// Note that setting the VeyronCredentials environement
+// variable changes the shell's principal which is used
+// for blessing the principals supplied to the shell's
+// children.
func (sh *Shell) SetVar(key, value string) {
sh.mu.Lock()
defer sh.mu.Unlock()
@@ -198,6 +336,11 @@
}
// ClearVar removes the speficied variable from the Shell's environment
+//
+// Note that clearing the VeyronCredentials environment variable
+// would amount to clearing the shell's principal, and therefore, any
+// children of this shell would have their VeyronCredentials environment
+// variable set only if it is explicitly set in their parameters.
func (sh *Shell) ClearVar(key string) {
sh.mu.Lock()
defer sh.mu.Unlock()
@@ -255,28 +398,49 @@
err = cerr
}
}
- if len(sh.credDir) > 0 {
- os.RemoveAll(sh.credDir)
+
+ // TODO(ataly, ashankar, caprita): The following code may lead to
+ // removing the credential directories for child processes that are
+ // still alive (this can happen e.g. if the Wait on the child timed
+ // out). While we can hope that some error will reach the caller of
+ // Cleanup (because we harvest the error codes from Shutdown), it may
+ // lead to failures that are harder to debug because the original issue
+ // will get masked by the credentials going away. One possibility is
+ // to only remove the credentials dir when Shutdown returns no timeout
+ // error.
+ for _, dir := range sh.tempCredDirs {
+ os.RemoveAll(dir)
}
return err
}
-// MergedEnv returns a slice that contains the merged set of environment
-// variables from the OS environment, those in this Shell and those provided
-// as a parameter to it. It prefers values from its parameter over those
-// from the Shell, over those from the OS.
-func (sh *Shell) MergedEnv(env []string) []string {
+func (sh *Shell) setupCommandEnv(env []string) ([]string, error) {
osmap := envSliceToMap(os.Environ())
evmap := envSliceToMap(env)
+
sh.mu.Lock()
- m1 := mergeMaps(osmap, sh.env)
defer sh.mu.Unlock()
+ m1 := mergeMaps(osmap, sh.env)
+ // Clear any VeyronCredentials directory in m1 as we never
+ // want the child to directly use the directory specified
+ // by the shell's VeyronCredentials.
+ delete(m1, consts.VeyronCredentials)
+
+ // Set the VeyronCredentials environment variable for the child
+ // if it is not already set and the shell's VeyronCredentials are set.
+ if len(evmap[consts.VeyronCredentials]) == 0 && len(sh.env[consts.VeyronCredentials]) != 0 {
+ var err error
+ if evmap[consts.VeyronCredentials], err = sh.getChildCredentials(sh.env[consts.VeyronCredentials]); err != nil {
+ return nil, err
+ }
+ }
+
m2 := mergeMaps(m1, evmap)
r := []string{}
for k, v := range m2 {
r = append(r, k+"="+v)
}
- return r
+ return r, nil
}
// Handle represents a running command.
diff --git a/lib/modules/util.go b/lib/modules/util.go
index c35c74e..b38e767 100644
--- a/lib/modules/util.go
+++ b/lib/modules/util.go
@@ -1,30 +1,42 @@
package modules
import (
- "bufio"
"fmt"
+ "hash/adler32"
"io"
"io/ioutil"
"os"
"strings"
+
+ vsecurity "veyron.io/veyron/veyron/security"
+
+ "veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/vlog"
)
-func newLogfile(prefix string) (*os.File, error) {
- f, err := ioutil.TempFile("", "__modules__"+prefix)
+func newLogfile(prefix, name string) (*os.File, error) {
+ nameHash := adler32.Checksum([]byte(name))
+ f, err := ioutil.TempFile("", fmt.Sprintf("__modules__%s-%x", prefix, nameHash))
if err != nil {
return nil, err
}
return f, nil
}
-func readTo(r io.Reader, w io.Writer) {
- if w == nil {
+func outputFromFile(f *os.File, out io.Writer) {
+ f.Close()
+ fName := f.Name()
+ defer os.Remove(fName)
+ if out == nil {
return
}
- scanner := bufio.NewScanner(r)
- for scanner.Scan() {
- fmt.Fprintf(w, "%s\n", scanner.Text())
+ var err error
+ if f, err = os.Open(fName); err != nil {
+ vlog.VI(1).Infof("failed to open %q: %s\n", fName, err)
+ return
}
+ io.Copy(out, f)
+ f.Close()
}
// envSliceToMap returns a map representation of a string slive
@@ -63,3 +75,21 @@
}
return merged
}
+
+func principalFromDir(dir string) (security.Principal, error) {
+ p, err := vsecurity.LoadPersistentPrincipal(dir, nil)
+ if err == nil {
+ return p, nil
+ }
+ if !os.IsNotExist(err) {
+ return nil, err
+ }
+ p, err = vsecurity.CreatePersistentPrincipal(dir, nil)
+ if err != nil {
+ return nil, err
+ }
+ if err := vsecurity.InitDefaultBlessings(p, shellBlessingExtension); err != nil {
+ return nil, err
+ }
+ return p, nil
+}
diff --git a/lib/signals/signals.go b/lib/signals/signals.go
index e24e1c3..35d2965 100644
--- a/lib/signals/signals.go
+++ b/lib/signals/signals.go
@@ -5,7 +5,7 @@
"os/signal"
"syscall"
- "veyron.io/veyron/veyron2/rt"
+ "veyron.io/veyron/veyron2"
)
type stopSignal string
@@ -31,7 +31,7 @@
// none are specified, the default signals. The first signal received will be
// made available on the returned channel; upon receiving a second signal, the
// process will exit.
-func ShutdownOnSignals(signals ...os.Signal) <-chan os.Signal {
+func ShutdownOnSignals(runtime veyron2.Runtime, signals ...os.Signal) <-chan os.Signal {
if len(signals) == 0 {
signals = defaultSignals()
}
@@ -45,9 +45,9 @@
case STOP:
if !sawStop {
sawStop = true
- if r := rt.R(); r != nil {
+ if runtime != nil {
stopWaiter := make(chan string, 1)
- r.AppCycle().WaitForStop(stopWaiter)
+ runtime.AppCycle().WaitForStop(stopWaiter)
go func() {
for {
ch <- stopSignal(<-stopWaiter)
diff --git a/lib/signals/signals_test.go b/lib/signals/signals_test.go
index d57c65c..4220712 100644
--- a/lib/signals/signals_test.go
+++ b/lib/signals/signals_test.go
@@ -41,7 +41,7 @@
modules.RegisterChild("handleDefaultsIgnoreChan", "", handleDefaultsIgnoreChan)
}
-func stopLoop(stdin io.Reader, ch chan<- struct{}) {
+func stopLoop(runtime veyron2.Runtime, stdin io.Reader, ch chan<- struct{}) {
scanner := bufio.NewScanner(stdin)
for scanner.Scan() {
switch scanner.Text() {
@@ -49,19 +49,23 @@
close(ch)
return
case "stop":
- rt.R().AppCycle().Stop()
+ runtime.AppCycle().Stop()
}
}
}
func program(stdin io.Reader, stdout io.Writer, signals ...os.Signal) {
- r := rt.Init()
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+
closeStopLoop := make(chan struct{})
- go stopLoop(stdin, closeStopLoop)
- wait := ShutdownOnSignals(signals...)
+ go stopLoop(runtime, stdin, closeStopLoop)
+ wait := ShutdownOnSignals(runtime, signals...)
fmt.Fprintf(stdout, "ready\n")
fmt.Fprintf(stdout, "received signal %s\n", <-wait)
- r.Cleanup()
+ runtime.Cleanup()
<-closeStopLoop
}
@@ -81,10 +85,14 @@
}
func handleDefaultsIgnoreChan(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
- defer rt.Init().Cleanup()
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
closeStopLoop := make(chan struct{})
- go stopLoop(stdin, closeStopLoop)
- ShutdownOnSignals()
+ go stopLoop(runtime, stdin, closeStopLoop)
+ ShutdownOnSignals(runtime)
fmt.Fprintf(stdout, "ready\n")
<-closeStopLoop
return nil
@@ -112,7 +120,10 @@
}
func newShell(t *testing.T, command string) (*modules.Shell, modules.Handle, *expect.Session) {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
handle, err := sh.Start(command, nil)
if err != nil {
sh.Cleanup(os.Stderr, os.Stderr)
@@ -284,7 +295,7 @@
// the input list of signals.
func TestParseSignalsList(t *testing.T) {
list := []os.Signal{STOP, syscall.SIGTERM}
- ShutdownOnSignals(list...)
+ ShutdownOnSignals(nil, list...)
if !isSignalInSet(syscall.SIGTERM, list) {
t.Errorf("signal %s not in signal set, as expected: %v", syscall.SIGTERM, list)
}
@@ -306,8 +317,8 @@
}
-func createConfigServer(t *testing.T) (ipc.Server, string, <-chan string) {
- server, err := rt.R().NewServer()
+func createConfigServer(t *testing.T, runtime veyron2.Runtime) (ipc.Server, string, <-chan string) {
+ server, err := runtime.NewServer()
if err != nil {
t.Fatalf("Got error: %v", err)
}
@@ -325,17 +336,23 @@
// TestCleanRemoteShutdown verifies that remote shutdown works correctly.
func TestCleanRemoteShutdown(t *testing.T) {
- r := rt.Init()
- defer r.Cleanup()
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(os.Stderr, os.Stderr)
// Set the child process up with a blessing from the parent so that
// the default authorization works for RPCs between the two.
- childcreds := security.NewVeyronCredentials(r.Principal(), "child")
+ childcreds := security.NewVeyronCredentials(runtime.Principal(), "child")
defer os.RemoveAll(childcreds)
- configServer, configServiceName, ch := createConfigServer(t)
+ configServer, configServiceName, ch := createConfigServer(t, runtime)
defer configServer.Stop()
sh.SetVar(consts.VeyronCredentials, childcreds)
sh.SetConfigKey(mgmt.ParentNameConfigKey, configServiceName)
@@ -349,7 +366,7 @@
appCycleName := <-ch
s.Expect("ready")
appCycle := appcycle.AppCycleClient(appCycleName)
- stream, err := appCycle.Stop(r.NewContext())
+ stream, err := appCycle.Stop(runtime.NewContext())
if err != nil {
t.Fatalf("Got error: %v", err)
}
diff --git a/lib/stats/counter/counter.go b/lib/stats/counter/counter.go
index 4aa9db3..0dc4ec9 100644
--- a/lib/stats/counter/counter.go
+++ b/lib/stats/counter/counter.go
@@ -20,7 +20,7 @@
var (
// Used for testing.
- Now func() time.Time = time.Now
+ TimeNow func() time.Time = time.Now
)
const (
@@ -39,7 +39,7 @@
// New returns a new Counter.
func New() *Counter {
- now := Now()
+ now := TimeNow()
c := &Counter{}
c.ts[hour] = newTimeSeries(now, time.Hour, time.Minute)
c.ts[tenminutes] = newTimeSeries(now, 10*time.Minute, 10*time.Second)
@@ -48,7 +48,7 @@
}
func (c *Counter) advance() time.Time {
- now := Now()
+ now := TimeNow()
for _, ts := range c.ts {
ts.advanceTime(now)
}
@@ -141,7 +141,7 @@
func (c *Counter) Reset() {
c.mu.Lock()
defer c.mu.Unlock()
- now := Now()
+ now := TimeNow()
for _, ts := range c.ts {
ts.reset(now)
}
diff --git a/lib/stats/counter/counter_test.go b/lib/stats/counter/counter_test.go
index 03a3951..fc943b3 100644
--- a/lib/stats/counter/counter_test.go
+++ b/lib/stats/counter/counter_test.go
@@ -10,7 +10,7 @@
func TestCounter(t *testing.T) {
now := time.Unix(1, 0)
- counter.Now = func() time.Time { return now }
+ counter.TimeNow = func() time.Time { return now }
c := counter.New()
// Time 1, value=1
@@ -107,7 +107,7 @@
func TestCounterRate(t *testing.T) {
now := time.Unix(1, 0)
- counter.Now = func() time.Time { return now }
+ counter.TimeNow = func() time.Time { return now }
c := counter.New()
// No data, rate is 0.
diff --git a/lib/stats/counter/timeseries.go b/lib/stats/counter/timeseries.go
index 06b5981..3573d57 100644
--- a/lib/stats/counter/timeseries.go
+++ b/lib/stats/counter/timeseries.go
@@ -29,10 +29,10 @@
}
}
-// advanceTime moves the timeseries forward to time t and fills in any slots
-// that get skipped in the process. Values older than the timeseries period are
-// lost.
-func (ts *timeseries) advanceTime(t time.Time) {
+// advanceTimeWithFill moves the timeseries forward to time t and fills in any
+// slots that get skipped in the process with the given value. Values older than
+// the timeseries period are lost.
+func (ts *timeseries) advanceTimeWithFill(t time.Time, value int64) {
advanceTo := t.Truncate(ts.resolution)
if !advanceTo.After(ts.time) {
// This is shortcut for the most common case of a busy counter
@@ -45,7 +45,6 @@
if steps > ts.size {
steps = ts.size
}
- value := ts.slots[ts.head]
for steps > 0 {
ts.head = (ts.head + 1) % ts.size
ts.slots[ts.head] = value
@@ -54,6 +53,13 @@
ts.time = advanceTo
}
+// advanceTime moves the timeseries forward to time t and fills in any slots
+// that get skipped in the process with the head value. Values older than the
+// timeseries period are lost.
+func (ts *timeseries) advanceTime(t time.Time) {
+ ts.advanceTimeWithFill(t, ts.slots[ts.head])
+}
+
// set sets the current value of the timeseries.
func (ts *timeseries) set(value int64) {
ts.slots[ts.head] = value
@@ -113,9 +119,10 @@
if ts.stepCount < int64(ts.size) {
to = ts.head + 1
}
+ tail := (ts.head + 1) % ts.size
min := int64(math.MaxInt64)
for b := 0; b < to; b++ {
- if ts.slots[b] < min {
+ if b != tail && ts.slots[b] < min {
min = ts.slots[b]
}
}
@@ -128,9 +135,10 @@
if ts.stepCount < int64(ts.size) {
to = ts.head + 1
}
+ tail := (ts.head + 1) % ts.size
max := int64(math.MinInt64)
for b := 0; b < to; b++ {
- if ts.slots[b] > max {
+ if b != tail && ts.slots[b] > max {
max = ts.slots[b]
}
}
diff --git a/lib/stats/counter/timeseries_test.go b/lib/stats/counter/timeseries_test.go
index a05191e..9a3700d 100644
--- a/lib/stats/counter/timeseries_test.go
+++ b/lib/stats/counter/timeseries_test.go
@@ -69,6 +69,28 @@
if expected, got := int64(345), ts.tailValue(); got != expected {
t.Errorf("unexpected value. Got %v, want %v", got, expected)
}
+ if expected, got := int64(111), ts.min(); got != expected {
+ t.Errorf("unexpected value. Got %v, want %v", got, expected)
+ }
+ if expected, got := int64(345), ts.max(); got != expected {
+ t.Errorf("unexpected value. Got %v, want %v", got, expected)
+ }
+
+ // Time 8
+ now = now.Add(time.Second)
+ ts.advanceTime(now)
+ if expected, got := int64(111), ts.headValue(); got != expected {
+ t.Errorf("unexpected value. Got %v, want %v", got, expected)
+ }
+ if expected, got := int64(345), ts.tailValue(); got != expected {
+ t.Errorf("unexpected value. Got %v, want %v", got, expected)
+ }
+ if expected, got := int64(111), ts.min(); got != expected {
+ t.Errorf("unexpected value. Got %v, want %v", got, expected)
+ }
+ if expected, got := int64(111), ts.max(); got != expected {
+ t.Errorf("unexpected value. Got %v, want %v", got, expected)
+ }
// Time 27
now = now.Add(20 * time.Second)
diff --git a/lib/stats/counter/tracker.go b/lib/stats/counter/tracker.go
new file mode 100644
index 0000000..30cc114
--- /dev/null
+++ b/lib/stats/counter/tracker.go
@@ -0,0 +1,159 @@
+package counter
+
+import (
+ "math"
+ "sync"
+ "time"
+)
+
+// Tracker is a min/max value tracker that keeps track of its min/max values
+// over a given period of time, and with a given resolution. The initial min
+// and max values are math.MaxInt64 and math.MinInt64 respectively.
+type Tracker struct {
+ mu sync.RWMutex
+ min, max int64 // All time min/max.
+ minTS, maxTS [3]*timeseries
+ lastUpdate time.Time
+}
+
+// NewTracker returns a new Tracker.
+func NewTracker() *Tracker {
+ now := TimeNow()
+ t := &Tracker{}
+ t.minTS[hour] = newTimeSeries(now, time.Hour, time.Minute)
+ t.minTS[tenminutes] = newTimeSeries(now, 10*time.Minute, 10*time.Second)
+ t.minTS[minute] = newTimeSeries(now, time.Minute, time.Second)
+ t.maxTS[hour] = newTimeSeries(now, time.Hour, time.Minute)
+ t.maxTS[tenminutes] = newTimeSeries(now, 10*time.Minute, 10*time.Second)
+ t.maxTS[minute] = newTimeSeries(now, time.Minute, time.Second)
+ t.init()
+ return t
+}
+
+func (t *Tracker) init() {
+ t.min = math.MaxInt64
+ t.max = math.MinInt64
+ for _, ts := range t.minTS {
+ ts.set(math.MaxInt64)
+ }
+ for _, ts := range t.maxTS {
+ ts.set(math.MinInt64)
+ }
+}
+
+func (t *Tracker) advance() time.Time {
+ now := TimeNow()
+ for _, ts := range t.minTS {
+ ts.advanceTimeWithFill(now, math.MaxInt64)
+ }
+ for _, ts := range t.maxTS {
+ ts.advanceTimeWithFill(now, math.MinInt64)
+ }
+ return now
+}
+
+// LastUpdate returns the last update time of the range.
+func (t *Tracker) LastUpdate() time.Time {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ return t.lastUpdate
+}
+
+// Push adds a new value if it is a new minimum or maximum.
+func (t *Tracker) Push(value int64) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.lastUpdate = t.advance()
+ if t.min > value {
+ t.min = value
+ }
+ if t.max < value {
+ t.max = value
+ }
+ for _, ts := range t.minTS {
+ if ts.headValue() > value {
+ ts.set(value)
+ }
+ }
+ for _, ts := range t.maxTS {
+ if ts.headValue() < value {
+ ts.set(value)
+ }
+ }
+}
+
+// Min returns the minimum value of the tracker
+func (t *Tracker) Min() int64 {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ return t.min
+}
+
+// Max returns the maximum value of the tracker.
+func (t *Tracker) Max() int64 {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ return t.max
+}
+
+// Min1h returns the minimum value for the last hour.
+func (t *Tracker) Min1h() int64 {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.advance()
+ return t.minTS[hour].min()
+}
+
+// Max1h returns the maximum value for the last hour.
+func (t *Tracker) Max1h() int64 {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.advance()
+ return t.maxTS[hour].max()
+}
+
+// Min10m returns the minimum value for the last 10 minutes.
+func (t *Tracker) Min10m() int64 {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.advance()
+ return t.minTS[tenminutes].min()
+}
+
+// Max10m returns the maximum value for the last 10 minutes.
+func (t *Tracker) Max10m() int64 {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.advance()
+ return t.maxTS[tenminutes].max()
+}
+
+// Min1m returns the minimum value for the last 1 minute.
+func (t *Tracker) Min1m() int64 {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.advance()
+ return t.minTS[minute].min()
+}
+
+// Max1m returns the maximum value for the last 1 minute.
+func (t *Tracker) Max1m() int64 {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.advance()
+ return t.maxTS[minute].max()
+}
+
+// Reset resets the range to an empty state.
+func (t *Tracker) Reset() {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ now := TimeNow()
+ for _, ts := range t.minTS {
+ ts.reset(now)
+ }
+ for _, ts := range t.maxTS {
+ ts.reset(now)
+ }
+ t.init()
+}
diff --git a/lib/stats/counter/tracker_test.go b/lib/stats/counter/tracker_test.go
new file mode 100644
index 0000000..c2e152c
--- /dev/null
+++ b/lib/stats/counter/tracker_test.go
@@ -0,0 +1,210 @@
+package counter_test
+
+import (
+ "fmt"
+ "math"
+ "math/rand"
+ "sync"
+ "testing"
+ "time"
+
+ "veyron.io/veyron/veyron/lib/stats/counter"
+)
+
+var trackerTests = []struct {
+ after time.Duration
+ push int64 // 0 = none, -1 = reset
+ mins, maxs []int64 // min, 10min, hour, all time
+}{
+ { // T0
+ after: 0,
+ push: 0,
+ mins: []int64{math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64},
+ maxs: []int64{math.MinInt64, math.MinInt64, math.MinInt64, math.MinInt64},
+ },
+ { // T1
+ after: 1 * time.Second,
+ push: 5,
+ mins: []int64{5, 5, 5, 5},
+ maxs: []int64{5, 5, 5, 5},
+ },
+ { // T2
+ after: 1 * time.Second,
+ push: 10,
+ mins: []int64{5, 5, 5, 5},
+ maxs: []int64{10, 10, 10, 10},
+ },
+ { // T3
+ after: 1 * time.Second,
+ push: 1,
+ mins: []int64{1, 1, 1, 1},
+ maxs: []int64{10, 10, 10, 10},
+ },
+ { // T4
+ after: 1 * time.Minute,
+ push: 0,
+ mins: []int64{math.MaxInt64, 1, 1, 1},
+ maxs: []int64{math.MinInt64, 10, 10, 10},
+ },
+ { // T5
+ after: 10 * time.Minute,
+ push: 0,
+ mins: []int64{math.MaxInt64, math.MaxInt64, 1, 1},
+ maxs: []int64{math.MinInt64, math.MinInt64, 10, 10},
+ },
+ { // T6
+ after: 1 * time.Hour,
+ push: 0,
+ mins: []int64{math.MaxInt64, math.MaxInt64, math.MaxInt64, 1},
+ maxs: []int64{math.MinInt64, math.MinInt64, math.MinInt64, 10},
+ },
+ { // T7
+ after: 1 * time.Second,
+ push: 5,
+ mins: []int64{5, 5, 5, 1},
+ maxs: []int64{5, 5, 5, 10},
+ },
+ { // T8
+ after: 1 * time.Minute,
+ push: 20,
+ mins: []int64{20, 5, 5, 1},
+ maxs: []int64{20, 20, 20, 20},
+ },
+ { // T9
+ after: 10 * time.Minute,
+ push: 15,
+ mins: []int64{15, 15, 5, 1},
+ maxs: []int64{15, 15, 20, 20},
+ },
+ { // T10
+ after: 1 * time.Hour,
+ push: 10,
+ mins: []int64{10, 10, 10, 1},
+ maxs: []int64{10, 10, 10, 20},
+ },
+ { // T11
+ after: 1 * time.Second,
+ push: -1,
+ mins: []int64{math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64},
+ maxs: []int64{math.MinInt64, math.MinInt64, math.MinInt64, math.MinInt64},
+ },
+ { // T12
+ after: 1 * time.Second,
+ push: 5,
+ mins: []int64{5, 5, 5, 5},
+ maxs: []int64{5, 5, 5, 5},
+ },
+}
+
+func TestTracker(t *testing.T) {
+ now := time.Unix(1, 0)
+ counter.TimeNow = func() time.Time { return now }
+
+ tracker := counter.NewTracker()
+ for i, tt := range trackerTests {
+ now = now.Add(tt.after)
+ name := fmt.Sprintf("[T%d] %s:", i, now.Format("15:04:05"))
+ if tt.push > 0 {
+ tracker.Push(tt.push)
+ t.Logf("%s pushed %d\n", name, tt.push)
+ } else if tt.push < 0 {
+ tracker.Reset()
+ t.Log(name, "reset")
+ } else {
+ t.Log(name, "none")
+ }
+
+ if expected, got := tt.mins[0], tracker.Min1m(); got != expected {
+ t.Errorf("%s Min1m returned %d, want %v", name, got, expected)
+ }
+ if expected, got := tt.maxs[0], tracker.Max1m(); got != expected {
+ t.Errorf("%s Max1m returned %d, want %v", name, got, expected)
+ }
+ if expected, got := tt.mins[1], tracker.Min10m(); got != expected {
+ t.Errorf("%s Min10m returned %d, want %v", name, got, expected)
+ }
+ if expected, got := tt.maxs[1], tracker.Max10m(); got != expected {
+ t.Errorf("%s Max10m returned %d, want %v", name, got, expected)
+ }
+ if expected, got := tt.mins[2], tracker.Min1h(); got != expected {
+ t.Errorf("%s Min1h returned %d, want %v", name, got, expected)
+ }
+ if expected, got := tt.maxs[2], tracker.Max1h(); got != expected {
+ t.Errorf("%s Max1h returned %d, want %v", name, got, expected)
+ }
+ if expected, got := tt.mins[3], tracker.Min(); got != expected {
+ t.Errorf("%s Min returned %d, want %v", name, got, expected)
+ }
+ if expected, got := tt.maxs[3], tracker.Max(); got != expected {
+ t.Errorf("%s Max returned %d, want %v", name, got, expected)
+ }
+ }
+}
+
+func min(a, b int64) int64 {
+ if a > b {
+ return b
+ }
+ return a
+}
+
+func max(a, b int64) int64 {
+ if a < b {
+ return b
+ }
+ return a
+}
+
+func TestTrackerConcurrent(t *testing.T) {
+ rand.Seed(time.Now().UnixNano())
+
+ const numGoRoutines = 100
+ const numPushPerGoRoutine = 100000
+ tracker := counter.NewTracker()
+
+ var mins, maxs [numGoRoutines]int64
+ var wg sync.WaitGroup
+ wg.Add(numGoRoutines)
+ for i := 0; i < numGoRoutines; i++ {
+ go func(i int) {
+ var localMin, localMax int64 = math.MaxInt64, math.MinInt64
+ for j := 0; j < numPushPerGoRoutine; j++ {
+ v := rand.Int63()
+ tracker.Push(v)
+ localMin, localMax = min(localMin, v), max(localMax, v)
+ }
+
+ mins[i], maxs[i] = localMin, localMax
+ wg.Done()
+ }(i)
+ }
+ wg.Wait()
+
+ var expectedMin, expectedMax int64 = math.MaxInt64, math.MinInt64
+ for _, v := range mins {
+ expectedMin = min(expectedMin, v)
+ }
+ for _, v := range maxs {
+ expectedMax = max(expectedMax, v)
+ }
+
+ if got := tracker.Min(); got != expectedMin {
+ t.Errorf("Min returned %d, want %v", got, expectedMin)
+ }
+ if got := tracker.Max(); got != expectedMax {
+ t.Errorf("Max returned %d, want %v", got, expectedMax)
+ }
+}
+
+func BenchmarkTrackerPush(b *testing.B) {
+ const numVals = 10000
+ vals := rand.Perm(numVals)
+ tracker := counter.NewTracker()
+
+ b.SetParallelism(100)
+ b.RunParallel(func(pb *testing.PB) {
+ for i := 0; pb.Next(); i++ {
+ tracker.Push(int64(vals[i%numVals]))
+ }
+ })
+}
diff --git a/lib/stats/histogram/histogram.go b/lib/stats/histogram/histogram.go
index 44c3b07..2c0ccef 100644
--- a/lib/stats/histogram/histogram.go
+++ b/lib/stats/histogram/histogram.go
@@ -17,8 +17,9 @@
type Histogram struct {
opts Options
buckets []bucketInternal
- sum *counter.Counter
count *counter.Counter
+ sum *counter.Counter
+ tracker *counter.Tracker
}
// Options contains the parameters that define the histogram's buckets.
@@ -54,8 +55,9 @@
h := Histogram{
opts: opts,
buckets: make([]bucketInternal, opts.NumBuckets),
- sum: counter.New(),
count: counter.New(),
+ sum: counter.New(),
+ tracker: counter.NewTracker(),
}
low := opts.MinValue
delta := opts.SmallestBucketSize
@@ -82,6 +84,7 @@
h.buckets[bucket].count.Incr(1)
h.count.Incr(1)
h.sum.Incr(value)
+ h.tracker.Push(value)
return nil
}
@@ -103,6 +106,8 @@
v := stats.HistogramValue{
Count: h.count.Value(),
Sum: h.sum.Value(),
+ Min: h.tracker.Min(),
+ Max: h.tracker.Max(),
Buckets: b,
}
return v
@@ -121,6 +126,8 @@
v := stats.HistogramValue{
Count: h.count.Delta1h(),
Sum: h.sum.Delta1h(),
+ Min: h.tracker.Min1h(),
+ Max: h.tracker.Max1h(),
Buckets: b,
}
return v
@@ -139,6 +146,8 @@
v := stats.HistogramValue{
Count: h.count.Delta10m(),
Sum: h.sum.Delta10m(),
+ Min: h.tracker.Min10m(),
+ Max: h.tracker.Max10m(),
Buckets: b,
}
return v
@@ -157,6 +166,8 @@
v := stats.HistogramValue{
Count: h.count.Delta1m(),
Sum: h.sum.Delta1m(),
+ Min: h.tracker.Min1m(),
+ Max: h.tracker.Max1m(),
Buckets: b,
}
return v
diff --git a/lib/stats/histogram/histogram_test.go b/lib/stats/histogram/histogram_test.go
index e184142..538e619 100644
--- a/lib/stats/histogram/histogram_test.go
+++ b/lib/stats/histogram/histogram_test.go
@@ -8,11 +8,11 @@
func TestHistogram(t *testing.T) {
// This creates a histogram with the following buckets:
- // [1, 2[
- // [2, 4[
- // [4, 8[
- // [8, 16[
- // [16, Inf
+ // [1, 2)
+ // [2, 4)
+ // [4, 8)
+ // [8, 16)
+ // [16, Inf)
opts := histogram.Options{
NumBuckets: 5,
GrowthFactor: 1.0,
@@ -22,21 +22,35 @@
h := histogram.New(opts)
// Trying to add a value that's less than MinValue, should return an error.
if err := h.Add(0); err == nil {
- t.Errorf("unexpected return value for Add(0.0). Want != nil, Got nil")
+ t.Errorf("unexpected return value for Add(0.0). Want != nil, got nil")
}
// Adding good values. Expect no errors.
for i := 1; i <= 50; i++ {
if err := h.Add(int64(i)); err != nil {
- t.Errorf("unexpected return value for Add(%d). Want nil, Got %v", i, err)
+ t.Errorf("unexpected return value for Add(%d). Want nil, got %v", i, err)
}
}
expectedCount := []int64{1, 2, 4, 8, 35}
buckets := h.Value().Buckets
for i := 0; i < opts.NumBuckets; i++ {
if buckets[i].Count != expectedCount[i] {
- t.Errorf("unexpected count for bucket[%d]. Want %d, Got %v", i, expectedCount[i], buckets[i].Count)
+ t.Errorf("unexpected count for bucket[%d]. Want %d, got %v", i, expectedCount[i], buckets[i].Count)
}
}
+
+ v := h.Value()
+ if expected, got := int64(50), v.Count; got != expected {
+ t.Errorf("unexpected count in histogram value. Want %d, got %v", expected, got)
+ }
+ if expected, got := int64(50*(1+50)/2), v.Sum; got != expected {
+ t.Errorf("unexpected sum in histogram value. Want %d, got %v", expected, got)
+ }
+ if expected, got := int64(1), v.Min; got != expected {
+ t.Errorf("unexpected min in histogram value. Want %d, got %v", expected, got)
+ }
+ if expected, got := int64(50), v.Max; got != expected {
+ t.Errorf("unexpected max in histogram value. Want %d, got %v", expected, got)
+ }
}
func BenchmarkHistogram(b *testing.B) {
diff --git a/lib/stats/stats_test.go b/lib/stats/stats_test.go
index 0773280..e7b28a4 100644
--- a/lib/stats/stats_test.go
+++ b/lib/stats/stats_test.go
@@ -25,7 +25,7 @@
func TestStats(t *testing.T) {
now := time.Unix(1, 0)
- counter.Now = func() time.Time { return now }
+ counter.TimeNow = func() time.Time { return now }
a := libstats.NewInteger("ipc/test/aaa")
b := libstats.NewFloat("ipc/test/bbb")
@@ -224,6 +224,8 @@
Value: istats.HistogramValue{
Count: 2,
Sum: 3,
+ Min: 1,
+ Max: 2,
Buckets: []istats.HistogramBucket{
istats.HistogramBucket{LowBound: 0, Count: 0},
istats.HistogramBucket{LowBound: 1, Count: 1},
@@ -253,6 +255,8 @@
Value: istats.HistogramValue{
Count: 2,
Sum: 5,
+ Min: 2,
+ Max: 3,
Buckets: []istats.HistogramBucket{
istats.HistogramBucket{LowBound: 0, Count: 0},
istats.HistogramBucket{LowBound: 1, Count: 0},
diff --git a/lib/testutil/glob.go b/lib/testutil/glob.go
index 0bc1482..fa36e79 100644
--- a/lib/testutil/glob.go
+++ b/lib/testutil/glob.go
@@ -4,15 +4,17 @@
"io"
"sort"
+ "veyron.io/veyron/veyron2"
+ "veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
- "veyron.io/veyron/veyron2/rt"
)
// GlobName calls __Glob on the given object with the given pattern and returns
// a sorted list of matching object names, or an error.
-func GlobName(name, pattern string) ([]string, error) {
- call, err := rt.R().Client().StartCall(rt.R().NewContext(), name, ipc.GlobMethod, []interface{}{pattern})
+func GlobName(ctx context.T, name, pattern string) ([]string, error) {
+ client := ctx.Runtime().(veyron2.Runtime).Client()
+ call, err := client.StartCall(ctx, name, ipc.GlobMethod, []interface{}{pattern})
if err != nil {
return nil, err
}
diff --git a/lib/testutil/init.go b/lib/testutil/init.go
index c8aaf5d..794ec5a 100644
--- a/lib/testutil/init.go
+++ b/lib/testutil/init.go
@@ -14,6 +14,7 @@
"strconv"
"sync"
"time"
+
"veyron.io/veyron/veyron2/vlog"
)
@@ -47,6 +48,7 @@
}
var Rand *Random
+var once sync.Once
// Init sets up state for running tests: Adjusting GOMAXPROCS,
// configuring the vlog logging library, setting up the random number generator
@@ -56,28 +58,32 @@
// flags. Thus, it is NOT a good idea to call this from the init() function
// of any module except "main" or _test.go files.
func Init() {
- if os.Getenv("GOMAXPROCS") == "" {
- // Set the number of logical processors to the number of CPUs,
- // if GOMAXPROCS is not set in the environment.
- runtime.GOMAXPROCS(runtime.NumCPU())
- }
- // At this point all of the flags that we're going to use for
- // tests must be defined.
- // This will be the case if this is called from the init()
- // function of a _test.go file.
- flag.Parse()
- vlog.ConfigureLibraryLoggerFromFlags()
- // Initialize pseudo-random number generator.
- seed := time.Now().UnixNano()
- seedString := os.Getenv(SeedEnv)
- if seedString != "" {
- var err error
- base, bitSize := 0, 64
- seed, err = strconv.ParseInt(seedString, base, bitSize)
- if err != nil {
- vlog.Fatalf("ParseInt(%v, %v, %v) failed: %v", seedString, base, bitSize, err)
+ init := func() {
+ if os.Getenv("GOMAXPROCS") == "" {
+ // Set the number of logical processors to the number of CPUs,
+ // if GOMAXPROCS is not set in the environment.
+ runtime.GOMAXPROCS(runtime.NumCPU())
}
+ // At this point all of the flags that we're going to use for
+ // tests must be defined.
+ // This will be the case if this is called from the init()
+ // function of a _test.go file.
+ flag.Parse()
+ vlog.ConfigureLibraryLoggerFromFlags()
+
+ // Initialize pseudo-random number generator.
+ seed := time.Now().UnixNano()
+ seedString := os.Getenv(SeedEnv)
+ if seedString != "" {
+ var err error
+ base, bitSize := 0, 64
+ seed, err = strconv.ParseInt(seedString, base, bitSize)
+ if err != nil {
+ vlog.Fatalf("ParseInt(%v, %v, %v) failed: %v", seedString, base, bitSize, err)
+ }
+ }
+ vlog.Infof("Seeding pseudo-random number generator with %v", seed)
+ Rand = &Random{rand: rand.New(rand.NewSource(seed))}
}
- vlog.Infof("Seeding pseudo-random number generator with %v", seed)
- Rand = &Random{rand: rand.New(rand.NewSource(seed))}
+ once.Do(init)
}
diff --git a/lib/testutil/security/util.go b/lib/testutil/security/util.go
index f92cd99..a696269 100644
--- a/lib/testutil/security/util.go
+++ b/lib/testutil/security/util.go
@@ -9,6 +9,7 @@
vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/services/security/access"
)
// NewVeyronCredentials generates a directory with a new principal
@@ -88,13 +89,13 @@
// temporary file, and returns the path to the file. This function is meant
// to be used for testing purposes only, it panics if there is an error. The
// caller must ensure that the created file is removed once it is no longer needed.
-func SaveACLToFile(acl security.ACL) string {
+func SaveACLToFile(acl access.TaggedACLMap) string {
f, err := ioutil.TempFile("", "saved_acl")
if err != nil {
panic(err)
}
defer f.Close()
- if err := vsecurity.SaveACL(f, acl); err != nil {
+ if err := acl.WriteTo(f); err != nil {
defer os.Remove(f.Name())
panic(err)
}
diff --git a/lib/testutil/security/util_test.go b/lib/testutil/security/util_test.go
index 08eb1e1..36718c3 100644
--- a/lib/testutil/security/util_test.go
+++ b/lib/testutil/security/util_test.go
@@ -7,6 +7,7 @@
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/services/security/access"
_ "veyron.io/veyron/veyron/profiles"
vsecurity "veyron.io/veyron/veyron/security"
@@ -37,14 +38,11 @@
t.Fatalf("rt.New failed: %v", err)
}
defer r.Cleanup()
- acl := security.ACL{}
- acl.In = map[security.BlessingPattern]security.LabelSet{
- "veyron/...": security.LabelSet(security.ReadLabel),
- "veyron/alice": security.LabelSet(security.ReadLabel | security.WriteLabel),
- "veyron/bob": security.LabelSet(security.AdminLabel),
- }
- acl.NotIn = map[string]security.LabelSet{
- "veyron/che": security.LabelSet(security.ReadLabel),
+ acl := access.TaggedACLMap{
+ "Admin": access.ACL{
+ In: []security.BlessingPattern{"comics/..."},
+ NotIn: []string{"comics/villain"},
+ },
}
filePath := SaveACLToFile(acl)
@@ -55,12 +53,12 @@
t.Fatalf("os.Open(%v) failed: %v", filePath, err)
}
defer f.Close()
- loadedACL, err := vsecurity.LoadACL(f)
+ loadedACL, err := access.ReadTaggedACLMap(f)
if err != nil {
t.Fatalf("LoadACL failed: %v", err)
}
if !reflect.DeepEqual(loadedACL, acl) {
- t.Fatalf("Got ACL %v, but want %v", loadedACL, acl)
+ t.Fatalf("Got %#v, want %#v", loadedACL, acl)
}
}
diff --git a/lib/websocket/conn_nacl.go b/lib/websocket/conn_nacl.go
index 22d368d..e20ced8 100644
--- a/lib/websocket/conn_nacl.go
+++ b/lib/websocket/conn_nacl.go
@@ -50,7 +50,7 @@
if len(c.currBuffer) == 0 {
c.currBuffer, err = c.ws.ReceiveMessage()
if err != nil {
- return 0, nil
+ return 0, err
}
}
diff --git a/runtimes/google/ipc/benchmarks/bmclient/main.go b/runtimes/google/ipc/benchmarks/bmclient/main.go
index aee94e8..874afb2 100644
--- a/runtimes/google/ipc/benchmarks/bmclient/main.go
+++ b/runtimes/google/ipc/benchmarks/bmclient/main.go
@@ -19,6 +19,8 @@
func main() {
r := rt.Init()
+ defer r.Cleanup()
+
ctx := r.NewContext()
if *chunkCount == 0 {
benchmarks.CallEcho(ctx, *server, *count, *payloadSize, os.Stdout)
diff --git a/runtimes/google/ipc/benchmarks/bmserver/main.go b/runtimes/google/ipc/benchmarks/bmserver/main.go
index f07afef..7d09e63 100644
--- a/runtimes/google/ipc/benchmarks/bmserver/main.go
+++ b/runtimes/google/ipc/benchmarks/bmserver/main.go
@@ -12,8 +12,10 @@
func main() {
r := rt.Init()
+ defer r.Cleanup()
+
addr, stop := benchmarks.StartServer(r, roaming.ListenSpec)
vlog.Infof("Listening on %s", addr)
defer stop()
- <-signals.ShutdownOnSignals()
+ <-signals.ShutdownOnSignals(r)
}
diff --git a/runtimes/google/ipc/client.go b/runtimes/google/ipc/client.go
index 2121314..91fc5bc 100644
--- a/runtimes/google/ipc/client.go
+++ b/runtimes/google/ipc/client.go
@@ -13,26 +13,73 @@
"veyron.io/veyron/veyron/runtimes/google/ipc/stream/vc"
"veyron.io/veyron/veyron/runtimes/google/ipc/version"
inaming "veyron.io/veyron/veyron/runtimes/google/naming"
- "veyron.io/veyron/veyron/runtimes/google/vtrace"
+ ivtrace "veyron.io/veyron/veyron/runtimes/google/vtrace"
"veyron.io/veyron/veyron2/context"
+ "veyron.io/veyron/veyron2/i18n"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/ipc/stream"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/options"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/vdl/vdlutil"
- "veyron.io/veyron/veyron2/verror"
+ old_verror "veyron.io/veyron/veyron2/verror"
+ verror "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
"veyron.io/veyron/veyron2/vom"
+ "veyron.io/veyron/veyron2/vom2"
+ "veyron.io/veyron/veyron2/vtrace"
)
+const pkgPath = "veyron.io/veyron/veyron/runtimes/google/ipc"
+
var (
- errNoServers = verror.NoExistf("ipc: no servers")
- errNoAccess = verror.NoAccessf("ipc: client unwilling to access to server")
- errFlowClosed = verror.Abortedf("ipc: flow closed")
- errRemainingStreamResults = verror.BadProtocolf("ipc: Finish called with remaining streaming results")
- errNonRootedName = verror.BadArgf("ipc: cannot connect to a non-rooted name")
+ // Local errs that are used to provide details to the public ones.
+ errClientCloseAlreadyCalled = verror.Register(pkgPath+".closeAlreadyCalled", verror.NoRetry,
+ "ipc.Client.Close has already been called")
+
+ errClientFinishAlreadyCalled = verror.Register(pkgPath+".finishAlreadyCalled", verror.NoRetry, "ipc.Call.Finish has already been called")
+
+ errNonRootedName = verror.Register(pkgPath+".nonRootedName", verror.NoRetry, "{3} does not appear to contain an address")
+
+ errInvalidEndpoint = verror.Register(pkgPath+".invalidEndpoint", verror.RetryRefetch, "{3} is an invalid endpoint")
+
+ errIncompatibleEndpoint = verror.Register(pkgPath+".invalidEndpoint", verror.RetryRefetch, "{3} is an incompatible endpoint")
+
+ errNotTrusted = verror.Register(pkgPath+".notTrusted", verror.RetryConnection, "name {3} not trusted using blessings {4}{:5}")
+
+ errAuthError = verror.Register(pkgPath+".authError", verror.RetryRefetch, "authentication error from server {3}{:4}")
+
+ errSystemRetry = verror.Register(pkgPath+".sysErrorRetryConnection", verror.RetryConnection, "{:3:}")
+ errSystemNoRetry = verror.Register(pkgPath+".sysErrorNoRetry", verror.NoRetry, "{:3:}")
+
+ errVomEncoder = verror.Register(pkgPath+".vomEncoder", verror.NoRetry, "failed to create vom encoder {:3}")
+ errVomDecoder = verror.Register(pkgPath+".vomDecoder", verror.NoRetry, "failed to create vom decoder {:3}")
+
+ errRequestEncoding = verror.Register(pkgPath+".requestEncoding", verror.NoRetry, "failed to encode request {3}{:4}")
+
+ errDischargeEncoding = verror.Register(pkgPath+".dischargeEncoding", verror.NoRetry, "failed to encode discharge {3}{:4}")
+
+ errArgEncoding = verror.Register(pkgPath+".argEncoding", verror.NoRetry, "failed to encode arg #{3}{:4:}")
+
+ errMismatchedResults = verror.Register(pkgPath+".mismatchedResults", verror.NoRetry, "expected {3} results, but got {4}")
+
+ errResultDecoding = verror.Register(pkgPath+".resultDecoding", verror.NoRetry, "failed to decode result #{3}{:4}")
+
+ errResponseDecoding = verror.Register(pkgPath+".responseDecoding", verror.NoRetry, "failed to decode response{:3}")
+
+ errRemainingStreamResults = verror.Register(pkgPath+".remaingStreamResults", verror.NoRetry, "stream closed with remaining stream results")
+
+ errNoBlessings = verror.Register(pkgPath+".noBlessings", verror.NoRetry, "server has not presented any blessings")
+
+ errAuthNoPatternMatch = verror.Register(pkgPath+".authNoPatternMatch",
+ verror.NoRetry, "server blessings {3} do not match pattern {4}")
+
+ errDefaultAuthDenied = verror.Register(pkgPath+".defaultAuthDenied", verror.NoRetry, "default authorization precludes talking to server with blessings{:3}")
+
+ errBlessingGrant = verror.Register(pkgPath+".blessingGrantFailed", verror.NoRetry, "failed to grant blessing to server with blessings {3}{:4}")
+
+ errBlessingAdd = verror.Register(pkgPath+".blessingAddFailed", verror.NoRetry, "failed to add blessing granted to server {3}{:4}")
)
var serverPatternRegexp = regexp.MustCompile("^\\[([^\\]]+)\\](.*)")
@@ -87,11 +134,11 @@
return c, nil
}
-func (c *client) createFlow(ep naming.Endpoint) (stream.Flow, error) {
+func (c *client) createFlow(ctx context.T, ep naming.Endpoint, noDischarges bool) (stream.Flow, verror.E) {
c.vcMapMu.Lock()
defer c.vcMapMu.Unlock()
if c.vcMap == nil {
- return nil, fmt.Errorf("client has been closed")
+ return nil, verror.Make(errClientCloseAlreadyCalled, ctx)
}
if vcinfo := c.vcMap[ep.String()]; vcinfo != nil {
if flow, err := vcinfo.vc.Connect(); err == nil {
@@ -109,14 +156,21 @@
vcOpts := make([]stream.VCOpt, len(c.vcOpts))
copy(vcOpts, c.vcOpts)
c.vcMapMu.Unlock()
- vc, err := sm.Dial(ep, vcOpts...)
+ if noDischarges {
+ vcOpts = append(vcOpts, vc.NoDischarges{})
+ }
+ vc, err := sm.Dial(ep, append(vcOpts, vc.DialContext{ctx})...)
c.vcMapMu.Lock()
if err != nil {
- return nil, err
+ if strings.Contains(err.Error(), "authentication failed") {
+ return nil, verror.Make(errAuthError, ctx, ep, err)
+ } else {
+ return nil, verror.Make(errSystemRetry, ctx, err)
+ }
}
if c.vcMap == nil {
sm.ShutdownEndpoint(ep)
- return nil, fmt.Errorf("client has been closed")
+ return nil, verror.Make(errClientCloseAlreadyCalled, ctx)
}
if othervc, exists := c.vcMap[ep.String()]; exists {
vc = othervc.vc
@@ -125,33 +179,40 @@
} else {
c.vcMap[ep.String()] = &vcInfo{vc: vc, remoteEP: ep}
}
- return vc.Connect()
+ flow, err := vc.Connect()
+ if err != nil {
+
+ return nil, verror.Make(errAuthError, ctx, ep, err)
+ }
+ return flow, nil
}
// connectFlow parses an endpoint and a suffix out of the server and establishes
// a flow to the endpoint, returning the parsed suffix.
// The server name passed in should be a rooted name, of the form "/ep/suffix" or
// "/ep//suffix", or just "/ep".
-func (c *client) connectFlow(server string) (stream.Flow, string, error) {
+func (c *client) connectFlow(ctx context.T, server string, noDischarges bool) (stream.Flow, string, verror.E) {
address, suffix := naming.SplitAddressName(server)
if len(address) == 0 {
- return nil, "", errNonRootedName
+ return nil, "", verror.Make(errNonRootedName, ctx, server)
}
ep, err := inaming.NewEndpoint(address)
if err != nil {
- return nil, "", err
+ return nil, "", verror.Make(errInvalidEndpoint, ctx, address)
}
if err = version.CheckCompatibility(ep); err != nil {
- return nil, "", err
+ return nil, "", verror.Make(errIncompatibleEndpoint, ctx, ep)
}
- flow, err := c.createFlow(ep)
- if err != nil {
- return nil, "", err
+ flow, verr := c.createFlow(ctx, ep, noDischarges)
+ if verr != nil {
+ return nil, "", verr
}
return flow, suffix, nil
}
// A randomized exponential backoff. The randomness deters error convoys from forming.
+// TODO(cnicolaou): rationalize this and the backoff in ipc.Server. Note
+// that rand is not thread safe and may crash.
func backoff(n int, deadline time.Time) bool {
b := time.Duration(math.Pow(1.5+(rand.Float64()/2.0), float64(n)) * float64(time.Second))
if b > maxBackoff {
@@ -171,29 +232,6 @@
return true
}
-// TODO(p): replace these checks with m3b's retry bit when it exists. This is currently a colossal hack.
-func retriable(err error) bool {
- e := err.Error()
- // Authentication errors are permanent.
- if strings.Contains(e, "authorized") {
- return false
- }
- // Resolution errors are retriable.
- if strings.Contains(e, "ipc: Resolve") {
- return true
- }
- // Kernel level errors are retriable.
- if strings.Contains(e, "errno") {
- return true
- }
- // Connection refused is retriable.
- if strings.Contains(e, "connection refused") {
- return true
- }
-
- return false
-}
-
func getRetryTimeoutOpt(opts []ipc.CallOpt) (time.Duration, bool) {
for _, o := range opts {
if r, ok := o.(options.RetryTimeout); ok {
@@ -217,6 +255,15 @@
return false
}
+func shouldNotFetchDischarges(opts []ipc.CallOpt) bool {
+ for _, o := range opts {
+ if _, ok := o.(vc.NoDischarges); ok {
+ return true
+ }
+ }
+ return false
+}
+
func mkDischargeImpetus(serverBlessings []string, method string, args []interface{}) security.DischargeImpetus {
var impetus security.DischargeImpetus
if len(serverBlessings) > 0 {
@@ -238,8 +285,10 @@
// startCall ensures StartCall always returns verror.E.
func (c *client) startCall(ctx context.T, name, method string, args []interface{}, opts []ipc.CallOpt) (ipc.Call, verror.E) {
if ctx == nil {
- return nil, verror.BadArgf("ipc: %s.%s called with nil context", name, method)
+ return nil, verror.ExplicitMake(verror.BadArg, i18n.NoLangID, "ipc.Client", "StartCall")
}
+ ctx, span := ivtrace.WithNewSpan(ctx, fmt.Sprintf("<client>%q.%s", name, method))
+ ctx = verror.ContextWithComponentName(ctx, "ipc.Client")
// Context specified deadline.
deadline, hasDeadline := ctx.Deadline()
@@ -251,6 +300,9 @@
// Caller specified deadline.
deadline = time.Now().Add(time.Duration(r))
}
+
+ skipResolve := getNoResolveOpt(opts)
+
var lastErr verror.E
for retries := 0; ; retries++ {
if retries != 0 {
@@ -258,64 +310,95 @@
break
}
}
- call, err := c.tryCall(ctx, name, method, args, opts)
+ call, err := c.tryCall(ctx, name, method, args, skipResolve, opts)
if err == nil {
return call, nil
}
lastErr = err
- if time.Now().After(deadline) || !retriable(err) {
+
+ shouldRetry := true
+ switch {
+ case err.Action() != verror.RetryConnection && err.Action() != verror.RetryRefetch:
+ shouldRetry = false
+ case time.Now().After(deadline):
+ shouldRetry = false
+ case verror.Is(err, verror.NoServers.ID) && skipResolve:
+ // If we're skipping resolution and there are no servers for
+ // this call retrying is not going to help, we can't come up
+ // with new servers if there is no resolution.
+ shouldRetry = false
+ }
+
+ if !shouldRetry {
+ span.Annotatef("Cannot retry after error: %s", err)
break
}
+ span.Annotatef("Retrying due to error: %s", err)
}
return nil, lastErr
}
type serverStatus struct {
- index int
- suffix string
- flow stream.Flow
- errConn verror.E
- errAccess verror.E
+ index int
+ suffix string
+ flow stream.Flow
+ err verror.E
}
// TODO(cnicolaou): implement real, configurable load balancing.
-func (c *client) tryServer(index int, server string, ch chan<- *serverStatus) {
+func (c *client) tryServer(ctx context.T, index int, server string, ch chan<- *serverStatus, noDischarges bool) {
status := &serverStatus{index: index}
- var err error
- if status.flow, status.suffix, err = c.connectFlow(server); err != nil {
- vlog.VI(2).Infof("ipc: couldn't connect to server %v: %v", server, err)
- status.errConn = verror.NoExistf("ipc: %q: %s", server, err)
+ var err verror.E
+ var span vtrace.Span
+ ctx, span = ivtrace.WithNewSpan(ctx, "<client>connectFlow")
+ span.Annotatef("address:%v", server)
+ defer span.Finish()
+ if status.flow, status.suffix, err = c.connectFlow(ctx, server, noDischarges); err != nil {
+ vlog.VI(2).Infof("ipc: err: %s", err)
+ status.err = err
status.flow = nil
}
ch <- status
}
// tryCall makes a single attempt at a call, against possibly multiple servers.
-func (c *client) tryCall(ctx context.T, name, method string, args []interface{}, opts []ipc.CallOpt) (ipc.Call, verror.E) {
- ctx, _ = vtrace.WithNewSpan(ctx, fmt.Sprintf("<client>\"%s\".%s", name, method))
- _, serverPattern, name := splitObjectName(name)
+func (c *client) tryCall(ctx context.T, name, method string, args []interface{}, skipResolve bool, opts []ipc.CallOpt) (ipc.Call, verror.E) {
+ mtPattern, serverPattern, name := splitObjectName(name)
+ noDischarges := shouldNotFetchDischarges(opts)
// Resolve name unless told not to.
var servers []string
- if getNoResolveOpt(opts) {
+ if skipResolve {
servers = []string{name}
} else {
- if resolved, err := c.ns.Resolve(ctx, name); err != nil {
- return nil, verror.NoExistf("ipc: Resolve(%q) failed: %v", name, err)
+ if resolved, err := c.ns.Resolve(ctx, name, naming.RootBlessingPatternOpt(mtPattern)); err != nil {
+ if verror.Is(err, naming.ErrNoSuchName.ID) {
+ return nil, verror.Make(verror.NoServers, ctx, name)
+ }
+ return nil, verror.Make(verror.NoExist, ctx, name, err)
} else {
+ if len(resolved) == 0 {
+ return nil, verror.Make(verror.NoServers, ctx, name)
+ }
// An empty set of protocols means all protocols...
ordered, err := filterAndOrderServers(resolved, c.preferredProtocols)
- if len(ordered) == 0 {
- return nil, verror.NoExistf("ipc: %q: %s", name, err)
+ if err != nil {
+ return nil, verror.Make(verror.NoServers, ctx, name, err)
+ } else if len(ordered) == 0 {
+ // sooo annoying....
+ r := []interface{}{err}
+ r = append(r, name)
+ for _, s := range resolved {
+ r = append(r, s)
+ }
+ return nil, verror.Make(verror.NoServers, ctx, r)
}
servers = ordered
}
}
+
// servers is now orderd by the priority heurestic implemented in
// filterAndOrderServers.
attempts := len(servers)
- if attempts == 0 {
- return nil, errNoServers
- }
// Try to connect to all servers in parallel. Provide sufficient buffering
// for all of the connections to finish instantaneously. This is important
@@ -325,7 +408,7 @@
responses := make([]*serverStatus, attempts)
ch := make(chan *serverStatus, attempts)
for i, server := range servers {
- go c.tryServer(i, server, ch)
+ go c.tryServer(ctx, i, server, ch, noDischarges)
}
delay := time.Duration(ipc.NoTimeout)
@@ -351,7 +434,7 @@
}
case <-timeoutChan:
vlog.VI(2).Infof("ipc: timeout on connection to server %v ", name)
- return c.failedTryCall(name, method, servers, responses, ch)
+ return c.failedTryCall(ctx, name, method, servers, responses, ch)
}
// Process new responses, in priority order.
@@ -375,9 +458,10 @@
if r.flow.LocalPrincipal() != nil {
// Validate caveats on the server's identity for the context associated with this call.
var err error
- if serverB, grantedB, err = c.authorizeServer(r.flow, name, method, serverPattern, opts); err != nil {
- vlog.VI(2).Infof("ipc: client unwilling to invoke %q.%q on server %v: %v", name, method, r.flow.RemoteBlessings(), err)
- r.errAccess = verror.NoAccessf("ipc: unwilling to invoke %q.%q on server %v: %v", name, method, r.flow.RemoteBlessings(), err)
+ if serverB, grantedB, err = c.authorizeServer(ctx, r.flow, name, method, serverPattern, opts); err != nil {
+ r.err = verror.Make(errNotTrusted, ctx,
+ name, r.flow.RemoteBlessings(), err)
+ vlog.VI(2).Infof("ipc: err: %s", r.err)
r.flow.Close()
r.flow = nil
continue
@@ -393,7 +477,10 @@
//
// We must ensure that all flows other than r.flow are closed.
go cleanupTryCall(r, responses, ch)
- fc := newFlowClient(ctx, serverB, r.flow, c.dc)
+ fc, err := newFlowClient(ctx, serverB, r.flow, c.dc)
+ if err != nil {
+ return nil, err.(verror.E)
+ }
if doneChan := ctx.Done(); doneChan != nil {
go func() {
@@ -409,13 +496,16 @@
if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
timeout = deadline.Sub(time.Now())
}
+ if noDischarges {
+ fc.dc = nil
+ }
if verr := fc.start(r.suffix, method, args, timeout, grantedB); verr != nil {
return nil, verr
}
return fc, nil
}
if numResponses == len(responses) {
- return c.failedTryCall(name, method, servers, responses, ch)
+ return c.failedTryCall(ctx, name, method, servers, responses, ch)
}
}
}
@@ -449,30 +539,38 @@
// failedTryCall performs asynchronous cleanup for tryCall, and returns an
// appropriate error from the responses we've already received. All parallel
// calls in tryCall failed or we timed out if we get here.
-func (c *client) failedTryCall(name, method string, servers []string, responses []*serverStatus, ch chan *serverStatus) (ipc.Call, verror.E) {
+func (c *client) failedTryCall(ctx context.T, name, method string, servers []string, responses []*serverStatus, ch chan *serverStatus) (ipc.Call, verror.E) {
go cleanupTryCall(nil, responses, ch)
c.ns.FlushCacheEntry(name)
- // TODO(cnicolaou): introduce a third error code here for mixed
- // conn/access errors.
- var errs []verror.E
- for _, r := range responses {
- switch {
- case r != nil && r.errConn != nil:
- errs = append(errs, r.errConn)
- case r != nil && r.errAccess != nil:
- errs = append(errs, r.errAccess)
+ noconn, untrusted := []string{}, []string{}
+ for i, r := range responses {
+ if r != nil && r.err != nil {
+ vlog.VI(2).Infof("Server: %s: %s", servers[i], r.err)
+ switch {
+ case verror.Is(r.err, errNotTrusted.ID) || verror.Is(r.err, errAuthError.ID):
+ untrusted = append(untrusted, r.err.Error())
+ default:
+ noconn = append(noconn, r.err.Error())
+ }
}
}
- return nil, verror.NoExistf("ipc: client failed to invoke %q.%q: on %v: %v", name, method, servers, errs)
+ switch {
+ case len(untrusted) > 0 && len(noconn) > 0:
+ return nil, verror.Make(verror.NoServersAndAuth, ctx, append(noconn, untrusted...))
+ case len(noconn) > 0:
+ return nil, verror.Make(verror.NoServers, ctx, noconn)
+ default:
+ return nil, verror.Make(verror.NotTrusted, ctx, untrusted)
+ }
}
// authorizeServer validates that the server (remote end of flow) has the credentials to serve
// the RPC name.method for the client (local end of the flow). It returns the blessings at the
// server that are authorized for this purpose and any blessings that are to be granted to
// the server (via ipc.Granter implementations in opts.)
-func (c *client) authorizeServer(flow stream.Flow, name, method string, serverPattern security.BlessingPattern, opts []ipc.CallOpt) (serverBlessings []string, grantedBlessings security.Blessings, err error) {
+func (c *client) authorizeServer(ctx context.T, flow stream.Flow, name, method string, serverPattern security.BlessingPattern, opts []ipc.CallOpt) (serverBlessings []string, grantedBlessings security.Blessings, err verror.E) {
if flow.RemoteBlessings() == nil {
- return nil, nil, fmt.Errorf("server has not presented any blessings")
+ return nil, nil, verror.Make(errNoBlessings, ctx)
}
ctxt := security.NewContext(&security.ContextParams{
LocalPrincipal: flow.LocalPrincipal(),
@@ -486,20 +584,20 @@
serverBlessings = flow.RemoteBlessings().ForContext(ctxt)
if serverPattern != "" {
if !serverPattern.MatchedBy(serverBlessings...) {
- return nil, nil, fmt.Errorf("server %v does not match the provided pattern %q", serverBlessings, serverPattern)
+ return nil, nil, verror.Make(errAuthNoPatternMatch, ctx, serverBlessings, serverPattern)
}
} else if enableSecureServerAuth {
if err := (defaultAuthorizer{}).Authorize(ctxt); err != nil {
- return nil, nil, fmt.Errorf("default authorization precludes talking to server %v", serverBlessings)
+ return nil, nil, verror.Make(errDefaultAuthDenied, ctx, serverBlessings)
}
}
for _, o := range opts {
switch v := o.(type) {
case ipc.Granter:
if b, err := v.Grant(flow.RemoteBlessings()); err != nil {
- return nil, nil, fmt.Errorf("failed to grant blessing to server %v: %v", serverBlessings, err)
+ return nil, nil, verror.Make(errBlessingGrant, ctx, serverBlessings, err)
} else if grantedBlessings, err = security.UnionOfBlessings(grantedBlessings, b); err != nil {
- return nil, nil, fmt.Errorf("failed to add blessing granted to server %v: %v", serverBlessings, err)
+ return nil, nil, verror.Make(errBlessingAdd, ctx, serverBlessings, err)
}
}
}
@@ -525,8 +623,8 @@
// flow that's already connected to the server.
type flowClient struct {
ctx context.T // context to annotate with call details
- dec *vom.Decoder // to decode responses and results from the server
- enc *vom.Encoder // to encode requests and args to the server
+ dec vomDecoder // to decode responses and results from the server
+ enc vomEncoder // to encode requests and args to the server
server []string // Blessings bound to the server that authorize it to receive the IPC request from the client.
flow stream.Flow // the underlying flow
response ipc.Response // each decoded response message is kept here
@@ -543,8 +641,8 @@
var _ ipc.Call = (*flowClient)(nil)
var _ ipc.Stream = (*flowClient)(nil)
-func newFlowClient(ctx context.T, server []string, flow stream.Flow, dc vc.DischargeClient) *flowClient {
- return &flowClient{
+func newFlowClient(ctx context.T, server []string, flow stream.Flow, dc vc.DischargeClient) (*flowClient, error) {
+ fc := &flowClient{
ctx: ctx,
dec: vom.NewDecoder(flow),
enc: vom.NewEncoder(flow),
@@ -552,11 +650,24 @@
flow: flow,
dc: dc,
}
+ if vom2.IsEnabled() {
+ var err error
+ if fc.enc, err = vom2.NewBinaryEncoder(flow); err != nil {
+ return nil, fc.close(badProtocol(fc.ctx, verror.Make(errVomEncoder, fc.ctx, err)))
+ }
+ if fc.dec, err = vom2.NewDecoder(flow); err != nil {
+ return nil, fc.close(badProtocol(fc.ctx, verror.Make(errVomDecoder, fc.ctx, err)))
+ }
+ } else {
+ fc.dec = vom.NewDecoder(flow)
+ fc.enc = vom.NewEncoder(flow)
+ }
+ return fc, nil
}
func (fc *flowClient) close(verr verror.E) verror.E {
if err := fc.flow.Close(); err != nil && verr == nil {
- verr = verror.Internalf("ipc: flow close failed: %v", err)
+ verr = verror.Make(errSystemNoRetry, fc.ctx, err)
}
return verr
}
@@ -565,7 +676,7 @@
// Fetch any discharges for third-party caveats on the client's blessings
// if this client owns a discharge-client.
if self := fc.flow.LocalBlessings(); self != nil && fc.dc != nil {
- fc.discharges = fc.dc.PrepareDischarges(self.ThirdPartyCaveats(), mkDischargeImpetus(fc.server, method, args))
+ fc.discharges = fc.dc.PrepareDischarges(fc.ctx, self.ThirdPartyCaveats(), mkDischargeImpetus(fc.server, method, args))
}
req := ipc.Request{
Suffix: suffix,
@@ -574,19 +685,19 @@
Timeout: int64(timeout),
GrantedBlessings: security.MarshalBlessings(blessings),
NumDischarges: uint64(len(fc.discharges)),
- TraceRequest: vtrace.Request(fc.ctx),
+ TraceRequest: ivtrace.Request(fc.ctx),
}
if err := fc.enc.Encode(req); err != nil {
- return fc.close(verror.BadProtocolf("ipc: request encoding failed: %v", err))
+ return fc.close(badProtocol(fc.ctx, verror.Make(errRequestEncoding, fc.ctx, req, err)))
}
for _, d := range fc.discharges {
if err := fc.enc.Encode(d); err != nil {
- return fc.close(verror.BadProtocolf("ipc: failed to encode discharge for %x: %v", d.ID(), err))
+ return fc.close(badProtocol(fc.ctx, verror.Make(errDischargeEncoding, fc.ctx, d.ID(), err)))
}
}
for ix, arg := range args {
if err := fc.enc.Encode(arg); err != nil {
- return fc.close(verror.BadProtocolf("ipc: arg %d encoding failed: %v", ix, err))
+ return fc.close(badProtocol(fc.ctx, verror.Make(errArgEncoding, fc.ctx, ix, err)))
}
}
return nil
@@ -595,15 +706,15 @@
func (fc *flowClient) Send(item interface{}) error {
defer vlog.LogCall()()
if fc.sendClosed {
- return errFlowClosed
+ return verror.Make(verror.Aborted, fc.ctx)
}
// The empty request header indicates what follows is a streaming arg.
if err := fc.enc.Encode(ipc.Request{}); err != nil {
- return fc.close(verror.BadProtocolf("ipc: streaming request header encoding failed: %v", err))
+ return fc.close(badProtocol(fc.ctx, verror.Make(errRequestEncoding, fc.ctx, ipc.Request{}, err)))
}
if err := fc.enc.Encode(item); err != nil {
- return fc.close(verror.BadProtocolf("ipc: streaming arg encoding failed: %v", err))
+ return fc.close(badProtocol(fc.ctx, verror.Make(errArgEncoding, fc.ctx, -1, err)))
}
return nil
}
@@ -619,7 +730,7 @@
// Decode the response header and handle errors and EOF.
if err := fc.dec.Decode(&fc.response); err != nil {
- return fc.close(verror.BadProtocolf("ipc: response header decoding failed: %v", err))
+ return fc.close(badProtocol(fc.ctx, verror.Make(errResponseDecoding, fc.ctx, err)))
}
if fc.response.Error != nil {
return fc.response.Error
@@ -632,7 +743,7 @@
}
// Decode the streaming result.
if err := fc.dec.Decode(itemptr); err != nil {
- return fc.close(verror.BadProtocolf("ipc: streaming result decoding failed: %v", err))
+ return fc.close(badProtocol(fc.ctx, verror.Make(errResponseDecoding, fc.ctx, err)))
}
return nil
}
@@ -672,14 +783,19 @@
func (fc *flowClient) Finish(resultptrs ...interface{}) error {
defer vlog.LogCall()()
err := fc.finish(resultptrs...)
- vtrace.FromContext(fc.ctx).Finish()
+ ivtrace.FromContext(fc.ctx).Finish()
return err
}
+func badProtocol(ctx context.T, err verror.E) verror.E {
+ return verror.Make(verror.BadProtocol, ctx, err)
+}
+
// finish ensures Finish always returns verror.E.
func (fc *flowClient) finish(resultptrs ...interface{}) verror.E {
if fc.finished {
- return fc.close(verror.BadProtocolf("ipc: multiple calls to Finish not allowed"))
+ err := verror.Make(errClientFinishAlreadyCalled, fc.ctx)
+ return fc.close(verror.Make(verror.BadState, fc.ctx, err))
}
fc.finished = true
// Call closeSend implicitly, if the user hasn't already called it. There are
@@ -701,19 +817,21 @@
// Decode the response header, if it hasn't already been decoded by Recv.
if fc.response.Error == nil && !fc.response.EndStreamResults {
if err := fc.dec.Decode(&fc.response); err != nil {
- return fc.close(verror.BadProtocolf("ipc: response header decoding failed: %v", err))
+ return fc.close(badProtocol(fc.ctx, verror.Make(errResponseDecoding, fc.ctx, err)))
}
// The response header must indicate the streaming results have ended.
if fc.response.Error == nil && !fc.response.EndStreamResults {
- return fc.close(errRemainingStreamResults)
+ return fc.close(badProtocol(fc.ctx, verror.Make(errRemainingStreamResults, fc.ctx)))
}
}
// Incorporate any VTrace info that was returned.
- vtrace.MergeResponse(fc.ctx, &fc.response.TraceResponse)
+ ivtrace.MergeResponse(fc.ctx, &fc.response.TraceResponse)
if fc.response.Error != nil {
- if verror.Is(fc.response.Error, verror.NoAccess) && fc.dc != nil {
+ // TODO(cnicolaou): remove verror.NoAccess with verror version
+ // when ipc.Server is converted.
+ if verror.Is(fc.response.Error, old_verror.NoAccess) && fc.dc != nil {
// In case the error was caused by a bad discharge, we do not want to get stuck
// with retrying again and again with this discharge. As there is no direct way
// to detect it, we conservatively flush all discharges we used from the cache.
@@ -721,14 +839,16 @@
vlog.VI(3).Infof("Discarging %d discharges as RPC failed with %v", len(fc.discharges), fc.response.Error)
fc.dc.Invalidate(fc.discharges...)
}
- return fc.close(verror.ConvertWithDefault(verror.Internal, fc.response.Error))
+ // TODO(cnicolaou): we turn this into a non-retryable error until
+ // we have verror on the server side.
+ return fc.close(verror.Convert(verror.Internal, fc.ctx, fc.response.Error))
}
if got, want := fc.response.NumPosResults, uint64(len(resultptrs)); got != want {
- return fc.close(verror.BadProtocolf("ipc: server sent %d results, client expected %d (%#v)", got, want, resultptrs))
+ return fc.close(badProtocol(fc.ctx, verror.Make(errMismatchedResults, fc.ctx, got, want)))
}
for ix, r := range resultptrs {
if err := fc.dec.Decode(r); err != nil {
- return fc.close(verror.BadProtocolf("ipc: result #%d decoding failed: %v", ix, err))
+ return fc.close(badProtocol(fc.ctx, verror.Make(errResultDecoding, fc.ctx, ix, err)))
}
}
return fc.close(nil)
@@ -736,7 +856,7 @@
func (fc *flowClient) Cancel() {
defer vlog.LogCall()()
- vtrace.FromContext(fc.ctx).Annotate("Cancelled")
+ ivtrace.FromContext(fc.ctx).Annotate("Cancelled")
fc.flow.Cancel()
}
diff --git a/runtimes/google/ipc/client_test.go b/runtimes/google/ipc/client_test.go
index c4e8b8a..aa159a7 100644
--- a/runtimes/google/ipc/client_test.go
+++ b/runtimes/google/ipc/client_test.go
@@ -26,7 +26,10 @@
}
func runMountTable(t *testing.T) (*modules.Shell, func()) {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
root, err := sh.Start(core.RootMTCommand, nil, testArgs()...)
if err != nil {
t.Fatalf("unexpected error for root mt: %s", err)
diff --git a/runtimes/google/ipc/context_test.go b/runtimes/google/ipc/context_test.go
index 515ee26..cdc7e0e 100644
--- a/runtimes/google/ipc/context_test.go
+++ b/runtimes/google/ipc/context_test.go
@@ -16,9 +16,13 @@
// so we use a fake one that panics if used. The runtime
// implementation should not ever use the Runtime from a context.
func testContext() context.T {
+ ctx, _ := testContextWithoutDeadline().WithTimeout(20 * time.Second)
+ return ctx
+}
+
+func testContextWithoutDeadline() context.T {
ctx := InternalNewContext(&runtime.PanicRuntime{})
ctx, _ = vtrace.WithNewRootSpan(ctx, nil, false)
- ctx, _ = ctx.WithDeadline(time.Now().Add(20 * time.Second))
return ctx
}
diff --git a/runtimes/google/ipc/discharges.go b/runtimes/google/ipc/discharges.go
index 40459fd..37bb62d 100644
--- a/runtimes/google/ipc/discharges.go
+++ b/runtimes/google/ipc/discharges.go
@@ -1,9 +1,11 @@
package ipc
import (
+ "fmt"
"sync"
"veyron.io/veyron/veyron/runtimes/google/ipc/stream/vc"
+ ivtrace "veyron.io/veyron/veyron/runtimes/google/vtrace"
"veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/ipc"
@@ -12,24 +14,35 @@
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/vdl/vdlutil"
"veyron.io/veyron/veyron2/vlog"
+ "veyron.io/veyron/veyron2/vtrace"
)
// discharger implements vc.DischargeClient.
type dischargeClient struct {
- c ipc.Client
- ctx context.T
- cache dischargeCache
+ c ipc.Client
+ defaultCtx context.T
+ cache dischargeCache
}
-func InternalNewDischargeClient(streamMgr stream.Manager, ns naming.Namespace, ctx context.T, opts ...ipc.ClientOpt) (vc.DischargeClient, error) {
+// InternalNewDischargeClient creates a vc.DischargeClient that will be used to
+// fetch discharges to support blessings presented to a remote process.
+//
+// defaultCtx is the context used when none (nil) is explicitly provided to the
+// PrepareDischarges call. This typically happens when fetching discharges on
+// behalf of a server accepting connections, i.e., before any notion of the
+// "context" of an API call has been established.
+func InternalNewDischargeClient(streamMgr stream.Manager, ns naming.Namespace, defaultCtx context.T, opts ...ipc.ClientOpt) (vc.DischargeClient, error) {
+ if defaultCtx == nil {
+ return nil, fmt.Errorf("must provide a non-nil context to InternalNewDischargeClient")
+ }
c, err := InternalNewClient(streamMgr, ns, opts...)
if err != nil {
return nil, err
}
return &dischargeClient{
- c: c,
- ctx: ctx,
- cache: dischargeCache{cache: make(map[string]security.Discharge)},
+ c: c,
+ defaultCtx: defaultCtx,
+ cache: dischargeCache{cache: make(map[string]security.Discharge)},
}, nil
}
@@ -43,7 +56,7 @@
// options, or requested from the discharge issuer indicated on the caveat.
// Note that requesting a discharge is an ipc call, so one copy of this
// function must be able to successfully terminate while another is blocked.
-func (d *dischargeClient) PrepareDischarges(forcaveats []security.ThirdPartyCaveat, impetus security.DischargeImpetus) (ret []security.Discharge) {
+func (d *dischargeClient) PrepareDischarges(ctx context.T, forcaveats []security.ThirdPartyCaveat, impetus security.DischargeImpetus) (ret []security.Discharge) {
if len(forcaveats) == 0 {
return
}
@@ -59,7 +72,16 @@
// Fetch discharges for caveats for which no discharges were found
// in the cache.
- d.fetchDischarges(d.ctx, caveats, impetus, discharges)
+ if ctx == nil {
+ ctx = d.defaultCtx
+ }
+ if ctx != nil {
+ var span vtrace.Span
+ ctx, span = ivtrace.WithNewSpan(ctx, "Fetching Discharges")
+ defer span.Finish()
+ }
+
+ d.fetchDischarges(ctx, caveats, impetus, discharges)
for _, d := range discharges {
if d != nil {
ret = append(ret, d)
@@ -89,10 +111,10 @@
continue
}
wg.Add(1)
- go func(i int, cav security.ThirdPartyCaveat) {
+ go func(i int, ctx context.T, cav security.ThirdPartyCaveat) {
defer wg.Done()
vlog.VI(3).Infof("Fetching discharge for %v", cav)
- call, err := d.c.StartCall(ctx, cav.Location(), "Discharge", []interface{}{cav, filteredImpetus(cav.Requirements(), impetus)})
+ call, err := d.c.StartCall(ctx, cav.Location(), "Discharge", []interface{}{cav, filteredImpetus(cav.Requirements(), impetus)}, vc.NoDischarges{})
if err != nil {
vlog.VI(3).Infof("Discharge fetch for %v failed: %v", cav, err)
return
@@ -107,7 +129,7 @@
vlog.Errorf("fetchDischarges: server at %s sent a %T (%v) instead of a Discharge", cav.Location(), dAny, dAny)
}
discharges <- fetched{i, d}
- }(i, caveats[i])
+ }(i, ctx, caveats[i])
}
wg.Wait()
close(discharges)
diff --git a/runtimes/google/ipc/flow_test.go b/runtimes/google/ipc/flow_test.go
deleted file mode 100644
index 703a44f..0000000
--- a/runtimes/google/ipc/flow_test.go
+++ /dev/null
@@ -1,167 +0,0 @@
-package ipc
-
-import (
- "bytes"
- "errors"
- "fmt"
- "testing"
-
- "veyron.io/veyron/veyron/lib/testutil"
- tsecurity "veyron.io/veyron/veyron/lib/testutil/security"
-
- "veyron.io/veyron/veyron2/ipc"
- "veyron.io/veyron/veyron2/naming"
- "veyron.io/veyron/veyron2/security"
- "veyron.io/veyron/veyron2/verror"
-)
-
-func init() { testutil.Init() }
-
-// newTestFlows returns the two ends of a bidirectional flow. Each end has its
-// own bookkeeping, to allow testing of method calls.
-func newTestFlows() (*testFlow, *testFlow) {
- var (
- p0, p1 = tsecurity.NewPrincipal("p0"), tsecurity.NewPrincipal("p1")
- blessing0, blessing1 = p0.BlessingStore().Default(), p1.BlessingStore().Default()
- b0, b1 = new(bytes.Buffer), new(bytes.Buffer)
- )
- return &testFlow{r: b0, w: b1, p: p0, lb: blessing0, rb: blessing1}, &testFlow{r: b1, w: b0, p: p1, lb: blessing1, rb: blessing0}
-}
-
-type testFlow struct {
- r, w *bytes.Buffer
- p security.Principal
- lb, rb security.Blessings
- numCloseCalls int
- errClose error
-}
-
-func (f *testFlow) Read(b []byte) (int, error) { return f.r.Read(b) }
-func (f *testFlow) Write(b []byte) (int, error) { return f.w.Write(b) }
-func (*testFlow) LocalEndpoint() naming.Endpoint { return nil }
-func (*testFlow) RemoteEndpoint() naming.Endpoint { return nil }
-func (f *testFlow) LocalPrincipal() security.Principal { return f.p }
-func (f *testFlow) LocalBlessings() security.Blessings { return f.lb }
-func (f *testFlow) RemoteBlessings() security.Blessings { return f.rb }
-func (*testFlow) RemoteDischarges() map[string]security.Discharge { return nil }
-func (*testFlow) SetDeadline(<-chan struct{}) {}
-func (*testFlow) IsClosed() bool { return false }
-func (*testFlow) Closed() <-chan struct{} { return nil }
-func (*testFlow) Cancel() {}
-
-func (f *testFlow) Close() error {
- f.numCloseCalls++
- return f.errClose
-}
-
-// testDisp implements a simple test dispatcher, that uses the newInvoker
-// factory function to create an underlying invoker on each Lookup.
-type testDisp struct {
- newInvoker func(suffix string) ipc.Invoker
-}
-
-func (td testDisp) Lookup(suffix string) (interface{}, security.Authorizer, error) {
- return td.newInvoker(suffix), testServerAuthorizer{}, nil
-}
-
-// closureInvoker serves a method with no user args or results:
-// func(ipc.ServerCall) error
-type closureInvoker struct{ suffix string }
-
-func newClosureInvoker(suffix string) ipc.Invoker {
- return closureInvoker{suffix}
-}
-
-func (closureInvoker) Prepare(method string, numArgs int) (argptrs, tags []interface{}, err error) {
- return nil, []interface{}{security.AdminLabel}, nil
-}
-func (inv closureInvoker) Invoke(method string, call ipc.ServerCall, argptrs []interface{}) (results []interface{}, err error) {
- if inv.suffix == "" {
- return nil, nil
- }
- return nil, errors.New(inv.suffix)
-}
-func (closureInvoker) Signature(ctx ipc.ServerContext) ([]ipc.InterfaceSig, error) {
- return nil, nil
-}
-func (closureInvoker) MethodSignature(ctx ipc.ServerContext, method string) (ipc.MethodSig, error) {
- return ipc.MethodSig{}, nil
-}
-func (closureInvoker) VGlob() *ipc.GlobState {
- return nil
-}
-
-// echoInvoker serves a method that takes a string and echoes it:
-// func(_ ServerCall, arg string) (string, error)
-type echoInvoker struct{ suffix string }
-
-func newEchoInvoker(suffix string) ipc.Invoker {
- return echoInvoker{suffix}
-}
-
-func (echoInvoker) Prepare(method string, numArgs int) (argptrs, tags []interface{}, err error) {
- var arg string
- return []interface{}{&arg}, []interface{}{security.AdminLabel}, nil
-}
-func (inv echoInvoker) Invoke(method string, call ipc.ServerCall, argptrs []interface{}) (results []interface{}, err error) {
- result := fmt.Sprintf("method:%q,suffix:%q,arg:%q", method, inv.suffix, *argptrs[0].(*string))
- return []interface{}{result}, nil
-}
-func (echoInvoker) Signature(ctx ipc.ServerContext) ([]ipc.InterfaceSig, error) {
- return nil, nil
-}
-func (echoInvoker) MethodSignature(ctx ipc.ServerContext, method string) (ipc.MethodSig, error) {
- return ipc.MethodSig{}, nil
-}
-func (echoInvoker) VGlob() *ipc.GlobState {
- return nil
-}
-
-func TestFlowClientServer(t *testing.T) {
- type v []interface{}
- type testcase struct {
- suffix string
- method string
- args []interface{}
- expect []interface{}
- err error
- }
- tests := []testcase{
- {"echo", "A", v{""}, v{`method:"A",suffix:"echo",arg:""`}, nil},
- {"echo", "B", v{"foo"}, v{`method:"B",suffix:"echo",arg:"foo"`}, nil},
- {"echo/abc", "C", v{""}, v{`method:"C",suffix:"echo/abc",arg:""`}, nil},
- {"echo/abc", "D", v{"foo"}, v{`method:"D",suffix:"echo/abc",arg:"foo"`}, nil},
- }
- name := func(t testcase) string {
- return fmt.Sprintf("%s.%s%v", t.suffix, t.method, t.args)
- }
-
- ipcServer := &server{
- ctx: testContext(),
- disp: testDisp{newEchoInvoker},
- stats: newIPCStats(""),
- }
- for _, test := range tests {
- clientFlow, serverFlow := newTestFlows()
- client := newFlowClient(testContext(), []string{"p0"}, clientFlow, nil)
- server := newFlowServer(serverFlow, ipcServer)
- err := client.start(test.suffix, test.method, test.args, 0, nil)
- if err != nil {
- t.Errorf("%s client.start unexpected error: %v", name(test), err)
- }
- if err := server.serve(); !verror.Equal(err, test.err) {
- t.Errorf("%s server.server returned %v want %v", name(test), err, test.err)
- }
- results := makeResultPtrs(test.expect)
- if err := client.Finish(results...); !verror.Equal(err, test.err) {
- t.Errorf(`%s client.Finish got error "%v", want "%v"`, name(test), err, test.err)
- }
- checkResultPtrs(t, name(test), results, test.expect)
- if clientFlow.numCloseCalls != 1 {
- t.Errorf("%s got %d client close calls, want 1", name(test), clientFlow.numCloseCalls)
- }
- if serverFlow.numCloseCalls != 1 {
- t.Errorf("%s got %d server close calls, want 1", name(test), serverFlow.numCloseCalls)
- }
- }
-}
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index 045bd84..82199f8 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -1,10 +1,12 @@
package ipc
import (
+ "encoding/hex"
"errors"
"fmt"
"io"
"net"
+ "os"
"path/filepath"
"reflect"
"runtime"
@@ -18,10 +20,11 @@
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/options"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/services/security/access"
+ "veyron.io/veyron/veyron2/uniqueid"
"veyron.io/veyron/veyron2/vdl/vdlutil"
- "veyron.io/veyron/veyron2/verror"
+ verror "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
- "veyron.io/veyron/veyron2/vom"
"veyron.io/veyron/veyron/lib/netstate"
"veyron.io/veyron/veyron/lib/testutil"
@@ -33,7 +36,7 @@
"veyron.io/veyron/veyron/runtimes/google/lib/publisher"
inaming "veyron.io/veyron/veyron/runtimes/google/naming"
tnaming "veyron.io/veyron/veyron/runtimes/google/testing/mocks/naming"
- vsecurity "veyron.io/veyron/veyron/security"
+ ivtrace "veyron.io/veyron/veyron/runtimes/google/vtrace"
)
func init() {
@@ -41,7 +44,7 @@
}
var (
- errMethod = verror.Abortedf("server returned an error")
+ errMethod = verror.Make(verror.Aborted, nil)
clock = new(fakeClock)
listenSpec = ipc.ListenSpec{Protocol: "tcp", Address: "127.0.0.1:0"}
)
@@ -150,10 +153,9 @@
authorizer = nil
case "aclAuth":
// Only authorize clients matching patterns "client" or "server/...".
- authorizer = vsecurity.NewACLAuthorizer(security.ACL{In: map[security.BlessingPattern]security.LabelSet{
- "server/...": security.LabelSet(security.AdminLabel),
- "client": security.LabelSet(security.AdminLabel),
- }})
+ authorizer = &access.ACL{
+ In: []security.BlessingPattern{"client", "server/..."},
+ }
default:
authorizer = testServerAuthorizer{}
}
@@ -290,11 +292,17 @@
return
}
-func matchesErrorPattern(err error, pattern string) bool {
- if (len(pattern) == 0) != (err == nil) {
- return false
+func matchesErrorPattern(err error, id verror.IDAction, pattern string) bool {
+ if len(pattern) > 0 && err != nil {
+ if strings.Index(err.Error(), pattern) < 0 {
+ fmt.Fprintf(os.Stderr, "got error msg: %q, expected: %q\n", err, pattern)
+ }
}
- return err == nil || strings.Index(err.Error(), pattern) >= 0
+ // TODO(cnicolaou): Move this special case into verror.Is.
+ if reflect.DeepEqual(id, verror.IDAction{}) {
+ return err == nil
+ }
+ return verror.Is(err, id.ID)
}
func TestMultipleCallsToServeAndName(t *testing.T) {
@@ -365,7 +373,7 @@
func TestRPCServerAuthorization(t *testing.T) {
const (
vcErr = "VC handshake failed"
- nameErr = "does not match the provided pattern"
+ nameErr = "do not match pattern"
)
var (
pprovider, pclient, pserver = tsecurity.NewPrincipal("root"), tsecurity.NewPrincipal(), tsecurity.NewPrincipal()
@@ -391,35 +399,39 @@
tests = []struct {
server security.Blessings // blessings presented by the server to the client.
pattern security.BlessingPattern // pattern on the server identity expected by the client.
+ errID verror.IDAction
err string
}{
// Client accepts talking to the server only if the server's blessings match the provided pattern
- {bServer, security.AllPrincipals, ""},
- {bServer, "root/server", ""},
- {bServer, "root/otherserver", nameErr},
- {bServer, "otherroot/server", nameErr},
+ {bServer, security.AllPrincipals, verror.Success, ""},
+ {bServer, "root/server", verror.Success, ""},
+ {bServer, "root/otherserver", verror.NotTrusted, nameErr},
+ {bServer, "otherroot/server", verror.NotTrusted, nameErr},
// and, if the server's blessing has third-party caveats then the server provides
// appropriate discharges.
- {bServerTPValid, security.AllPrincipals, ""},
- {bServerTPValid, "root/serverWithTPCaveats", ""},
- {bServerTPValid, "root/otherserver", nameErr},
- {bServerTPValid, "otherroot/server", nameErr},
+ {bServerTPValid, security.AllPrincipals, verror.Success, ""},
+ {bServerTPValid, "root/serverWithTPCaveats", verror.Success, ""},
+ {bServerTPValid, "root/otherserver", verror.NotTrusted, nameErr},
+ {bServerTPValid, "otherroot/server", verror.NotTrusted, nameErr},
// Client does not talk to a server that presents expired blessings.
- {bServerExpired, security.AllPrincipals, vcErr},
+ {bServerExpired, security.AllPrincipals, verror.NotTrusted, vcErr},
// Client does not talk to a server that fails to provide discharges for
// third-party caveats on the blessings presented by it.
- {bServerTPExpired, security.AllPrincipals, vcErr},
+ {bServerTPExpired, security.AllPrincipals, verror.NotTrusted, vcErr},
}
)
+ ctx := testContextWithoutDeadline()
+
// Start the main server.
- dc, err := InternalNewDischargeClient(mgr, ns, testContext(), vc.LocalPrincipal{pserver})
+ dc, err := InternalNewDischargeClient(mgr, ns, ctx, vc.LocalPrincipal{pserver})
if err != nil {
t.Errorf("InternalNewDischargeClient failed: %v", err)
}
+
_, server := startServer(t, pserver, mgr, ns, serverName, testServerDisp{&testServer{}}, dc)
defer stopServer(t, server, ns, serverName)
@@ -433,7 +445,7 @@
// Set a blessing that the client is willing to share with servers with blessings
// from pprovider.
pclient.BlessingStore().Set(bless(pprovider, pclient, "client"), "root/...")
- for _, test := range tests {
+ for i, test := range tests {
name := fmt.Sprintf("(%q@%q)", test.pattern, test.server)
if err := pserver.BlessingStore().SetDefault(test.server); err != nil {
t.Fatalf("SetDefault failed on server's BlessingStore: %v", err)
@@ -447,8 +459,10 @@
t.Errorf("%s: failed to create client: %v", name, err)
continue
}
- if call, err := client.StartCall(testContext(), fmt.Sprintf("[%s]%s/suffix", test.pattern, serverName), "Method", nil); !matchesErrorPattern(err, test.err) {
- t.Errorf(`%s: client.StartCall: got error "%v", want to match "%v"`, name, err, test.err)
+ dctx, cancel := ctx.WithTimeout(10 * time.Second)
+ call, err := client.StartCall(dctx, fmt.Sprintf("[%s]%s/suffix", test.pattern, serverName), "Method", nil)
+ if !matchesErrorPattern(err, test.errID, test.err) {
+ t.Errorf(`%d: %s: client.StartCall: got error "%v", want to match "%v"`, i, name, err, test.err)
} else if call != nil {
blessings, proof := call.RemoteBlessings()
if proof == nil {
@@ -458,8 +472,8 @@
t.Errorf("%s: %q.MatchedBy(%v) failed", name, test.pattern, blessings)
}
}
+ cancel()
client.Close()
-
}
}
@@ -600,9 +614,8 @@
t.Fatalf(`call.Finish got error "%v"`, err)
}
// Calling Finish a second time should result in a useful error.
- err = call.Finish(&results)
- if got, want := err, verror.BadProtocolf("ipc: multiple calls to Finish not allowed"); got != want {
- t.Fatalf(`call.Finish got error "%v", want "%v"`, got, want)
+ if err = call.Finish(&results); !matchesErrorPattern(err, verror.BadState, "xxx") {
+ t.Fatalf(`got "%v", want "%v"`, err, verror.BadState)
}
}
@@ -625,23 +638,24 @@
tests := []struct {
granter ipc.Granter
+ startErrID, finishErrID verror.IDAction
blessing, starterr, finisherr string
}{
{blessing: "<nil>"},
{granter: granter{b: bless(pclient, pserver, "blessed")}, blessing: "client/blessed"},
- {granter: granter{err: errors.New("hell no")}, starterr: "hell no"},
- {granter: granter{b: pclient.BlessingStore().Default()}, finisherr: "blessing granted not bound to this server"},
+ {granter: granter{err: errors.New("hell no")}, startErrID: verror.NotTrusted, starterr: "hell no"},
+ {granter: granter{b: pclient.BlessingStore().Default()}, finishErrID: verror.NoAccess, finisherr: "blessing granted not bound to this server"},
}
- for _, test := range tests {
+ for i, test := range tests {
call, err := b.client.StartCall(testContext(), "mountpoint/server/suffix", "EchoGrantedBlessings", []interface{}{"argument"}, test.granter)
- if !matchesErrorPattern(err, test.starterr) {
- t.Errorf("%+v: StartCall returned error %v", test, err)
+ if !matchesErrorPattern(err, test.startErrID, test.starterr) {
+ t.Errorf("%d: %+v: StartCall returned error %v", i, test, err)
}
if err != nil {
continue
}
var result, blessing string
- if err = call.Finish(&result, &blessing); !matchesErrorPattern(err, test.finisherr) {
+ if err = call.Finish(&result, &blessing); !matchesErrorPattern(err, test.finishErrID, test.finisherr) {
t.Errorf("%+v: Finish returned error %v", test, err)
}
if err != nil {
@@ -661,53 +675,37 @@
return newCaveat(tpc)
}
-// dischargeImpetusServer implements the discharge service. Always fails to
-// issue a discharge, but records the impetus.
-type dischargeImpetusServer struct {
- mu sync.Mutex
- impetus []security.DischargeImpetus // GUARDED_BY(mu)
+// dischargeTestServer implements the discharge service. Always fails to
+// issue a discharge, but records the impetus and traceid of the RPC call.
+type dischargeTestServer struct {
+ p security.Principal
+ impetus []security.DischargeImpetus
+ traceid []uniqueid.ID
}
-func (s *dischargeImpetusServer) Discharge(ctx ipc.ServerContext, cav vdlutil.Any, impetus security.DischargeImpetus) (vdlutil.Any, error) {
- s.mu.Lock()
- defer s.mu.Unlock()
+func (s *dischargeTestServer) Discharge(ctx ipc.ServerContext, cav vdlutil.Any, impetus security.DischargeImpetus) (vdlutil.Any, error) {
s.impetus = append(s.impetus, impetus)
+ s.traceid = append(s.traceid, ivtrace.FromContext(ctx).Trace().ID())
return nil, fmt.Errorf("discharges not issued")
}
-// TestAndClearImpetus checks if all the recorded impetuses match want.
-// Returns an error if they do not.
-// Error or no error, it clears the set of recorded impetuses.
-func (s *dischargeImpetusServer) TestAndClearImpetus(want security.DischargeImpetus) error {
- s.mu.Lock()
- defer s.mu.Unlock()
- defer func() { s.impetus = nil }()
- for idx, imp := range s.impetus {
- if !reflect.DeepEqual(imp, want) {
- return fmt.Errorf("impetus %d of %d: Got [%v] want [%v]", idx, len(s.impetus), imp, want)
- }
- }
- return nil
+func (s *dischargeTestServer) Release() ([]security.DischargeImpetus, []uniqueid.ID) {
+ impetus, traceid := s.impetus, s.traceid
+ s.impetus, s.traceid = nil, nil
+ return impetus, traceid
}
-func names2patterns(names []string) []security.BlessingPattern {
- ret := make([]security.BlessingPattern, len(names))
- for idx, n := range names {
- ret[idx] = security.BlessingPattern(n)
- }
- return ret
-}
-
-func TestDischargeImpetus(t *testing.T) {
+func TestDischargeImpetusAndContextPropagation(t *testing.T) {
var (
pserver = tsecurity.NewPrincipal("server")
- pdischarger = pserver // In general, the discharger can be a separate principal. In this test, it happens to be the server.
+ pdischarger = tsecurity.NewPrincipal("discharger")
+ pclient = tsecurity.NewPrincipal("client")
sm = imanager.InternalNew(naming.FixedRoutingID(0x555555555))
ns = tnaming.NewSimpleNamespace()
mkClient = func(req security.ThirdPartyRequirements) vc.LocalPrincipal {
- pclient := tsecurity.NewPrincipal()
- tpc, err := security.NewPublicKeyCaveat(pdischarger.PublicKey(), "mountpoint/server/discharger", req, security.UnconstrainedUse())
+ // Setup the client so that it shares a blessing with a third-party caveat with the server.
+ tpc, err := security.NewPublicKeyCaveat(pdischarger.PublicKey(), "mountpoint/discharger", req, security.UnconstrainedUse())
if err != nil {
t.Fatalf("Failed to create ThirdPartyCaveat(%+v): %v", req, err)
}
@@ -715,29 +713,77 @@
if err != nil {
t.Fatal(err)
}
- b, err := pclient.BlessSelf("client", cav)
+ b, err := pclient.BlessSelf("client_for_server", cav)
if err != nil {
t.Fatalf("BlessSelf failed: %v", err)
}
- pclient.AddToRoots(pserver.BlessingStore().Default()) // make the client recognize the server.
pclient.BlessingStore().Set(b, "server")
return vc.LocalPrincipal{pclient}
}
)
- server, err := InternalNewServer(testContext(), sm, ns, nil, vc.LocalPrincipal{pserver})
+ // Initialize the client principal.
+ // It trusts both the application server and the discharger.
+ pclient.AddToRoots(pserver.BlessingStore().Default())
+ pclient.AddToRoots(pdischarger.BlessingStore().Default())
+ // Share a blessing without any third-party caveats with the discharger.
+ // It could share the same blessing as generated by setupClientBlessing, but
+ // that will lead to possibly debugging confusion (since it will try to fetch
+ // a discharge to talk to the discharge service).
+ if b, err := pclient.BlessSelf("client_for_discharger"); err != nil {
+ t.Fatalf("BlessSelf failed: %v", err)
+ } else {
+ pclient.BlessingStore().Set(b, "discharger")
+ }
+
+ // Setup the discharge server.
+ var tester dischargeTestServer
+ dischargeServer, err := InternalNewServer(testContext(), sm, ns, nil, vc.LocalPrincipal{pdischarger})
if err != nil {
t.Fatal(err)
}
- defer server.Stop()
- if _, err := server.Listen(listenSpec); err != nil {
+ defer dischargeServer.Stop()
+ if _, err := dischargeServer.Listen(listenSpec); err != nil {
+ t.Fatal(err)
+ }
+ if err := dischargeServer.Serve("mountpoint/discharger", &tester, &testServerAuthorizer{}); err != nil {
t.Fatal(err)
}
- var tester dischargeImpetusServer
- if err := server.Serve("mountpoint", &tester, testServerAuthorizer{}); err != nil {
- t.Fatal(err)
+ // DischargeClient used to fetch discharges.
+ dc, err := InternalNewDischargeClient(sm, ns, testContext(), vc.LocalPrincipal{pclient})
+ if err != nil {
+ t.Fatalf("InternalDischargeNewClient failed: %v", err)
}
+ // Setup the application server.
+ appServer, err := InternalNewServer(testContext(), sm, ns, nil, vc.LocalPrincipal{pserver})
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer appServer.Stop()
+ ep, err := appServer.Listen(listenSpec)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // TODO(bjornick,cnicolaou,ashankar): This is a hack to workaround the
+ // fact that a single Listen on the "tcp" protocol followed by a call
+ // to Serve(<name>, ...) transparently creates two endpoints (one for
+ // tcp, one for websockets) and maps both to <name> via a mount.
+ // Because all endpoints to a name are tried in a parallel, this
+ // transparency makes this test hard to follow (many discharge fetch
+ // attempts are made - one for VIF authentication, one for VC
+ // authentication and one for the actual RPC - and having them be made
+ // to two different endpoints in parallel leads to a lot of
+ // non-determinism). The last plan of record known by the author of
+ // this comment was to stop this sly creation of two endpoints and
+ // require that they be done explicitly. When that happens, this hack
+ // can go away, but till then, this workaround allows the test to be
+ // more predictable by ensuring there is only one VIF/VC/Flow to the
+ // server.
+ object := naming.JoinAddressName(ep.String(), "object") // instead of "mountpoint/object"
+ if err := appServer.Serve("mountpoint/object", &testServer{}, &testServerAuthorizer{}); err != nil {
+ t.Fatal(err)
+ }
tests := []struct {
Requirements security.ThirdPartyRequirements
Impetus security.DischargeImpetus
@@ -756,24 +802,52 @@
},
}
- for _, test := range tests {
+ for testidx, test := range tests {
pclient := mkClient(test.Requirements)
- dc, err := InternalNewDischargeClient(sm, ns, testContext(), pclient)
- if err != nil {
- t.Fatalf("InternalDischargeNewClient failed: %v", err)
- }
client, err := InternalNewClient(sm, ns, pclient, dc)
if err != nil {
t.Fatalf("InternalNewClient(%+v) failed: %v", test.Requirements, err)
}
defer client.Close()
+ ctx := testContext()
+ tid := ivtrace.FromContext(ctx).Trace().ID()
// StartCall should fetch the discharge, do not worry about finishing the RPC - do not care about that for this test.
- if _, err := client.StartCall(testContext(), "mountpoint/object", "Method", []interface{}{"argument"}); err != nil {
+ if _, err := client.StartCall(ctx, object, "Method", []interface{}{"argument"}); err != nil {
t.Errorf("StartCall(%+v) failed: %v", test.Requirements, err)
continue
}
- if err := tester.TestAndClearImpetus(test.Impetus); err != nil {
- t.Errorf("Test %+v: %v", test.Requirements, err)
+ impetus, traceid := tester.Release()
+ // There should have been 2 or 3 attempts to fetch a discharge
+ // (since the discharge service doesn't actually issue a valid
+ // discharge, there is no re-usable discharge between these attempts):
+ // (1) When creating a VIF with the server hosting the remote object.
+ // (This will happen only for the first test, where the stream.Manager
+ // authenticates at the VIF level for the very first time).
+ // (2) When creating a VC with the server hosting the remote object.
+ // (3) When making the RPC to the remote object.
+ num := 3
+ if testidx > 0 {
+ num = 2
+ }
+ if want := num; len(impetus) != want || len(traceid) != want {
+ t.Errorf("Test %+v: Got (%d, %d) (#impetus, #traceid), wanted %d each", test.Requirements, len(impetus), len(traceid), want)
+ continue
+ }
+ // VC creation does not have any "impetus", it is established without
+ // knowledge of the context of the RPC. So ignore that.
+ //
+ // TODO(ashankar): Should the impetus of the RPC that initiated the
+ // VIF/VC creation be propagated?
+ if got, want := impetus[len(impetus)-1], test.Impetus; !reflect.DeepEqual(got, want) {
+ t.Errorf("Test %+v: Got impetus %v, want %v", test.Requirements, got, want)
+ }
+ // But the context used for all of this should be the same
+ // (thereby allowing debug traces to link VIF/VC creation with
+ // the RPC that initiated them).
+ for idx, got := range traceid {
+ if !reflect.DeepEqual(got, tid) {
+ t.Errorf("Test %+v: %d - Got trace id %q, want %q", test.Requirements, idx, hex.EncodeToString(got[:]), hex.EncodeToString(tid[:]))
+ }
}
}
}
@@ -911,8 +985,8 @@
t.Errorf(`%s call.Finish got error: "%v", wanted the RPC to succeed`, name, err)
} else if err == nil && !test.authorized {
t.Errorf("%s call.Finish succeeded, expected authorization failure", name)
- } else if !test.authorized && !verror.Is(err, verror.NoAccess) {
- t.Errorf("%s. call.Finish returned error %v(%v), wanted %v", name, verror.Convert(err).ErrorID(), err, verror.NoAccess)
+ } else if !test.authorized && !verror.Is(err, verror.NoAccess.ID) {
+ t.Errorf("%s. call.Finish returned error %v(%v), wanted %v", name, verror.Convert(verror.NoAccess, nil, err).ErrorID(), err, verror.NoAccess)
}
}
}
@@ -940,17 +1014,17 @@
if b.client, err = InternalNewClient(b.sm, b.ns, vc.LocalPrincipal{pclient}, dc); err != nil {
t.Fatalf("InternalNewClient failed: %v", err)
}
- call := func() error {
+ call := func() verror.E {
call, err := b.client.StartCall(testContext(), "mountpoint/server/aclAuth", "Echo", []interface{}{"batman"})
if err != nil {
- return fmt.Errorf("client.StartCall failed: %v", err)
+ return err.(verror.E) //fmt.Errorf("client.StartCall failed: %v", err)
}
var got string
if err := call.Finish(&got); err != nil {
- return fmt.Errorf("client.Finish failed: %v", err)
+ return err.(verror.E) //fmt.Errorf("client.Finish failed: %v", err)
}
if want := `method:"Echo",suffix:"aclAuth",arg:"batman"`; got != want {
- return fmt.Errorf("Got [%v] want [%v]", got, want)
+ return verror.Convert(verror.BadArg, nil, fmt.Errorf("Got [%v] want [%v]", got, want))
}
return nil
}
@@ -961,7 +1035,7 @@
}
// Advance virtual clock, which will invalidate the discharge
clock.Advance(1)
- if err, want := call(), "not authorized"; !matchesErrorPattern(err, want) {
+ if err, want := call(), "not authorized"; !matchesErrorPattern(err, verror.NoAccess, want) {
t.Errorf("Got error [%v] wanted to match pattern %q", err, want)
}
// But retrying will succeed since the discharge should be purged from cache and refreshed
@@ -1218,7 +1292,7 @@
t.Fatalf("InternalNewClient failed: %v", err)
}
// When using VCSecurityNone, all authorization checks should be skipped, so
- // unauthorized methods shoudl be callable.
+ // unauthorized methods should be callable.
call, err := client.StartCall(testContext(), "mp/server", "Unauthorized", nil)
if err != nil {
t.Fatalf("client.StartCall failed: %v", err)
@@ -1245,8 +1319,8 @@
if call != nil {
t.Errorf("Expected nil interface got: %#v", call)
}
- if !verror.Is(err, verror.BadArg) {
- t.Errorf("Expected a BadArg error, got: %s", err.Error())
+ if !verror.Is(err, verror.BadArg.ID) {
+ t.Errorf("Expected an BadArg error, got: %s", err.Error())
}
}
@@ -1362,7 +1436,176 @@
}
}
+type mockDischarger struct {
+ mu sync.Mutex
+ called bool
+}
+
+func (m *mockDischarger) Discharge(ctx ipc.ServerContext, caveatAny vdlutil.Any, _ security.DischargeImpetus) (vdlutil.Any, error) {
+ m.mu.Lock()
+ m.called = true
+ m.mu.Unlock()
+ caveat, ok := caveatAny.(security.ThirdPartyCaveat)
+ if !ok {
+ return nil, fmt.Errorf("type %T does not implement security.ThirdPartyCaveat", caveatAny)
+ }
+ return ctx.LocalPrincipal().MintDischarge(caveat, security.UnconstrainedUse())
+}
+
+func TestNoDischargesOpt(t *testing.T) {
+ var (
+ pdischarger = tsecurity.NewPrincipal("discharger")
+ pserver = tsecurity.NewPrincipal("server")
+ pclient = tsecurity.NewPrincipal("client")
+ )
+ // Make the client recognize all server blessings
+ if err := pclient.AddToRoots(pserver.BlessingStore().Default()); err != nil {
+ t.Fatal(err)
+ }
+ if err := pclient.AddToRoots(pdischarger.BlessingStore().Default()); err != nil {
+ t.Fatal(err)
+ }
+
+ // Bless the client with a ThirdPartyCaveat.
+ tpcav := mkThirdPartyCaveat(pdischarger.PublicKey(), "mountpoint/discharger", mkCaveat(security.ExpiryCaveat(time.Now().Add(time.Hour))))
+ blessings, err := pserver.Bless(pclient.PublicKey(), pserver.BlessingStore().Default(), "tpcav", tpcav)
+ if err != nil {
+ t.Fatalf("failed to create Blessings: %v", err)
+ }
+ if _, err = pclient.BlessingStore().Set(blessings, "server"); err != nil {
+ t.Fatalf("failed to set blessings: %v", err)
+ }
+
+ ns := tnaming.NewSimpleNamespace()
+ runServer := func(name string, obj interface{}, principal security.Principal) stream.Manager {
+ rid, err := naming.NewRoutingID()
+ if err != nil {
+ t.Fatal(err)
+ }
+ sm := imanager.InternalNew(rid)
+ server, err := InternalNewServer(testContext(), sm, ns, nil, vc.LocalPrincipal{principal})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if _, err := server.Listen(listenSpec); err != nil {
+ t.Fatal(err)
+ }
+ if err := server.Serve(name, obj, acceptAllAuthorizer{}); err != nil {
+ t.Fatal(err)
+ }
+ return sm
+ }
+
+ // Setup the disharger and test server.
+ discharger := &mockDischarger{}
+ defer runServer("mountpoint/discharger", discharger, pdischarger).Shutdown()
+ defer runServer("mountpoint/testServer", &testServer{}, pserver).Shutdown()
+
+ runClient := func(noDischarges bool) {
+ rid, err := naming.NewRoutingID()
+ if err != nil {
+ t.Fatal(err)
+ }
+ smc := imanager.InternalNew(rid)
+ defer smc.Shutdown()
+ dc, err := InternalNewDischargeClient(smc, ns, testContext())
+ if err != nil {
+ t.Fatal(err)
+ }
+ client, err := InternalNewClient(smc, ns, vc.LocalPrincipal{pclient}, dc)
+ if err != nil {
+ t.Fatalf("failed to create client: %v", err)
+ }
+ defer client.Close()
+ var opts []ipc.CallOpt
+ if noDischarges {
+ opts = append(opts, vc.NoDischarges{})
+ }
+ if _, err = client.StartCall(testContext(), "mountpoint/testServer", "Closure", nil, opts...); err != nil {
+ t.Fatalf("failed to StartCall: %v", err)
+ }
+ }
+
+ // Test that when the NoDischarges option is set, mockDischarger does not get called.
+ if runClient(true); discharger.called {
+ t.Errorf("did not expect discharger to be called")
+ }
+ discharger.called = false
+ // Test that when the Nodischarges option is not set, mockDischarger does get called.
+ if runClient(false); !discharger.called {
+ t.Errorf("expected discharger to be called")
+ }
+}
+
+func TestNoImplicitDischargeFetching(t *testing.T) {
+ // This test ensures that discharge clients only fetch discharges for the specified tp caveats and not its own.
+ var (
+ pdischarger1 = tsecurity.NewPrincipal("discharger1")
+ pdischarger2 = tsecurity.NewPrincipal("discharger2")
+ pdischargeClient = tsecurity.NewPrincipal("dischargeClient")
+ )
+
+ // Bless the client with a ThirdPartyCaveat from discharger1.
+ tpcav1 := mkThirdPartyCaveat(pdischarger1.PublicKey(), "mountpoint/discharger1", mkCaveat(security.ExpiryCaveat(time.Now().Add(time.Hour))))
+ blessings, err := pdischarger1.Bless(pdischargeClient.PublicKey(), pdischarger1.BlessingStore().Default(), "tpcav1", tpcav1)
+ if err != nil {
+ t.Fatalf("failed to create Blessings: %v", err)
+ }
+ if err = pdischargeClient.BlessingStore().SetDefault(blessings); err != nil {
+ t.Fatalf("failed to set blessings: %v", err)
+ }
+
+ ns := tnaming.NewSimpleNamespace()
+ runServer := func(name string, obj interface{}, principal security.Principal) stream.Manager {
+ rid, err := naming.NewRoutingID()
+ if err != nil {
+ t.Fatal(err)
+ }
+ sm := imanager.InternalNew(rid)
+ server, err := InternalNewServer(testContext(), sm, ns, nil, vc.LocalPrincipal{principal})
+ if err != nil {
+ t.Fatal(err)
+ }
+ if _, err := server.Listen(listenSpec); err != nil {
+ t.Fatal(err)
+ }
+ if err := server.Serve(name, obj, acceptAllAuthorizer{}); err != nil {
+ t.Fatal(err)
+ }
+ return sm
+ }
+
+ // Setup the disharger and test server.
+ discharger1 := &mockDischarger{}
+ discharger2 := &mockDischarger{}
+ defer runServer("mountpoint/discharger1", discharger1, pdischarger1).Shutdown()
+ defer runServer("mountpoint/discharger2", discharger2, pdischarger2).Shutdown()
+
+ rid, err := naming.NewRoutingID()
+ if err != nil {
+ t.Fatal(err)
+ }
+ sm := imanager.InternalNew(rid)
+ dc, err := InternalNewDischargeClient(sm, ns, testContext(), vc.LocalPrincipal{pdischargeClient})
+ if err != nil {
+ t.Fatal(err)
+ }
+ tpcav2, err := security.NewPublicKeyCaveat(pdischarger2.PublicKey(), "mountpoint/discharger2", security.ThirdPartyRequirements{}, mkCaveat(security.ExpiryCaveat(time.Now().Add(time.Hour))))
+ if err != nil {
+ t.Error(err)
+ }
+ dc.PrepareDischarges(testContext(), []security.ThirdPartyCaveat{tpcav2}, security.DischargeImpetus{})
+
+ // Ensure that discharger1 was not called and discharger2 was called.
+ if discharger1.called {
+ t.Errorf("discharge for caveat on discharge client should not have been fetched.")
+ }
+ if !discharger2.called {
+ t.Errorf("discharge for caveat passed to PrepareDischarges should have been fetched.")
+ }
+}
+
func init() {
testutil.Init()
- vom.Register(fakeTimeCaveat(0))
+ vdlutil.Register(fakeTimeCaveat(0))
}
diff --git a/runtimes/google/ipc/glob.go b/runtimes/google/ipc/glob.go
index 9dba1e4..33c897c 100644
--- a/runtimes/google/ipc/glob.go
+++ b/runtimes/google/ipc/glob.go
@@ -8,6 +8,7 @@
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/services/security/access"
"veyron.io/veyron/veyron2/verror"
"veyron.io/veyron/veyron2/vlog"
@@ -145,23 +146,23 @@
// response if the double underscore is explicitly part of the pattern, e.g.
// "".Glob("__*/*"), or "".Glob("__debug/...").
//
-// Service objects may choose to implement either VAllGlobber or
-// VChildrenGlobber. VAllGlobber is more flexible, but VChildrenGlobber is
-// simpler to implement and less prone to errors.
+// Service objects may choose to implement either AllGlobber or ChildrenGlobber.
+// AllGlobber is more flexible, but ChildrenGlobber is simpler to implement and
+// less prone to errors.
//
-// If objects implement VAllGlobber, it must be able to handle recursive pattern
+// If objects implement AllGlobber, it must be able to handle recursive pattern
// for the entire namespace below the receiver object, i.e. "a/b".Glob("...")
// must return the name of all the objects under "a/b".
//
-// If they implement VChildrenGlobber, it provides a list of the receiver's
+// If they implement ChildrenGlobber, it provides a list of the receiver's
// immediate children names, or a non-nil error if the receiver doesn't exist.
//
// globInternal constructs the Glob response by internally accessing the
-// VAllGlobber or VChildrenGlobber interface of objects as many times as needed.
+// AllGlobber or ChildrenGlobber interface of objects as many times as needed.
//
// Before accessing an object, globInternal ensures that the requester is
-// authorized to access it. Internal objects require either security.DebugLabel
-// or security.MonitoringLabel. Service objects require security.ResolveLabel.
+// authorized to access it. Internal objects require access.Debug. Service
+// objects require access.Resolve.
type globInternal struct {
dispNormal ipc.Dispatcher
dispReserved ipc.Dispatcher
@@ -182,75 +183,95 @@
}
disp := i.dispNormal
call.M.Method = ipc.GlobMethod
- call.M.MethodTags = []interface{}{security.ResolveLabel}
+ call.M.MethodTags = []interface{}{access.Resolve}
if naming.IsReserved(i.receiver) || (i.receiver == "" && naming.IsReserved(pattern)) {
disp = i.dispReserved
- call.M.MethodTags = []interface{}{security.DebugLabel | security.MonitoringLabel}
+ call.M.MethodTags = []interface{}{access.Debug}
}
if disp == nil {
return verror.NoExistf("ipc: Glob is not implemented by %q", i.receiver)
}
- return i.globStep(call, disp, "", g, 0)
-}
-func (i *globInternal) globStep(call *mutableCall, disp ipc.Dispatcher, name string, g *glob.Glob, depth int) error {
- call.M.Suffix = naming.Join(i.receiver, name)
- if depth > maxRecursiveGlobDepth {
- err := verror.Internalf("ipc: Glob exceeded its recursion limit (%d): %q", maxRecursiveGlobDepth, call.Suffix())
- vlog.Error(err)
- return err
+ type gState struct {
+ name string
+ glob *glob.Glob
+ depth int
}
- obj, auth, err := disp.Lookup(call.Suffix())
- switch {
- case err != nil:
- return err
- case obj == nil:
- return verror.NoExistf("ipc: invoker not found for %q.%s", call.Suffix(), ipc.GlobMethod)
- }
+ queue := []gState{gState{glob: g}}
- // Verify that that requester is authorized for the current object.
- if err := authorize(call, auth); err != nil {
- return err
- }
-
- // If the object implements both VAllGlobber and VChildrenGlobber, we'll
- // use VAllGlobber.
- gs := objectToInvoker(obj).VGlob()
- if gs == nil || (gs.VAllGlobber == nil && gs.VChildrenGlobber == nil) {
- if g.Len() == 0 {
- call.Send(naming.VDLMountEntry{Name: name})
+ for len(queue) != 0 {
+ select {
+ case <-call.Done():
+ // RPC timed out or was canceled.
+ return nil
+ default:
}
- return nil
- }
- if gs.VAllGlobber != nil {
- vlog.VI(3).Infof("ipc Glob: %q implements VAllGlobber", call.Suffix())
- childCtx := &ipc.GlobContextStub{&localServerCall{call, name}}
- return gs.VAllGlobber.Glob(childCtx, g.String())
- }
- vlog.VI(3).Infof("ipc Glob: %q implements VChildrenGlobber", call.Suffix())
- children, err := gs.VChildrenGlobber.VGlobChildren()
- if err != nil {
- return nil
- }
- if g.Len() == 0 {
- call.Send(naming.VDLMountEntry{Name: name})
- }
- if g.Finished() {
- return nil
- }
- if g.Len() == 0 {
- // This is a recursive pattern. Make sure we don't recurse forever.
- depth++
- }
- for _, child := range children {
- if len(child) == 0 || strings.Contains(child, "/") {
- vlog.Errorf("ipc: %q.VGlobChildren() returned an invalid child name: %q", call.Suffix(), child)
+ state := queue[0]
+ queue = queue[1:]
+
+ call.M.Suffix = naming.Join(i.receiver, state.name)
+ if state.depth > maxRecursiveGlobDepth {
+ vlog.Errorf("ipc Glob: exceeded recursion limit (%d): %q", maxRecursiveGlobDepth, call.Suffix())
continue
}
- if ok, _, left := g.MatchInitialSegment(child); ok {
- next := naming.Join(name, child)
- if err := i.globStep(call, disp, next, left, depth); err != nil {
- vlog.VI(1).Infof("ipc Glob: globStep(%q, %q): %v", next, left, err)
+ obj, auth, err := disp.Lookup(call.Suffix())
+ if err != nil {
+ vlog.VI(3).Infof("ipc Glob: Lookup failed for %q: %v", call.Suffix(), err)
+ continue
+ }
+ if obj == nil {
+ vlog.VI(3).Infof("ipc Glob: object not found for %q", call.Suffix())
+ continue
+ }
+
+ // Verify that that requester is authorized for the current object.
+ if err := authorize(call, auth); err != nil {
+ vlog.VI(3).Infof("ipc Glob: client is not authorized for %q: %v", call.Suffix(), err)
+ continue
+ }
+
+ // If the object implements both AllGlobber and ChildrenGlobber, we'll
+ // use AllGlobber.
+ gs := objectToInvoker(obj).VGlob()
+ if gs == nil || (gs.AllGlobber == nil && gs.ChildrenGlobber == nil) {
+ if state.glob.Len() == 0 {
+ call.Send(naming.VDLMountEntry{Name: state.name})
+ }
+ continue
+ }
+ if gs.AllGlobber != nil {
+ vlog.VI(3).Infof("ipc Glob: %q implements AllGlobber", call.Suffix())
+ childCtx := &ipc.GlobContextStub{&localServerCall{call, state.name}}
+ gs.AllGlobber.Glob(childCtx, state.glob.String())
+ continue
+ }
+ vlog.VI(3).Infof("ipc Glob: %q implements ChildrenGlobber", call.Suffix())
+ children, err := gs.ChildrenGlobber.GlobChildren__()
+ // The requested object doesn't exist.
+ if err != nil {
+ continue
+ }
+ // The glob pattern matches the current object.
+ if state.glob.Len() == 0 {
+ call.Send(naming.VDLMountEntry{Name: state.name})
+ }
+ // The current object has no children.
+ if children == nil {
+ continue
+ }
+ depth := state.depth
+ // This is a recursive pattern. Make sure we don't recurse forever.
+ if state.glob.Len() == 0 {
+ depth++
+ }
+ for child := range children {
+ if len(child) == 0 || strings.Contains(child, "/") {
+ vlog.Errorf("ipc Glob: %q.GlobChildren__() sent an invalid child name: %q", call.Suffix(), child)
+ continue
+ }
+ if ok, _, left := state.glob.MatchInitialSegment(child); ok {
+ next := naming.Join(state.name, child)
+ queue = append(queue, gState{next, left, depth})
}
}
}
@@ -308,14 +329,11 @@
}
}
-func (c *mutableContext) Timestamp() time.Time { return c.M.Timestamp }
-func (c *mutableContext) Method() string { return c.M.Method }
-func (c *mutableContext) MethodTags() []interface{} { return c.M.MethodTags }
-func (c *mutableContext) Name() string { return c.M.Suffix }
-func (c *mutableContext) Suffix() string { return c.M.Suffix }
-func (c *mutableContext) Label() security.Label {
- return security.LabelFromMethodTags(c.M.MethodTags)
-}
+func (c *mutableContext) Timestamp() time.Time { return c.M.Timestamp }
+func (c *mutableContext) Method() string { return c.M.Method }
+func (c *mutableContext) MethodTags() []interface{} { return c.M.MethodTags }
+func (c *mutableContext) Name() string { return c.M.Suffix }
+func (c *mutableContext) Suffix() string { return c.M.Suffix }
func (c *mutableContext) LocalPrincipal() security.Principal { return c.M.LocalPrincipal }
func (c *mutableContext) LocalBlessings() security.Blessings { return c.M.LocalBlessings }
func (c *mutableContext) RemoteBlessings() security.Blessings { return c.M.RemoteBlessings }
diff --git a/runtimes/google/ipc/glob_test.go b/runtimes/google/ipc/glob_test.go
index 5d497e8..79e9859 100644
--- a/runtimes/google/ipc/glob_test.go
+++ b/runtimes/google/ipc/glob_test.go
@@ -34,7 +34,10 @@
}
func TestGlob(t *testing.T) {
- runtime := rt.Init()
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
defer runtime.Cleanup()
namespace := []string{
@@ -158,7 +161,7 @@
}
for _, tc := range testcases {
name := naming.JoinAddressName(ep, tc.name)
- results, err := testutil.GlobName(name, tc.pattern)
+ results, err := testutil.GlobName(runtime.NewContext(), name, tc.pattern)
if err != nil {
t.Errorf("unexpected Glob error for (%q, %q): %v", tc.name, tc.pattern, err)
continue
@@ -177,7 +180,7 @@
elems := strings.Split(suffix, "/")
if len(elems) != 0 && elems[0] == "muah" {
// Infinite space. Each node has one child named "ha".
- return ipc.VChildrenGlobberInvoker("ha"), nil, nil
+ return ipc.ChildrenGlobberInvoker("ha"), nil, nil
}
if len(elems) != 0 && elems[0] == "leaf" {
@@ -226,18 +229,17 @@
suffix []string
}
-func (o *vChildrenObject) VGlobChildren() ([]string, error) {
+func (o *vChildrenObject) GlobChildren__() (<-chan string, error) {
n := o.n.find(o.suffix, false)
if n == nil {
return nil, fmt.Errorf("object does not exist")
}
- children := make([]string, len(n.children))
- index := 0
+ ch := make(chan string, len(n.children))
for child, _ := range n.children {
- children[index] = child
- index++
+ ch <- child
}
- return children, nil
+ close(ch)
+ return ch, nil
}
type node struct {
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index abd5986..ca392b5 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -16,9 +16,11 @@
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/options"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/services/security/access"
"veyron.io/veyron/veyron2/verror"
"veyron.io/veyron/veyron2/vlog"
"veyron.io/veyron/veyron2/vom"
+ "veyron.io/veyron/veyron2/vom2"
"veyron.io/veyron/veyron2/vtrace"
"veyron.io/veyron/veyron/lib/netstate"
@@ -35,18 +37,19 @@
type server struct {
sync.Mutex
- ctx context.T // context used by the server to make internal RPCs.
- streamMgr stream.Manager // stream manager to listen for new flows.
- publisher publisher.Publisher // publisher to publish mounttable mounts.
- listenerOpts []stream.ListenerOpt // listener opts passed to Listen.
- listeners map[stream.Listener]*dhcpListener // listeners created by Listen.
- disp ipc.Dispatcher // dispatcher to serve RPCs
- dispReserved ipc.Dispatcher // dispatcher for reserved methods
- active sync.WaitGroup // active goroutines we've spawned.
- stopped bool // whether the server has been stopped.
- stoppedChan chan struct{} // closed when the server has been stopped.
- ns naming.Namespace
- servesMountTable bool
+ ctx context.T // context used by the server to make internal RPCs.
+ streamMgr stream.Manager // stream manager to listen for new flows.
+ publisher publisher.Publisher // publisher to publish mounttable mounts.
+ listenerOpts []stream.ListenerOpt // listener opts passed to Listen.
+ listeners map[stream.Listener]*dhcpListener // listeners created by Listen.
+ disp ipc.Dispatcher // dispatcher to serve RPCs
+ dispReserved ipc.Dispatcher // dispatcher for reserved methods
+ active sync.WaitGroup // active goroutines we've spawned.
+ stopped bool // whether the server has been stopped.
+ stoppedChan chan struct{} // closed when the server has been stopped.
+ preferredProtocols []string // protocols to use when resolving proxy name to endpoint.
+ ns naming.Namespace
+ servesMountTable bool
// TODO(cnicolaou): remove this when the publisher tracks published names
// and can return an appropriate error for RemoveName on a name that
// wasn't 'Added' for this server.
@@ -68,6 +71,12 @@
ch chan config.Setting // channel to receive settings over
}
+// This option is used to sort and filter the endpoints when resolving the
+// proxy name from a mounttable.
+type PreferredServerResolveProtocols []string
+
+func (PreferredServerResolveProtocols) IPCServerOpt() {}
+
func InternalNewServer(ctx context.T, streamMgr stream.Manager, ns naming.Namespace, store *ivtrace.Store, opts ...ipc.ServerOpt) (ipc.Server, error) {
ctx, _ = ivtrace.WithNewSpan(ctx, "NewServer")
statsPrefix := naming.Join("ipc", "server", "routing-id", streamMgr.RoutingID().String())
@@ -100,6 +109,8 @@
s.servesMountTable = bool(opt)
case options.ReservedNameDispatcher:
s.dispReserved = opt.Dispatcher
+ case PreferredServerResolveProtocols:
+ s.preferredProtocols = []string(opt)
}
}
blessingsStatsName := naming.Join(statsPrefix, "security", "blessings")
@@ -140,7 +151,12 @@
} else {
names = append(names, address)
}
- for _, n := range names {
+ // An empty set of protocols means all protocols...
+ ordered, err := filterAndOrderServers(names, s.preferredProtocols)
+ if err != nil {
+ return "", err
+ }
+ for _, n := range ordered {
address, suffix := naming.SplitAddressName(n)
if suffix != "" {
continue
@@ -417,12 +433,17 @@
}
calls.Add(1)
go func(flow stream.Flow) {
- if err := newFlowServer(flow, s).serve(); err != nil {
+ defer calls.Done()
+ fs, err := newFlowServer(flow, s)
+ if err != nil {
+ vlog.Errorf("newFlowServer on %v failed: %v", ln, err)
+ return
+ }
+ if err := fs.serve(); err != nil {
// TODO(caprita): Logging errors here is too spammy. For example, "not
// authorized" errors shouldn't be logged as server errors.
vlog.Errorf("Flow serve on %v failed: %v", ln, err)
}
- calls.Done()
}(flow)
}
}
@@ -612,14 +633,23 @@
return firstErr
}
+// TODO(toddw): Remove these interfaces after the vom2 transition.
+type vomEncoder interface {
+ Encode(v interface{}) error
+}
+
+type vomDecoder interface {
+ Decode(v interface{}) error
+}
+
// flowServer implements the RPC server-side protocol for a single RPC, over a
// flow that's already connected to the client.
type flowServer struct {
context.T
server *server // ipc.Server that this flow server belongs to
disp ipc.Dispatcher // ipc.Dispatcher that will serve RPCs on this flow
- dec *vom.Decoder // to decode requests and args from the client
- enc *vom.Encoder // to encode responses and results to the client
+ dec vomDecoder // to decode requests and args from the client
+ enc vomEncoder // to encode responses and results to the client
flow stream.Flow // underlying flow
// Fields filled in during the server invocation.
@@ -634,21 +664,33 @@
var _ ipc.Stream = (*flowServer)(nil)
-func newFlowServer(flow stream.Flow, server *server) *flowServer {
+func newFlowServer(flow stream.Flow, server *server) (*flowServer, error) {
server.Lock()
disp := server.disp
server.Unlock()
- return &flowServer{
- T: server.ctx,
- server: server,
- disp: disp,
- // TODO(toddw): Support different codecs
- dec: vom.NewDecoder(flow),
- enc: vom.NewEncoder(flow),
+ fs := &flowServer{
+ T: server.ctx,
+ server: server,
+ disp: disp,
flow: flow,
discharges: make(map[string]security.Discharge),
}
+ if vom2.IsEnabled() {
+ var err error
+ if fs.dec, err = vom2.NewDecoder(flow); err != nil {
+ flow.Close()
+ return nil, err
+ }
+ if fs.enc, err = vom2.NewBinaryEncoder(flow); err != nil {
+ flow.Close()
+ return nil, err
+ }
+ } else {
+ fs.dec = vom.NewDecoder(flow)
+ fs.enc = vom.NewEncoder(flow)
+ }
+ return fs, nil
}
// Vom does not encode untyped nils.
@@ -662,7 +704,7 @@
// - Server methods return 0 or more results
// - Any values returned by the server that have an interface type are either
// non-nil or of type error.
-func result2vom(res interface{}) vom.Value {
+func vomErrorHack(res interface{}) vom.Value {
v := vom.ValueOf(res)
if !v.IsValid() {
// Untyped nils are assumed to be nil-errors.
@@ -678,6 +720,22 @@
return v
}
+// TODO(toddw): Remove this function and encodeValueHack after the vom2 transition.
+func vom2ErrorHack(res interface{}) interface{} {
+ if err, ok := res.(error); ok {
+ return &err
+ }
+ return res
+}
+
+// TODO(toddw): Remove this function and vom2ErrorHack after the vom2 transition.
+func (fs *flowServer) encodeValueHack(res interface{}) error {
+ if vom2.IsEnabled() {
+ return fs.enc.Encode(vom2ErrorHack(res))
+ }
+ return fs.enc.(*vom.Encoder).EncodeValue(vomErrorHack(res))
+}
+
func (fs *flowServer) serve() error {
defer fs.flow.Close()
@@ -704,7 +762,7 @@
return response.Error
}
for ix, res := range results {
- if err := fs.enc.EncodeValue(result2vom(res)); err != nil {
+ if err := fs.encodeValueHack(res); err != nil {
return verror.BadProtocolf("ipc: result #%d [%T=%v] encoding failed: %v", ix, res, res, err)
}
}
@@ -796,7 +854,7 @@
return nil, verr
}
// Check if the caller is permitted to view debug information.
- // TODO(mattr): Is DebugLabel the right thing to check?
+ // TODO(mattr): Is access.Debug the right thing to check?
fs.allowDebug = authorize(debugContext{fs}, auth) == nil
// Invoke the method.
results, err := invoker.Invoke(fs.method, fs, argptrs)
@@ -873,7 +931,7 @@
// this - should servers be able to assume that a blessing is something that
// does not have the authorizations that the server's own identity has?
if blessings != nil && !reflect.DeepEqual(blessings.PublicKey(), fs.flow.LocalPrincipal().PublicKey()) {
- return verror.BadProtocolf("ipc: blessing granted not bound to this server(%v vs %v)", blessings.PublicKey(), fs.flow.LocalPrincipal().PublicKey())
+ return verror.NoAccessf("ipc: blessing granted not bound to this server(%v vs %v)", blessings.PublicKey(), fs.flow.LocalPrincipal().PublicKey())
}
// Receive third party caveat discharges the client sent
for i := uint64(0); i < req.NumDischarges; i++ {
@@ -909,12 +967,14 @@
}
// debugContext is a context which wraps another context but always returns
-// the debug label.
+// the debug tag.
type debugContext struct {
security.Context
}
-func (debugContext) Label() security.Label { return security.DebugLabel }
+func (debugContext) MethodTags() []interface{} {
+ return []interface{}{access.Debug}
+}
// Send implements the ipc.Stream method.
func (fs *flowServer) Send(item interface{}) error {
@@ -973,10 +1033,6 @@
//nologcall
return fs.suffix
}
-func (fs *flowServer) Label() security.Label {
- //nologcall
- return security.LabelFromMethodTags(fs.tags)
-}
func (fs *flowServer) LocalPrincipal() security.Principal {
//nologcall
return fs.flow.LocalPrincipal()
diff --git a/runtimes/google/ipc/server_test.go b/runtimes/google/ipc/server_test.go
index a918f00..8e43cca 100644
--- a/runtimes/google/ipc/server_test.go
+++ b/runtimes/google/ipc/server_test.go
@@ -29,7 +29,10 @@
func TestReconnect(t *testing.T) {
b := createBundle(t, tsecurity.NewPrincipal("client"), nil, nil) // We only need the client from the bundle.
defer b.cleanup(t)
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(os.Stderr, os.Stderr)
server, err := sh.Start("runServer", nil, "127.0.0.1:0")
if err != nil {
@@ -86,7 +89,10 @@
}
func (h *proxyHandle) Start(t *testing.T) error {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
server, err := sh.Start("runProxy", nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
diff --git a/runtimes/google/ipc/sort_internal_test.go b/runtimes/google/ipc/sort_internal_test.go
index c234557..1995c16 100644
--- a/runtimes/google/ipc/sort_internal_test.go
+++ b/runtimes/google/ipc/sort_internal_test.go
@@ -73,10 +73,10 @@
// Just foobar and tcp4
want := []string{
- "/@2@foobar@127.0.0.10@00000000000000000000000000000000@@@@",
- "/@2@foobar@127.0.0.11@00000000000000000000000000000000@@@@",
- "/@2@tcp4@127.0.0.1@00000000000000000000000000000000@@@@",
- "/@2@tcp4@127.0.0.2@00000000000000000000000000000000@@@@",
+ "/@3@foobar@127.0.0.10@00000000000000000000000000000000@@@m@@",
+ "/@3@foobar@127.0.0.11@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp4@127.0.0.1@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp4@127.0.0.2@00000000000000000000000000000000@@@m@@",
}
if !reflect.DeepEqual(got, want) {
t.Errorf("got: %v, want %v", got, want)
@@ -88,10 +88,10 @@
}
// tcp or tcp4
want = []string{
- "/@2@tcp4@127.0.0.1@00000000000000000000000000000000@@@@",
- "/@2@tcp4@127.0.0.2@00000000000000000000000000000000@@@@",
- "/@2@tcp@127.0.0.3@00000000000000000000000000000000@@@@",
- "/@2@tcp@127.0.0.4@00000000000000000000000000000000@@@@",
+ "/@3@tcp4@127.0.0.1@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp4@127.0.0.2@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp@127.0.0.3@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp@127.0.0.4@00000000000000000000000000000000@@@m@@",
}
// Everything, since we didn't specify a protocol
@@ -103,14 +103,14 @@
// original ordering within each protocol, with protocols that
// are not in the default ordering list at the end.
want = []string{
- "/@2@tcp@127.0.0.3@00000000000000000000000000000000@@@@",
- "/@2@tcp@127.0.0.4@00000000000000000000000000000000@@@@",
- "/@2@tcp4@127.0.0.1@00000000000000000000000000000000@@@@",
- "/@2@tcp4@127.0.0.2@00000000000000000000000000000000@@@@",
- "/@2@tcp6@127.0.0.7@00000000000000000000000000000000@@@@",
- "/@2@tcp6@127.0.0.8@00000000000000000000000000000000@@@@",
- "/@2@foobar@127.0.0.10@00000000000000000000000000000000@@@@",
- "/@2@foobar@127.0.0.11@00000000000000000000000000000000@@@@",
+ "/@3@tcp@127.0.0.3@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp@127.0.0.4@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp4@127.0.0.1@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp4@127.0.0.2@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp6@127.0.0.7@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp6@127.0.0.8@00000000000000000000000000000000@@@m@@",
+ "/@3@foobar@127.0.0.10@00000000000000000000000000000000@@@m@@",
+ "/@3@foobar@127.0.0.11@00000000000000000000000000000000@@@m@@",
}
if !reflect.DeepEqual(got, want) {
t.Errorf("got: %v, want %v", got, want)
@@ -132,13 +132,13 @@
t.Fatalf("unexpected error: %s", err)
}
want = []string{
- "/@2@tcp@127.0.0.3@00000000000000000000000000000000@@@@",
- "/@2@tcp@127.0.0.1@00000000000000000000000000000000@@@@",
- "/@2@tcp@74.125.69.139@00000000000000000000000000000000@@@@",
- "/@2@tcp@192.168.1.10@00000000000000000000000000000000@@@@",
- "/@2@tcp@74.125.142.83@00000000000000000000000000000000@@@@",
- "/@2@foobar@127.0.0.10@00000000000000000000000000000000@@@@",
- "/@2@foobar@127.0.0.11@00000000000000000000000000000000@@@@",
+ "/@3@tcp@127.0.0.3@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp@127.0.0.1@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp@74.125.69.139@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp@192.168.1.10@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp@74.125.142.83@00000000000000000000000000000000@@@m@@",
+ "/@3@foobar@127.0.0.10@00000000000000000000000000000000@@@m@@",
+ "/@3@foobar@127.0.0.11@00000000000000000000000000000000@@@m@@",
}
if !reflect.DeepEqual(got, want) {
t.Errorf("got: %v, want %v", got, want)
@@ -156,11 +156,11 @@
t.Fatalf("unexpected error: %s", err)
}
want := []string{
- "/@2@tcp@127.0.0.3@00000000000000000000000000000000@@@@",
- "/@2@tcp@127.0.0.1@00000000000000000000000000000000@@@@",
- "/@2@tcp@74.125.69.139@00000000000000000000000000000000@@@@",
- "/@2@tcp@192.168.1.10@00000000000000000000000000000000@@@@",
- "/@2@tcp@74.125.142.83@00000000000000000000000000000000@@@@",
+ "/@3@tcp@127.0.0.3@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp@127.0.0.1@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp@74.125.69.139@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp@192.168.1.10@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp@74.125.142.83@00000000000000000000000000000000@@@m@@",
}
if !reflect.DeepEqual(got, want) {
t.Errorf("got: %v, want %v", got, want)
@@ -175,16 +175,16 @@
t.Fatalf("unexpected error: %s", err)
}
want = []string{
- "/@2@ws@127.0.0.3:123@00000000000000000000000000000000@@@@",
- "/@2@ws@127.0.0.1@00000000000000000000000000000000@@@@",
- "/@2@ws@74.125.69.139@00000000000000000000000000000000@@@@",
- "/@2@ws@192.168.1.10@00000000000000000000000000000000@@@@",
- "/@2@ws@74.125.142.83@00000000000000000000000000000000@@@@",
- "/@2@tcp@127.0.0.3@00000000000000000000000000000000@@@@",
- "/@2@tcp@127.0.0.1@00000000000000000000000000000000@@@@",
- "/@2@tcp@74.125.69.139@00000000000000000000000000000000@@@@",
- "/@2@tcp@192.168.1.10@00000000000000000000000000000000@@@@",
- "/@2@tcp@74.125.142.83@00000000000000000000000000000000@@@@",
+ "/@3@ws@127.0.0.3:123@00000000000000000000000000000000@@@m@@",
+ "/@3@ws@127.0.0.1@00000000000000000000000000000000@@@m@@",
+ "/@3@ws@74.125.69.139@00000000000000000000000000000000@@@m@@",
+ "/@3@ws@192.168.1.10@00000000000000000000000000000000@@@m@@",
+ "/@3@ws@74.125.142.83@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp@127.0.0.3@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp@127.0.0.1@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp@74.125.69.139@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp@192.168.1.10@00000000000000000000000000000000@@@m@@",
+ "/@3@tcp@74.125.142.83@00000000000000000000000000000000@@@m@@",
}
if !reflect.DeepEqual(got, want) {
t.Errorf("got: %v, want %v", got, want)
diff --git a/runtimes/google/ipc/stream/manager/manager_test.go b/runtimes/google/ipc/stream/manager/manager_test.go
index 8d4d872..829800f 100644
--- a/runtimes/google/ipc/stream/manager/manager_test.go
+++ b/runtimes/google/ipc/stream/manager/manager_test.go
@@ -544,7 +544,10 @@
func TestServerRestartDuringClientLifetime(t *testing.T) {
client := InternalNew(naming.FixedRoutingID(0xcccccccc))
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(nil, nil)
h, err := sh.Start("runServer", nil, "127.0.0.1:0")
if err != nil {
@@ -583,7 +586,10 @@
func TestServerRestartDuringClientLifetimeWS(t *testing.T) {
client := InternalNew(naming.FixedRoutingID(0xcccccccc))
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(nil, nil)
h, err := sh.Start("runServer", nil, "127.0.0.1:0")
if err != nil {
diff --git a/runtimes/google/ipc/stream/proxy/protocol.vdl.go b/runtimes/google/ipc/stream/proxy/protocol.vdl.go
index c5d943f..fefef65 100644
--- a/runtimes/google/ipc/stream/proxy/protocol.vdl.go
+++ b/runtimes/google/ipc/stream/proxy/protocol.vdl.go
@@ -3,12 +3,22 @@
package proxy
+import (
+ // The non-user imports are prefixed with "__" to prevent collisions.
+ __vdl "veyron.io/veyron/veyron2/vdl"
+)
+
// Request is the message sent by a server to request that the proxy route
// traffic intended for the server's RoutingID to the network connection
// between the server and the proxy.
type Request struct {
}
+func (Request) __VDLReflect(struct {
+ Name string "veyron.io/veyron/veyron/runtimes/google/ipc/stream/proxy.Request"
+}) {
+}
+
// Response is sent by the proxy to the server after processing Request.
type Response struct {
// Error is a description of why the proxy refused to proxy the server.
@@ -18,3 +28,13 @@
// used to communicate with the server through the proxy.
Endpoint string
}
+
+func (Response) __VDLReflect(struct {
+ Name string "veyron.io/veyron/veyron/runtimes/google/ipc/stream/proxy.Response"
+}) {
+}
+
+func init() {
+ __vdl.Register(Request{})
+ __vdl.Register(Response{})
+}
diff --git a/runtimes/google/ipc/stream/vc/auth.go b/runtimes/google/ipc/stream/vc/auth.go
index cf83fff..5ecc6ca 100644
--- a/runtimes/google/ipc/stream/vc/auth.go
+++ b/runtimes/google/ipc/stream/vc/auth.go
@@ -9,6 +9,7 @@
"veyron.io/veyron/veyron/runtimes/google/ipc/stream/crypto"
"veyron.io/veyron/veyron/runtimes/google/lib/iobuf"
+ "veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/ipc/version"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/vom"
@@ -37,7 +38,7 @@
}
var discharges []security.Discharge
if tpcavs := server.ThirdPartyCaveats(); len(tpcavs) > 0 && dc != nil {
- discharges = dc.PrepareDischarges(tpcavs, security.DischargeImpetus{})
+ discharges = dc.PrepareDischarges(nil, tpcavs, security.DischargeImpetus{})
}
if err = writeBlessings(conn, authServerContextTag, crypter, principal, server, discharges, v); err != nil {
return
@@ -56,7 +57,7 @@
//
// TODO(ashankar): Seems like there is no way the blessing store
// can say that it does NOT want to share the default blessing with the server?
-func AuthenticateAsClient(conn io.ReadWriteCloser, principal security.Principal, dc DischargeClient, crypter crypto.Crypter, v version.IPCVersion) (server, client security.Blessings, serverDischarges map[string]security.Discharge, err error) {
+func AuthenticateAsClient(ctx context.T, conn io.ReadWriteCloser, principal security.Principal, dc DischargeClient, crypter crypto.Crypter, v version.IPCVersion) (server, client security.Blessings, serverDischarges map[string]security.Discharge, err error) {
defer conn.Close()
if server, serverDischarges, err = readBlessings(conn, authServerContextTag, crypter, v); err != nil {
return
@@ -79,7 +80,7 @@
}
var discharges []security.Discharge
if dc != nil {
- discharges = dc.PrepareDischarges(client.ThirdPartyCaveats(), security.DischargeImpetus{})
+ discharges = dc.PrepareDischarges(ctx, client.ThirdPartyCaveats(), security.DischargeImpetus{})
}
if err = writeBlessings(conn, authClientContextTag, crypter, principal, client, discharges, v); err != nil {
return
diff --git a/runtimes/google/ipc/stream/vc/vc.go b/runtimes/google/ipc/stream/vc/vc.go
index f5783cb..4460e6e 100644
--- a/runtimes/google/ipc/stream/vc/vc.go
+++ b/runtimes/google/ipc/stream/vc/vc.go
@@ -16,13 +16,16 @@
"veyron.io/veyron/veyron/runtimes/google/lib/bqueue"
"veyron.io/veyron/veyron/runtimes/google/lib/iobuf"
vsync "veyron.io/veyron/veyron/runtimes/google/lib/sync"
+ ivtrace "veyron.io/veyron/veyron/runtimes/google/vtrace"
+ "veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/ipc/stream"
"veyron.io/veyron/veyron2/ipc/version"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/options"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/vlog"
+ "veyron.io/veyron/veyron2/vtrace"
)
var (
@@ -62,6 +65,12 @@
version version.IPCVersion
}
+// NoDischarges specifies that the RPC call should not fetch discharges.
+type NoDischarges struct{}
+
+func (NoDischarges) IPCCallOpt() {}
+func (NoDischarges) IPCStreamVCOpt() {}
+
var _ stream.VC = (*VC)(nil)
// Helper is the interface for functionality required by the stream.VC
@@ -111,7 +120,7 @@
//
// TODO(ataly, ashankar): What should be the impetus for obtaining the discharges?
type DischargeClient interface {
- PrepareDischarges(forcaveats []security.ThirdPartyCaveat, impetus security.DischargeImpetus) []security.Discharge
+ PrepareDischarges(ctx context.T, forcaveats []security.ThirdPartyCaveat, impetus security.DischargeImpetus) []security.Discharge
// Invalidate marks the provided discharges as invalid, and therefore unfit
// for being returned by a subsequent PrepareDischarges call.
Invalidate(discharges ...security.Discharge)
@@ -121,6 +130,11 @@
IPCStreamVCOpt()
}
+// DialContext establishes the context under which a VC Dial was initiated.
+type DialContext struct{ context.T }
+
+func (DialContext) IPCStreamVCOpt() {}
+
// InternalNew creates a new VC, which implements the stream.VC interface.
//
// As the name suggests, this method is intended for use only within packages
@@ -361,9 +375,13 @@
tlsSessionCache crypto.TLSClientSessionCache
securityLevel options.VCSecurityLevel
dischargeClient DischargeClient
+ ctx context.T
+ noDischarges bool
)
for _, o := range opts {
switch v := o.(type) {
+ case DialContext:
+ ctx = v.T
case DischargeClient:
dischargeClient = v
case LocalPrincipal:
@@ -372,8 +390,19 @@
securityLevel = v
case crypto.TLSClientSessionCache:
tlsSessionCache = v
+ case NoDischarges:
+ noDischarges = true
}
}
+ if ctx != nil {
+ var span vtrace.Span
+ ctx, span = ivtrace.WithNewSpan(ctx, "vc.HandshakeDialedVC")
+ defer span.Finish()
+ }
+ // If noDischarge is provided, disable the dischargeClient.
+ if noDischarges {
+ dischargeClient = nil
+ }
switch securityLevel {
case options.VCSecurityConfidential:
if principal == nil {
@@ -410,7 +439,7 @@
if err != nil {
return vc.err(fmt.Errorf("failed to create a Flow for authentication: %v", err))
}
- rBlessings, lBlessings, rDischarges, err := AuthenticateAsClient(authConn, principal, dischargeClient, crypter, vc.version)
+ rBlessings, lBlessings, rDischarges, err := AuthenticateAsClient(ctx, authConn, principal, dischargeClient, crypter, vc.version)
if err != nil {
return vc.err(fmt.Errorf("authentication failed: %v", err))
}
diff --git a/runtimes/google/ipc/stream/vc/vc_test.go b/runtimes/google/ipc/stream/vc/vc_test.go
index 4e3b376..4ac541e 100644
--- a/runtimes/google/ipc/stream/vc/vc_test.go
+++ b/runtimes/google/ipc/stream/vc/vc_test.go
@@ -21,6 +21,7 @@
"veyron.io/veyron/veyron/runtimes/google/lib/bqueue/drrqueue"
"veyron.io/veyron/veyron/runtimes/google/lib/iobuf"
+ "veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/ipc/stream"
"veyron.io/veyron/veyron2/ipc/version"
"veyron.io/veyron/veyron2/naming"
@@ -164,7 +165,7 @@
type mockDischargeClient []security.Discharge
-func (m mockDischargeClient) PrepareDischarges(forcaveats []security.ThirdPartyCaveat, impetus security.DischargeImpetus) []security.Discharge {
+func (m mockDischargeClient) PrepareDischarges(_ context.T, forcaveats []security.ThirdPartyCaveat, impetus security.DischargeImpetus) []security.Discharge {
return m
}
func (mockDischargeClient) Invalidate(...security.Discharge) {}
diff --git a/runtimes/google/ipc/stream/vif/auth.go b/runtimes/google/ipc/stream/vif/auth.go
index 6a05496..53a4b71 100644
--- a/runtimes/google/ipc/stream/vif/auth.go
+++ b/runtimes/google/ipc/stream/vif/auth.go
@@ -14,6 +14,7 @@
"veyron.io/veyron/veyron/runtimes/google/ipc/stream/vc"
"veyron.io/veyron/veyron/runtimes/google/ipc/version"
"veyron.io/veyron/veyron/runtimes/google/lib/iobuf"
+ "veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/ipc/stream"
ipcversion "veyron.io/veyron/veyron2/ipc/version"
"veyron.io/veyron/veyron2/options"
@@ -60,7 +61,7 @@
// including a hash of the HopSetup message in the encrypted stream. It is
// likely that this will be addressed in subsequent protocol versions (or it may
// not be addressed at all if IPCVersion6 becomes the only supported version).
-func AuthenticateAsClient(conn net.Conn, versions *version.Range, principal security.Principal, dc vc.DischargeClient) (crypto.ControlCipher, error) {
+func AuthenticateAsClient(ctx context.T, conn net.Conn, versions *version.Range, principal security.Principal, dc vc.DischargeClient) (crypto.ControlCipher, error) {
if versions == nil {
versions = version.SupportedRange
}
@@ -110,13 +111,13 @@
// Perform the authentication.
switch v {
case ipcversion.IPCVersion6:
- return authenticateAsClientIPC6(conn, reader, principal, dc, &pvt, &pub, ppub)
+ return authenticateAsClientIPC6(ctx, conn, reader, principal, dc, &pvt, &pub, ppub)
default:
return nil, errUnsupportedEncryptVersion
}
}
-func authenticateAsClientIPC6(writer io.Writer, reader *iobuf.Reader, principal security.Principal, dc vc.DischargeClient,
+func authenticateAsClientIPC6(ctx context.T, writer io.Writer, reader *iobuf.Reader, principal security.Principal, dc vc.DischargeClient,
pvt *privateData, pub, ppub *message.HopSetup) (crypto.ControlCipher, error) {
pbox := ppub.NaclBox()
if pbox == nil {
@@ -125,7 +126,7 @@
c := crypto.NewControlCipherIPC6(&pbox.PublicKey, &pvt.naclBoxPrivateKey, false)
sconn := newSetupConn(writer, reader, c)
// TODO(jyh): act upon the authentication results.
- _, _, _, err := vc.AuthenticateAsClient(sconn, principal, dc, crypto.NewNullCrypter(), ipcversion.IPCVersion6)
+ _, _, _, err := vc.AuthenticateAsClient(ctx, sconn, principal, dc, crypto.NewNullCrypter(), ipcversion.IPCVersion6)
if err != nil {
return nil, err
}
@@ -229,18 +230,26 @@
// clientAuthOptions extracts the client authentication options from the options
// list.
-func clientAuthOptions(lopts []stream.VCOpt) (principal security.Principal, dischargeClient vc.DischargeClient, err error) {
+func clientAuthOptions(lopts []stream.VCOpt) (ctx context.T, principal security.Principal, dischargeClient vc.DischargeClient, err error) {
var securityLevel options.VCSecurityLevel
+ var noDischarges bool
for _, o := range lopts {
switch v := o.(type) {
+ case vc.DialContext:
+ ctx = v.T
case vc.DischargeClient:
dischargeClient = v
case vc.LocalPrincipal:
principal = v.Principal
case options.VCSecurityLevel:
securityLevel = v
+ case vc.NoDischarges:
+ noDischarges = true
}
}
+ if noDischarges {
+ dischargeClient = nil
+ }
switch securityLevel {
case options.VCSecurityConfidential:
if principal == nil {
diff --git a/runtimes/google/ipc/stream/vif/vif.go b/runtimes/google/ipc/stream/vif/vif.go
index 355ea0b..6447009 100644
--- a/runtimes/google/ipc/stream/vif/vif.go
+++ b/runtimes/google/ipc/stream/vif/vif.go
@@ -25,10 +25,12 @@
"veyron.io/veyron/veyron/runtimes/google/lib/pcqueue"
vsync "veyron.io/veyron/veyron/runtimes/google/lib/sync"
"veyron.io/veyron/veyron/runtimes/google/lib/upcqueue"
+ ivtrace "veyron.io/veyron/veyron/runtimes/google/vtrace"
"veyron.io/veyron/veyron2/ipc/stream"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/verror"
"veyron.io/veyron/veyron2/vlog"
+ "veyron.io/veyron/veyron2/vtrace"
)
// VIF implements a "virtual interface" over an underlying network connection
@@ -107,11 +109,17 @@
// placed inside veyron/runtimes/google. Code outside the
// veyron2/runtimes/google/* packages should never call this method.
func InternalNewDialedVIF(conn net.Conn, rid naming.RoutingID, versions *version.Range, opts ...stream.VCOpt) (*VIF, error) {
- principal, dc, err := clientAuthOptions(opts)
+ ctx, principal, dc, err := clientAuthOptions(opts)
if err != nil {
return nil, err
}
- c, err := AuthenticateAsClient(conn, versions, principal, dc)
+ if ctx != nil {
+ var span vtrace.Span
+ ctx, span = ivtrace.WithNewSpan(ctx, "InternalNewDialedVIF")
+ span.Annotatef("(%v, %v)", conn.RemoteAddr().Network(), conn.RemoteAddr())
+ defer span.Finish()
+ }
+ c, err := AuthenticateAsClient(ctx, conn, versions, principal, dc)
if err != nil {
return nil, err
}
diff --git a/runtimes/google/ipc/testutil_test.go b/runtimes/google/ipc/testutil_test.go
index 4144d97..02f07c7 100644
--- a/runtimes/google/ipc/testutil_test.go
+++ b/runtimes/google/ipc/testutil_test.go
@@ -4,10 +4,10 @@
"reflect"
"testing"
- "veyron.io/veyron/veyron/lib/testutil"
-
"veyron.io/veyron/veyron2/security"
- "veyron.io/veyron/veyron2/verror"
+ "veyron.io/veyron/veyron2/verror2"
+
+ "veyron.io/veyron/veyron/lib/testutil"
)
func init() { testutil.Init() }
@@ -22,7 +22,7 @@
// reasons for this check and conditions for when it
// can be removed can be seen in the comments for
// result2vom.
- var verr verror.E
+ var verr verror2.E
typ = reflect.ValueOf(&verr).Elem().Type()
}
outs[ix] = reflect.New(typ).Interface()
@@ -34,9 +34,23 @@
for ix, res := range gotptrs {
got := reflect.ValueOf(res).Elem().Interface()
want := want[ix]
- if !reflect.DeepEqual(got, want) {
- t.Errorf("%s result %d got %v, want %v", name, ix, got, want)
+ switch g := got.(type) {
+ case verror2.Standard:
+ w, ok := want.(verror2.Standard)
+ // don't use reflect deep equal on verror's since they contain
+ // a list of stack PCs which will be different.
+ if !ok {
+ t.Errorf("%s result %d got type %T, want %T", name, ix, g, w)
+ }
+ if !verror2.Is(g, w.IDAction.ID) {
+ t.Errorf("%s result %d got %v, want %v", name, ix, g, w)
+ }
+ default:
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("%s result %d got %v, want %v", name, ix, got, want)
+ }
}
+
}
}
diff --git a/runtimes/google/naming/endpoint.go b/runtimes/google/naming/endpoint.go
index e2fefd9..2552bc2 100644
--- a/runtimes/google/naming/endpoint.go
+++ b/runtimes/google/naming/endpoint.go
@@ -36,6 +36,9 @@
func NewEndpoint(input string) (*Endpoint, error) {
var ep Endpoint
+ // We have to guess this is a mount table if we don't know.
+ ep.IsMountTable = true
+
// The prefix and suffix are optional.
input = strings.TrimPrefix(strings.TrimSuffix(input, suffix), separator)
@@ -116,7 +119,10 @@
}
func parseMountTableFlag(input string) (bool, error) {
- if len(input) == 1 {
+ switch len(input) {
+ case 0:
+ return true, nil
+ case 1:
switch f := input[0]; f {
case 'm':
return true, nil
@@ -169,7 +175,7 @@
return Network
}
-var defaultVersion = 2
+var defaultVersion = 3
func (ep *Endpoint) VersionedString(version int) string {
switch version {
@@ -205,7 +211,7 @@
func (ep *Endpoint) ServesMountTable() bool {
//nologcall
- return true
+ return ep.IsMountTable
}
type addr struct {
diff --git a/runtimes/google/naming/endpoint_test.go b/runtimes/google/naming/endpoint_test.go
index 00135c5..1aaca1b 100644
--- a/runtimes/google/naming/endpoint_test.go
+++ b/runtimes/google/naming/endpoint_test.go
@@ -11,9 +11,10 @@
func TestEndpoint(t *testing.T) {
v1 := &Endpoint{
- Protocol: "tcp",
- Address: "batman.com:1234",
- RID: naming.FixedRoutingID(0xdabbad00),
+ Protocol: "tcp",
+ Address: "batman.com:1234",
+ RID: naming.FixedRoutingID(0xdabbad00),
+ IsMountTable: true,
}
v2 := &Endpoint{
Protocol: "tcp",
@@ -21,6 +22,7 @@
RID: naming.FixedRoutingID(0xdabbad00),
MinIPCVersion: 1,
MaxIPCVersion: 10,
+ IsMountTable: true,
}
v2hp := &Endpoint{
Protocol: "tcp",
@@ -28,13 +30,22 @@
RID: naming.FixedRoutingID(0x0),
MinIPCVersion: 2,
MaxIPCVersion: 3,
+ IsMountTable: true,
}
- v3 := &Endpoint{
+ v3s := &Endpoint{
Protocol: "tcp",
Address: "batman.com:2345",
RID: naming.FixedRoutingID(0x0),
MinIPCVersion: 2,
MaxIPCVersion: 3,
+ IsMountTable: false,
+ }
+ v3m := &Endpoint{
+ Protocol: "tcp",
+ Address: "batman.com:2345",
+ RID: naming.FixedRoutingID(0xdabbad00),
+ MinIPCVersion: 2,
+ MaxIPCVersion: 3,
IsMountTable: true,
}
@@ -56,6 +67,45 @@
}
}
+ // Test v3 endpoints.
+ defaultVersion = 3
+ testcasesC := []struct {
+ Endpoint naming.Endpoint
+ String string
+ Input string
+ min, max version.IPCVersion
+ servesMT bool
+ }{
+ {v3s, "@3@tcp@batman.com:2345@00000000000000000000000000000000@2@3@s@@", "", 2, 3, false},
+ {v3m, "@3@tcp@batman.com:2345@000000000000000000000000dabbad00@2@3@m@@", "", 2, 3, true},
+ }
+
+ for _, test := range testcasesC {
+ if got, want := test.Endpoint.String(), test.String; got != want {
+ t.Errorf("Got %q want %q for endpoint %T = %#v", got, want, test.Endpoint, test.Endpoint)
+ }
+ str := test.Input
+ var ep naming.Endpoint
+ var err error
+ if str == "" {
+ str = test.String
+ ep, err = NewEndpoint(str)
+ } else {
+ ep, err = NewEndpoint(naming.FormatEndpoint("tcp", str,
+ version.IPCVersionRange{test.min, test.max},
+ naming.ServesMountTableOpt(test.servesMT)))
+ }
+ if err != nil {
+ t.Errorf("Endpoint(%q) failed with %v", str, err)
+ continue
+ }
+ if !reflect.DeepEqual(ep, test.Endpoint) {
+ t.Errorf("Got endpoint %T = %#v, want %T = %#v for string %q", ep, ep, test.Endpoint, test.Endpoint, str)
+ }
+ }
+
+ // Make sure we can continue to parse and create v2 endpoints.
+ defaultVersion = 2
testcasesB := []struct {
Endpoint naming.Endpoint
String string
@@ -63,11 +113,9 @@
min, max version.IPCVersion
servesMT bool
}{
- {v1, "@2@tcp@batman.com:1234@000000000000000000000000dabbad00@@@@", "", version.UnknownIPCVersion, version.UnknownIPCVersion, false},
- {v2, "@2@tcp@batman.com:2345@000000000000000000000000dabbad00@1@10@@", "", 1, 10, false},
- {v2hp, "@2@tcp@batman.com:2345@00000000000000000000000000000000@2@3@@", "batman.com:2345", 2, 3, false},
- // the v3 format is ignored unless explicitly enabled.
- {v3, "@2@tcp@batman.com:2345@00000000000000000000000000000000@2@3@@", "batman.com:2345", 2, 3, true},
+ {v1, "@2@tcp@batman.com:1234@000000000000000000000000dabbad00@@@@", "", version.UnknownIPCVersion, version.UnknownIPCVersion, true},
+ {v2, "@2@tcp@batman.com:2345@000000000000000000000000dabbad00@1@10@@", "", 1, 10, true},
+ {v2hp, "@2@tcp@batman.com:2345@00000000000000000000000000000000@2@3@@", "batman.com:2345", 2, 3, true},
}
for _, test := range testcasesB {
@@ -93,25 +141,7 @@
t.Errorf("Got endpoint %T = %#v, want %T = %#v for string %q", ep, ep, test.Endpoint, test.Endpoint, str)
}
}
-
- // Enable V3 endpoints.
defaultVersion = 3
- for _, c := range []struct {
- servesMT bool
- str string
- }{
- {true, "@3@tcp@a:10@00000000000000000000000000000000@1@3@m@@"},
- {false, "@3@tcp@a:10@00000000000000000000000000000000@1@3@s@@"},
- } {
- ep, _ := NewEndpoint(naming.FormatEndpoint("tcp", "a:10",
- version.IPCVersionRange{1, 3},
- naming.ServesMountTableOpt(c.servesMT)))
- if got, want := ep.String(), c.str; got != want {
- t.Errorf("got: %s, want: %s", got, want)
- }
- }
- // Disable V3 endpoints.
- defaultVersion = 2
}
type endpointTest struct {
@@ -121,15 +151,17 @@
func TestEndpointDefaults(t *testing.T) {
testcases := []endpointTest{
- {"@1@tcp@batman@@@", "@2@tcp@batman@00000000000000000000000000000000@@@@", nil},
- {"@2@tcp@robin@@@@@", "@2@tcp@robin@00000000000000000000000000000000@@@@", nil},
- {"@1@@@@@", "@2@tcp@:0@00000000000000000000000000000000@@@@", nil},
- {"@2@@@@@@@", "@2@tcp@:0@00000000000000000000000000000000@@@@", nil},
- {"@1@tcp@batman:12@@@", "@2@tcp@batman:12@00000000000000000000000000000000@@@@", nil},
- {"@host:10@@", "@2@tcp@host:10@00000000000000000000000000000000@@@@", nil},
- {"@2@tcp@foo:12@@9@@@", "@2@tcp@foo:12@00000000000000000000000000000000@9@@@", nil},
- {"@2@tcp@foo:12@@@4@@", "@2@tcp@foo:12@00000000000000000000000000000000@@4@@", nil},
- {"@2@tcp@foo:12@@2@4@@", "@2@tcp@foo:12@00000000000000000000000000000000@2@4@@", nil},
+ {"@1@tcp@batman@@@", "@3@tcp@batman@00000000000000000000000000000000@@@m@@", nil},
+ {"@2@tcp@robin@@@@@", "@3@tcp@robin@00000000000000000000000000000000@@@m@@", nil},
+ {"@1@@@@@", "@3@tcp@:0@00000000000000000000000000000000@@@m@@", nil},
+ {"@2@@@@@@@", "@3@tcp@:0@00000000000000000000000000000000@@@m@@", nil},
+ {"@1@tcp@batman:12@@@", "@3@tcp@batman:12@00000000000000000000000000000000@@@m@@", nil},
+ {"@host:10@@", "@3@tcp@host:10@00000000000000000000000000000000@@@m@@", nil},
+ {"@2@tcp@foo:12@@9@@@", "@3@tcp@foo:12@00000000000000000000000000000000@9@@m@@", nil},
+ {"@2@tcp@foo:12@@@4@@", "@3@tcp@foo:12@00000000000000000000000000000000@@4@m@@", nil},
+ {"@2@tcp@foo:12@@2@4@@", "@3@tcp@foo:12@00000000000000000000000000000000@2@4@m@@", nil},
+ {"@3@@host:11@@@@m@@", "@3@tcp@host:11@00000000000000000000000000000000@@@m@@", nil},
+ {"@3@@host:12@@@@@@", "@3@tcp@host:12@00000000000000000000000000000000@@@m@@", nil},
}
runEndpointTests(t, testcases)
}
@@ -156,8 +188,8 @@
func TestHostPortEndpoint(t *testing.T) {
testcases := []endpointTest{
- {"localhost:10", "@2@tcp@localhost:10@00000000000000000000000000000000@@@@", nil},
- {"localhost:", "@2@tcp@localhost:@00000000000000000000000000000000@@@@", nil},
+ {"localhost:10", "@3@tcp@localhost:10@00000000000000000000000000000000@@@m@@", nil},
+ {"localhost:", "@3@tcp@localhost:@00000000000000000000000000000000@@@m@@", nil},
{"localhost", "", errInvalidEndpointString},
}
runEndpointTests(t, testcases)
@@ -170,18 +202,21 @@
Address: "batman.com:4444",
MinIPCVersion: min,
MaxIPCVersion: max,
+ IsMountTable: true,
}
ipv4 := &Endpoint{
Protocol: "tcp",
Address: "192.168.1.1:4444",
MinIPCVersion: min,
MaxIPCVersion: max,
+ IsMountTable: true,
}
ipv6 := &Endpoint{
Protocol: "tcp",
Address: "[01:02::]:4444",
MinIPCVersion: min,
MaxIPCVersion: max,
+ IsMountTable: true,
}
testcases := []struct {
Endpoint naming.Endpoint
diff --git a/runtimes/google/naming/namespace/all_test.go b/runtimes/google/naming/namespace/all_test.go
index 645050a..c85a1d1 100644
--- a/runtimes/google/naming/namespace/all_test.go
+++ b/runtimes/google/naming/namespace/all_test.go
@@ -9,6 +9,7 @@
"time"
"veyron.io/veyron/veyron2"
+ "veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/ipc/stream"
"veyron.io/veyron/veyron2/naming"
@@ -24,6 +25,7 @@
"veyron.io/veyron/veyron/lib/websocket"
_ "veyron.io/veyron/veyron/profiles"
"veyron.io/veyron/veyron/runtimes/google/naming/namespace"
+ vsecurity "veyron.io/veyron/veyron/security"
service "veyron.io/veyron/veyron/services/mounttable/lib"
)
@@ -148,20 +150,28 @@
}
}
-func testResolveToMountTable(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
- servers, err := ns.ResolveToMountTable(r.NewContext(), name)
+func doResolveTest(t *testing.T, fname string, f func(context.T, string, ...naming.ResolveOpt) ([]string, error), ctx context.T, name string, want []string, opts ...naming.ResolveOpt) {
+ servers, err := f(ctx, name, opts...)
if err != nil {
- boom(t, "Failed to ResolveToMountTable %q: %s", name, err)
+ boom(t, "Failed to %s %s: %s", fname, name, err)
}
- compare(t, "ResolveToMountTable", name, servers, want)
+ compare(t, fname, name, servers, want)
+}
+
+func testResolveToMountTable(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
+ doResolveTest(t, "ResolveToMountTable", ns.ResolveToMountTable, r.NewContext(), name, want)
+}
+
+func testResolveToMountTableWithPattern(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, pattern naming.ResolveOpt, want ...string) {
+ doResolveTest(t, "ResolveToMountTable", ns.ResolveToMountTable, r.NewContext(), name, want, pattern)
}
func testResolve(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
- servers, err := ns.Resolve(r.NewContext(), name)
- if err != nil {
- boom(t, "Failed to Resolve %q: %s", name, err)
- }
- compare(t, "Resolve", name, servers, want)
+ doResolveTest(t, "Resolve", ns.Resolve, r.NewContext(), name, want)
+}
+
+func testResolveWithPattern(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, pattern naming.ResolveOpt, want ...string) {
+ doResolveTest(t, "Resolve", ns.Resolve, r.NewContext(), name, want, pattern)
}
func testUnresolve(t *testing.T, r veyron2.Runtime, ns naming.Namespace, name string, want ...string) {
@@ -623,3 +633,66 @@
t.Errorf("namespace.New should have failed with an unrooted name")
}
}
+
+func bless(blesser, delegate security.Principal, extension string) {
+ b, err := blesser.Bless(delegate.PublicKey(), blesser.BlessingStore().Default(), extension, security.UnconstrainedUse())
+ if err != nil {
+ panic(err)
+ }
+ delegate.BlessingStore().SetDefault(b)
+}
+
+func TestRootBlessing(t *testing.T) {
+ // We need the default runtime for the server-side mounttable code
+ // which references rt.R() to create new endpoints
+ cr := rt.Init()
+ r, _ := rt.New() // We use a different runtime for the client side.
+
+ proot, err := vsecurity.NewPrincipal()
+ if err != nil {
+ panic(err)
+ }
+ b, err := proot.BlessSelf("root")
+ if err != nil {
+ panic(err)
+ }
+ proot.BlessingStore().SetDefault(b)
+
+ bless(proot, r.Principal(), "server")
+ bless(proot, cr.Principal(), "client")
+
+ cr.Principal().AddToRoots(proot.BlessingStore().Default())
+ r.Principal().AddToRoots(proot.BlessingStore().Default())
+
+ root, mts, _, stopper := createNamespace(t, r)
+ defer stopper()
+ ns := r.Namespace()
+
+ name := naming.Join(root.name, mt2MP)
+ // First check with a non-matching blessing pattern.
+ println("********")
+ _, err = ns.Resolve(r.NewContext(), name, naming.RootBlessingPatternOpt("root/foobar"))
+ if !verror.Is(err, verror.NotTrusted.ID) {
+ t.Errorf("Resolve expected NotTrusted error, got %v", err)
+ }
+ _, err = ns.ResolveToMountTable(r.NewContext(), name, naming.RootBlessingPatternOpt("root/foobar"))
+ if !verror.Is(err, verror.NotTrusted.ID) {
+ t.Errorf("ResolveToMountTable expected NotTrusted error, got %v", err)
+ }
+
+ // Now check a matching pattern.
+ testResolveWithPattern(t, r, ns, name, naming.RootBlessingPatternOpt("root/server"), addWSName(mts[mt2MP].name)...)
+ testResolveToMountTableWithPattern(t, r, ns, name, naming.RootBlessingPatternOpt("root/server"), name)
+
+ // After successful lookup it should be cached, so the pattern doesn't matter.
+ testResolveWithPattern(t, r, ns, name, naming.RootBlessingPatternOpt("root/foobar"), addWSName(mts[mt2MP].name)...)
+
+ // Test calling a method.
+ jokeName := naming.Join(root.name, mt4MP, j1MP)
+ runServer(t, r, &dispatcher{}, naming.Join(mts["mt4"].name, j1MP))
+ _, err = r.Client().StartCall(r.NewContext(), "[root/foobar]"+jokeName, "KnockKnock", nil)
+ if err == nil {
+ t.Errorf("StartCall expected NoAccess error, got %v", err)
+ }
+ knockKnock(t, r, "[root/server]"+jokeName)
+}
diff --git a/runtimes/google/naming/namespace/namespace.go b/runtimes/google/naming/namespace/namespace.go
index 26cc469..9d58742 100644
--- a/runtimes/google/naming/namespace/namespace.go
+++ b/runtimes/google/naming/namespace/namespace.go
@@ -4,6 +4,8 @@
"sync"
"time"
+ inaming "veyron.io/veyron/veyron/runtimes/google/naming"
+
"veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/verror"
@@ -125,9 +127,11 @@
}
return e, false
}
- // TODO(p): right now I assume any address handed to me to be resolved is a mount table.
- // Eventually we should do something like the following:
- e.SetServesMountTable(true)
+ servesMT := true
+ if ep, err := inaming.NewEndpoint(address); err == nil {
+ servesMT = ep.ServesMountTable()
+ }
+ e.SetServesMountTable(servesMT)
e.Name = suffix
e.Servers = append(e.Servers, naming.MountedServer{Server: naming.JoinAddressName(address, ""), Expires: expiration})
return e, true
diff --git a/runtimes/google/naming/namespace/resolve.go b/runtimes/google/naming/namespace/resolve.go
index eb69a3c..110dcbf 100644
--- a/runtimes/google/naming/namespace/resolve.go
+++ b/runtimes/google/naming/namespace/resolve.go
@@ -2,6 +2,7 @@
import (
"errors"
+ "fmt"
"runtime"
"veyron.io/veyron/veyron2/context"
@@ -12,11 +13,17 @@
"veyron.io/veyron/veyron2/vlog"
)
-func (ns *namespace) resolveAgainstMountTable(ctx context.T, client ipc.Client, e *naming.MountEntry) (*naming.MountEntry, error) {
+func (ns *namespace) resolveAgainstMountTable(ctx context.T, client ipc.Client, e *naming.MountEntry, pattern string) (*naming.MountEntry, error) {
// Try each server till one answers.
finalErr := errors.New("no servers to resolve query")
for _, s := range e.Servers {
+ var pattern_and_name string
name := naming.JoinAddressName(s.Server, e.Name)
+ if pattern != "" {
+ pattern_and_name = naming.JoinAddressName(s.Server, fmt.Sprintf("[%s]%s", pattern, e.Name))
+ } else {
+ pattern_and_name = name
+ }
// First check the cache.
if ne, err := ns.resolutionCache.lookup(name); err == nil {
vlog.VI(2).Infof("resolveAMT %s from cache -> %v", name, convertServersToStrings(ne.Servers, ne.Name))
@@ -24,7 +31,7 @@
}
// Not in cache, call the real server.
callCtx, _ := ctx.WithTimeout(callTimeout)
- call, err := client.StartCall(callCtx, name, "ResolveStepX", nil, options.NoResolve(true))
+ call, err := client.StartCall(callCtx, pattern_and_name, "ResolveStepX", nil, options.NoResolve(true))
if err != nil {
finalErr = err
vlog.VI(2).Infof("ResolveStep.StartCall %s failed: %s", name, err)
@@ -61,7 +68,7 @@
}
// ResolveX implements veyron2/naming.Namespace.
-func (ns *namespace) ResolveX(ctx context.T, name string) (*naming.MountEntry, error) {
+func (ns *namespace) ResolveX(ctx context.T, name string, opts ...naming.ResolveOpt) (*naming.MountEntry, error) {
defer vlog.LogCall()()
e, _ := ns.rootMountEntry(name)
if vlog.V(2) {
@@ -72,6 +79,7 @@
if len(e.Servers) == 0 {
return nil, verror.Make(naming.ErrNoSuchName, ctx, name)
}
+ pattern := getRootPattern(opts)
// Iterate walking through mount table servers.
for remaining := ns.maxResolveDepth; remaining > 0; remaining-- {
vlog.VI(2).Infof("ResolveX(%s) loop %v", name, *e)
@@ -81,7 +89,7 @@
}
var err error
curr := e
- if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), curr); err != nil {
+ if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), curr, pattern); err != nil {
// If the name could not be found in the mount table, return an error.
if verror.Is(err, naming.ErrNoSuchNameRoot.ID) {
err = verror.Make(naming.ErrNoSuchName, ctx, name)
@@ -90,20 +98,26 @@
vlog.VI(1).Infof("ResolveX(%s) -> (NoSuchName: %v)", name, curr)
return nil, err
}
+ if verror.Is(err, verror.NotTrusted.ID) {
+ vlog.VI(1).Infof("ResolveX(%s) -> (NotTrusted: %v)", name, curr)
+ return nil, err
+
+ }
// Any other failure (server not found, no ResolveStep
// method, etc.) are a sign that iterative resolution can
// stop.
vlog.VI(1).Infof("ResolveX(%s) -> %v", name, curr)
return curr, nil
}
+ pattern = ""
}
return nil, verror.Make(naming.ErrResolutionDepthExceeded, ctx)
}
// Resolve implements veyron2/naming.Namespace.
-func (ns *namespace) Resolve(ctx context.T, name string) ([]string, error) {
+func (ns *namespace) Resolve(ctx context.T, name string, opts ...naming.ResolveOpt) ([]string, error) {
defer vlog.LogCall()()
- e, err := ns.ResolveX(ctx, name)
+ e, err := ns.ResolveX(ctx, name, opts...)
if err != nil {
return nil, err
}
@@ -111,7 +125,7 @@
}
// ResolveToMountTableX implements veyron2/naming.Namespace.
-func (ns *namespace) ResolveToMountTableX(ctx context.T, name string) (*naming.MountEntry, error) {
+func (ns *namespace) ResolveToMountTableX(ctx context.T, name string, opts ...naming.ResolveOpt) (*naming.MountEntry, error) {
defer vlog.LogCall()()
e, _ := ns.rootMountEntry(name)
if vlog.V(2) {
@@ -122,6 +136,7 @@
if len(e.Servers) == 0 {
return nil, verror.Make(naming.ErrNoMountTable, ctx)
}
+ pattern := getRootPattern(opts)
last := e
for remaining := ns.maxResolveDepth; remaining > 0; remaining-- {
vlog.VI(2).Infof("ResolveToMountTableX(%s) loop %v", name, e)
@@ -132,7 +147,7 @@
vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v", name, last)
return last, nil
}
- if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), e); err != nil {
+ if e, err = ns.resolveAgainstMountTable(ctx, ns.rt.Client(), e, pattern); err != nil {
if verror.Is(err, naming.ErrNoSuchNameRoot.ID) {
vlog.VI(1).Infof("ResolveToMountTableX(%s) -> %v (NoSuchRoot: %v)", name, last, curr)
return last, nil
@@ -157,14 +172,15 @@
return nil, err
}
last = curr
+ pattern = ""
}
return nil, verror.Make(naming.ErrResolutionDepthExceeded, ctx)
}
// ResolveToMountTable implements veyron2/naming.Namespace.
-func (ns *namespace) ResolveToMountTable(ctx context.T, name string) ([]string, error) {
+func (ns *namespace) ResolveToMountTable(ctx context.T, name string, opts ...naming.ResolveOpt) ([]string, error) {
defer vlog.LogCall()()
- e, err := ns.ResolveToMountTableX(ctx, name)
+ e, err := ns.ResolveToMountTableX(ctx, name, opts...)
if err != nil {
return nil, err
}
@@ -254,3 +270,12 @@
}
return flushed
}
+
+func getRootPattern(opts []naming.ResolveOpt) string {
+ for _, opt := range opts {
+ if pattern, ok := opt.(naming.RootBlessingPatternOpt); ok {
+ return string(pattern)
+ }
+ }
+ return ""
+}
diff --git a/runtimes/google/rt/ipc.go b/runtimes/google/rt/ipc.go
index 9214839..98c1f91 100644
--- a/runtimes/google/rt/ipc.go
+++ b/runtimes/google/rt/ipc.go
@@ -109,14 +109,20 @@
otherOpts = append(otherOpts, rt.reservedOpts...)
}
+ // Add the preferred protocols from the runtime if there are any.
+ if len(rt.preferredProtocols) > 0 {
+ otherOpts = append(otherOpts, iipc.PreferredServerResolveProtocols(rt.preferredProtocols))
+ }
+
// Add the option that provides a discharge client to the server.
// TODO(cnicolaou): extend the timeout when parallel connections are
// going.
- dc, err := iipc.InternalNewDischargeClient(sm, ns, rt.NewContext(), vc.LocalPrincipal{rt.principal}, &imanager.DialTimeout{5 * time.Second})
+ ctx := rt.NewContext()
+ dc, err := iipc.InternalNewDischargeClient(sm, ns, ctx, vc.LocalPrincipal{rt.principal}, &imanager.DialTimeout{5 * time.Second})
if err != nil {
return nil, fmt.Errorf("failed to create discharge-client: %v", err)
}
- return iipc.InternalNewServer(rt.NewContext(), sm, ns, rt.traceStore, append(otherOpts, dc)...)
+ return iipc.InternalNewServer(ctx, sm, ns, rt.traceStore, append(otherOpts, dc)...)
}
func (rt *vrt) NewStreamManager(opts ...stream.ManagerOpt) (stream.Manager, error) {
diff --git a/runtimes/google/rt/ipc_test.go b/runtimes/google/rt/ipc_test.go
index 018817b..7f7c589 100644
--- a/runtimes/google/rt/ipc_test.go
+++ b/runtimes/google/rt/ipc_test.go
@@ -15,7 +15,6 @@
"veyron.io/veyron/veyron/lib/testutil"
tsecurity "veyron.io/veyron/veyron/lib/testutil/security"
"veyron.io/veyron/veyron/profiles"
- vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron2/vdl/vdlutil"
"veyron.io/veyron/veyron2/verror"
)
@@ -96,7 +95,7 @@
return nil, "", err
}
serverObjectName := naming.JoinAddressName(endpoint.String(), "")
- if err := server.Serve("", s, vsecurity.NewACLAuthorizer(vsecurity.OpenACL())); err != nil {
+ if err := server.Serve("", s, allowEveryone{}); err != nil {
return nil, "", err
}
return server, serverObjectName, nil
@@ -277,3 +276,7 @@
t.Errorf("client.StartCall passed unexpectedly with remote end authenticated as: %v", remoteBlessings)
}
}
+
+type allowEveryone struct{}
+
+func (allowEveryone) Authorize(security.Context) error { return nil }
diff --git a/runtimes/google/rt/mgmt_test.go b/runtimes/google/rt/mgmt_test.go
index defc3c7..cae8d50 100644
--- a/runtimes/google/rt/mgmt_test.go
+++ b/runtimes/google/rt/mgmt_test.go
@@ -116,7 +116,10 @@
// TestNoWaiters verifies that the child process exits in the absence of any
// wait channel being registered with its runtime.
func TestNoWaiters(t *testing.T) {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(os.Stderr, os.Stderr)
h, err := sh.Start(noWaitersCmd, nil)
if err != nil {
@@ -143,7 +146,10 @@
// TestForceStop verifies that ForceStop causes the child process to exit
// immediately.
func TestForceStop(t *testing.T) {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(os.Stderr, os.Stderr)
h, err := sh.Start(forceStopCmd, nil)
if err != nil {
@@ -295,7 +301,10 @@
childcreds := security.NewVeyronCredentials(r.Principal(), appCmd)
configServer, configServiceName, ch := createConfigServer(t, r)
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
sh.SetVar(consts.VeyronCredentials, childcreds)
sh.SetConfigKey(mgmt.ParentNameConfigKey, configServiceName)
sh.SetConfigKey(mgmt.ProtocolConfigKey, "tcp")
@@ -335,7 +344,6 @@
if err == nil || err.Error() != want {
t.Errorf("got %v, want %s", err, want)
}
-
}
// TestRemoteStop verifies that the child shuts down cleanly when sending it
diff --git a/runtimes/google/rt/rt_test.go b/runtimes/google/rt/rt_test.go
index 3f244fb..eb8e14b 100644
--- a/runtimes/google/rt/rt_test.go
+++ b/runtimes/google/rt/rt_test.go
@@ -26,7 +26,6 @@
testutil.Init()
modules.RegisterChild("child", "", child)
modules.RegisterChild("principal", "", principal)
- modules.RegisterChild("mutate", "", mutatePrincipal)
modules.RegisterChild("runner", "", runner)
}
@@ -43,7 +42,7 @@
args := fmt.Sprintf("%s", l)
expected := regexp.MustCompile("name=veyron logdirs=\\[/tmp\\] logtostderr=true|false alsologtostderr=false|true max_stack_buf_size=4292608 v=[0-9] stderrthreshold=2 vmodule= log_backtrace_at=:0")
if !expected.MatchString(args) {
- t.Errorf("unexpected default args: %q", args)
+ t.Errorf("unexpected default args: %s", args)
}
p := r.Principal()
if p == nil {
@@ -70,9 +69,12 @@
}
func TestInitArgs(t *testing.T) {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(os.Stderr, os.Stderr)
- h, err := sh.Start("child", nil, "--logtostderr=true", "--vv=3", "--", "foobar")
+ h, err := sh.Start("child", nil, "--logtostderr=true", "--vmodule=*=3", "--", "foobar")
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -82,9 +84,9 @@
"logtostderr=true "+
"alsologtostderr=true "+
"max_stack_buf_size=4292608 "+
- "v=3 "+
+ "v=0 "+
"stderrthreshold=2 "+
- "vmodule= "+
+ "vmodule=*=3 "+
"log_backtrace_at=:0",
os.TempDir()))
h.CloseStdin()
@@ -100,7 +102,6 @@
blessings := p.BlessingStore().Default()
if blessings == nil {
return fmt.Errorf("rt.Principal().BlessingStore().Default() returned nil")
-
}
ctx := security.NewContext(&security.ContextParams{LocalPrincipal: p})
if n := len(blessings.ForContext(ctx)); n != 1 {
@@ -109,6 +110,10 @@
return nil
}
+func defaultBlessing(p security.Principal) string {
+ return p.BlessingStore().Default().ForContext(security.NewContext(&security.ContextParams{LocalPrincipal: p}))[0]
+}
+
func tmpDir(t *testing.T) string {
dir, err := ioutil.TempDir("", "rt_test_dir")
if err != nil {
@@ -119,10 +124,10 @@
func principal(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
r := rt.Init()
- err := validatePrincipal(r.Principal())
- fmt.Fprintf(stdout, "ERROR=%v\n", err)
- fmt.Fprintf(stdout, "PUBKEY=%s\n", r.Principal().PublicKey())
- modules.WaitForEOF(stdin)
+ if err := validatePrincipal(r.Principal()); err != nil {
+ return err
+ }
+ fmt.Fprintf(stdout, "DEFAULT_BLESSING=%s\n", defaultBlessing(r.Principal()))
return nil
}
@@ -130,77 +135,48 @@
// own security info and it's childs.
func runner(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
r := rt.Init()
- err := validatePrincipal(r.Principal())
- fmt.Fprintf(stdout, "RUNNER_ERROR=%v\n", err)
- fmt.Fprintf(stdout, "RUNNER_PUBKEY=%s\n", r.Principal().PublicKey())
+ if err := validatePrincipal(r.Principal()); err != nil {
+ return err
+ }
+ fmt.Fprintf(stdout, "RUNNER_DEFAULT_BLESSING=%v\n", defaultBlessing(r.Principal()))
+ sh, err := modules.NewShell(nil)
if err != nil {
return err
}
- sh := modules.NewShell()
- _, err = sh.Start("principal", nil, args[1:]...)
- if err != nil {
+ if _, err := sh.Start("principal", nil, args[1:]...); err != nil {
return err
}
// Cleanup copies the output of sh to these Writers.
sh.Cleanup(stdout, stderr)
- modules.WaitForEOF(stdin)
return nil
}
-func mutatePrincipal(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
- r := rt.Init()
-
- rtPrincipal := r.Principal()
- err := validatePrincipal(rtPrincipal)
- fmt.Fprintf(stdout, "ERROR=%v\n", err)
-
- // Mutate the roots and store of this principal.
- blessing, err := rtPrincipal.BlessSelf("irrelevant")
- if err != nil {
- return err
- }
- if _, err := rtPrincipal.BlessingStore().Set(blessing, security.AllPrincipals); err != nil {
- return err
- }
- if err := rtPrincipal.AddToRoots(blessing); err != nil {
- return err
- }
- newRT, err := rt.New(profileOpt)
- if err != nil {
- return fmt.Errorf("rt.New failed: %v", err)
- }
- // Test that the same principal gets initialized on creating a new runtime
- // from the same credentials directory.
- if got := newRT.Principal(); !reflect.DeepEqual(got, rtPrincipal) {
- return fmt.Errorf("Initialized Principal: %v, expected: %v", got.PublicKey(), rtPrincipal.PublicKey())
- }
- fmt.Fprintf(stdout, "PUBKEY=%s\n", newRT.Principal().PublicKey())
- modules.WaitForEOF(stdin)
- return nil
-}
-
-func createCredentialsInDir(t *testing.T, dir string) security.Principal {
+func createCredentialsInDir(t *testing.T, dir string, blessing string) {
principal, err := vsecurity.CreatePersistentPrincipal(dir, nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
- vsecurity.InitDefaultBlessings(principal, "test")
- return principal
+ if err := vsecurity.InitDefaultBlessings(principal, blessing); err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
}
func TestPrincipalInheritance(t *testing.T) {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer func() {
sh.Cleanup(os.Stdout, os.Stderr)
}()
- // Test that the child inherits the parent's credentials correctly.
+ // Test that the child inherits from the parent's credentials correctly.
// The running test process may or may not have a credentials directory set
// up so we have to use a 'runner' process to ensure the correct setup.
cdir := tmpDir(t)
defer os.RemoveAll(cdir)
- principal := createCredentialsInDir(t, cdir)
+ createCredentialsInDir(t, cdir, "test")
// directory supplied by the environment.
credEnv := []string{consts.VeyronCredentials + "=" + cdir}
@@ -209,41 +185,33 @@
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
- s := expect.NewSession(t, h.Stdout(), 2*time.Second) //time.Minute)
- runnerErr := s.ExpectVar("RUNNER_ERROR")
- runnerPubKey := s.ExpectVar("RUNNER_PUBKEY")
- principalErr := s.ExpectVar("ERROR")
- principalPubKey := s.ExpectVar("PUBKEY")
+
+ s := expect.NewSession(t, h.Stdout(), time.Minute)
+ runnerBlessing := s.ExpectVar("RUNNER_DEFAULT_BLESSING")
+ principalBlessing := s.ExpectVar("DEFAULT_BLESSING")
if err := s.Error(); err != nil {
t.Fatalf("failed to read input from children: %s", err)
}
h.Shutdown(os.Stdout, os.Stderr)
- if runnerErr != "<nil>" || principalErr != "<nil>" {
- t.Fatalf("unexpected error: runner %q, principal %q", runnerErr, principalErr)
- }
- pubKey := principal.PublicKey().String()
- if runnerPubKey != pubKey || principalPubKey != pubKey {
- t.Fatalf("unexpected pubkeys: expected %s: runner %s, principal %s",
- pubKey, runnerPubKey, principalPubKey)
+
+ wantRunnerBlessing := "test"
+ wantPrincipalBlessing := "test/child"
+ if runnerBlessing != wantRunnerBlessing || principalBlessing != wantPrincipalBlessing {
+ t.Fatalf("unexpected default blessing: got runner %s, principal %s, want runner %s, principal %s", runnerBlessing, principalBlessing, wantRunnerBlessing, wantPrincipalBlessing)
}
}
func TestPrincipalInit(t *testing.T) {
- // Collet the process' public key and error status
- collect := func(sh *modules.Shell, cmd string, env []string, args ...string) (string, error) {
- h, err := sh.Start(cmd, env, args...)
+ // Collect the process' public key and error status
+ collect := func(sh *modules.Shell, env []string, args ...string) string {
+ h, err := sh.Start("principal", env, args...)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
s := expect.NewSession(t, h.Stdout(), time.Minute)
s.SetVerbosity(testing.Verbose())
- errstr := s.ExpectVar("ERROR")
- pubkey := s.ExpectVar("PUBKEY")
- if errstr != "<nil>" {
- return pubkey, fmt.Errorf("%s", errstr)
- }
- return pubkey, nil
+ return s.ExpectVar("DEFAULT_BLESSING")
}
// A credentials directory may, or may, not have been already specified.
@@ -257,55 +225,36 @@
t.Fatal(err)
}
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(os.Stderr, os.Stderr)
- pubkey, err := collect(sh, "principal", nil)
- if err != nil {
- t.Fatalf("child failed to create+validate principal: %v", err)
- }
- if len(pubkey) == 0 {
- t.Fatalf("child failed to return a public key")
+ blessing := collect(sh, nil)
+ if len(blessing) == 0 {
+ t.Fatalf("child returned an empty default blessings set")
}
- // Test specifying credentials via VEYRON_CREDENTIALS
+ // Test specifying credentials via VEYRON_CREDENTIALS environment.
cdir1 := tmpDir(t)
defer os.RemoveAll(cdir1)
- principal := createCredentialsInDir(t, cdir1)
- // directory supplied by the environment.
+ createCredentialsInDir(t, cdir1, "test_env")
credEnv := []string{consts.VeyronCredentials + "=" + cdir1}
- pubkey, err = collect(sh, "principal", credEnv)
- if err != nil {
- t.Errorf("unexpected error: %s", err)
- }
-
- if got, want := pubkey, principal.PublicKey().String(); got != want {
- t.Errorf("got %q, want %q", got, want)
+ blessing = collect(sh, credEnv)
+ if got, want := blessing, "test_env"; got != want {
+ t.Errorf("got default blessings: %q, want %q", got, want)
}
// Test specifying credentials via the command line and that the
// comand line overrides the environment
cdir2 := tmpDir(t)
defer os.RemoveAll(cdir2)
- clPrincipal := createCredentialsInDir(t, cdir2)
+ createCredentialsInDir(t, cdir2, "test_cmd")
- pubkey, err = collect(sh, "principal", credEnv, "--veyron.credentials="+cdir2)
- if err != nil {
- t.Errorf("unexpected error: %s", err)
- }
-
- if got, want := pubkey, clPrincipal.PublicKey().String(); got != want {
- t.Errorf("got %q, want %q", got, want)
- }
-
- // Mutate the roots and store of the principal in the child process.
- pubkey, err = collect(sh, "mutate", credEnv, "--veyron.credentials="+cdir2)
- if err != nil {
- t.Errorf("unexpected error: %s", err)
- }
-
- if got, want := pubkey, clPrincipal.PublicKey().String(); got != want {
+ blessing = collect(sh, credEnv, "--veyron.credentials="+cdir2)
+ if got, want := blessing, "test_cmd"; got != want {
t.Errorf("got %q, want %q", got, want)
}
}
diff --git a/runtimes/google/rt/shutdown_servers_test.go b/runtimes/google/rt/shutdown_servers_test.go
index 3e3f730..cba2006 100644
--- a/runtimes/google/rt/shutdown_servers_test.go
+++ b/runtimes/google/rt/shutdown_servers_test.go
@@ -47,7 +47,6 @@
func remoteCmdLoop(stdin io.Reader) func() {
done := make(chan struct{})
go func() {
- defer close(done)
scanner := bufio.NewScanner(stdin)
for scanner.Scan() {
switch scanner.Text() {
@@ -57,6 +56,7 @@
fmt.Println("straight exit")
rt.R().AppCycle().ForceStop()
case "close":
+ close(done)
return
}
}
@@ -76,7 +76,7 @@
// commands from the parent process to simulate Stop and
// RemoteStop commands that would normally be issued from
// application code.
- go remoteCmdLoop(stdin)()
+ defer remoteCmdLoop(stdin)()
// r.Cleanup is optional, but it's a good idea to clean up, especially
// since it takes care of flushing the logs before exiting.
@@ -128,7 +128,7 @@
case <-stopChan:
}
<-blockingCh
- os.Exit(1)
+ os.Exit(signals.DoubleStopExitCode)
}()
// This communicates to the parent test driver process in our unit test
@@ -219,7 +219,7 @@
// commands from the parent process to simulate Stop and
// RemoteStop commands that would normally be issued from
// application code.
- go remoteCmdLoop(stdin)()
+ defer remoteCmdLoop(stdin)()
// r.Cleanup is optional, but it's a good idea to clean up, especially
// since it takes care of flushing the logs before exiting.
@@ -238,7 +238,7 @@
// Note, if the developer wants to exit immediately upon receiving a
// signal or stop command, they can skip this, in which case the default
// behavior is for the process to exit.
- waiter := signals.ShutdownOnSignals()
+ waiter := signals.ShutdownOnSignals(r)
// This communicates to the parent test driver process in our unit test
// that this server is ready and waiting on signals or stop commands.
diff --git a/runtimes/google/rt/shutdown_test.go b/runtimes/google/rt/shutdown_test.go
index f0a57aa..4ee9468 100644
--- a/runtimes/google/rt/shutdown_test.go
+++ b/runtimes/google/rt/shutdown_test.go
@@ -25,10 +25,18 @@
}
}
+func newShell(t *testing.T) *modules.Shell {
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ return sh
+}
+
// TestSimpleServerSignal verifies that sending a signal to the simple server
// causes it to exit cleanly.
func TestSimpleServerSignal(t *testing.T) {
- sh := modules.NewShell()
+ sh := newShell(t)
defer sh.Cleanup(os.Stdout, cstderr)
h, _ := sh.Start("simpleServerProgram", nil)
s := expect.NewSession(t, h.Stdout(), time.Minute)
@@ -44,7 +52,7 @@
// TestSimpleServerLocalStop verifies that sending a local stop command to the
// simple server causes it to exit cleanly.
func TestSimpleServerLocalStop(t *testing.T) {
- sh := modules.NewShell()
+ sh := newShell(t)
defer sh.Cleanup(os.Stdout, cstderr)
h, _ := sh.Start("simpleServerProgram", nil)
s := expect.NewSession(t, h.Stdout(), time.Minute)
@@ -61,7 +69,7 @@
// signals to the simple server causes it to initiate the cleanup sequence on
// the first signal and then exit immediately on the second signal.
func TestSimpleServerDoubleSignal(t *testing.T) {
- sh := modules.NewShell()
+ sh := newShell(t)
defer sh.Cleanup(os.Stdout, cstderr)
h, _ := sh.Start("simpleServerProgram", nil)
s := expect.NewSession(t, h.Stdout(), time.Minute)
@@ -81,7 +89,7 @@
// TestSimpleServerLocalForceStop verifies that sending a local ForceStop
// command to the simple server causes it to exit immediately.
func TestSimpleServerLocalForceStop(t *testing.T) {
- sh := modules.NewShell()
+ sh := newShell(t)
defer sh.Cleanup(os.Stdout, cstderr)
h, _ := sh.Start("simpleServerProgram", nil)
s := expect.NewSession(t, h.Stdout(), time.Minute)
@@ -100,7 +108,7 @@
// TestSimpleServerKill demonstrates that a SIGKILL still forces the server
// to exit regardless of our signal handling.
func TestSimpleServerKill(t *testing.T) {
- sh := modules.NewShell()
+ sh := newShell(t)
defer sh.Cleanup(os.Stdout, cstderr)
h, _ := sh.Start("simpleServerProgram", nil)
s := expect.NewSession(t, h.Stdout(), time.Minute)
@@ -120,7 +128,7 @@
// corresponding to all the simulated sequential/parallel and
// blocking/interruptible shutdown steps), and then exits cleanly.
func TestComplexServerSignal(t *testing.T) {
- sh := modules.NewShell()
+ sh := newShell(t)
defer sh.Cleanup(os.Stdout, cstderr)
h, _ := sh.Start("complexServerProgram", nil)
s := expect.NewSession(t, h.Stdout(), time.Minute)
@@ -142,7 +150,7 @@
// printouts corresponding to all the simulated sequential/parallel and
// blocking/interruptible shutdown steps), and then exits cleanly.
func TestComplexServerLocalStop(t *testing.T) {
- sh := modules.NewShell()
+ sh := newShell(t)
defer sh.Cleanup(os.Stdout, cstderr)
h, _ := sh.Start("complexServerProgram", nil)
s := expect.NewSession(t, h.Stdout(), time.Minute)
@@ -169,7 +177,7 @@
// the corresponding printouts from the server). Note that we have no
// expectations on whether or not the interruptible shutdown steps execute.
func TestComplexServerDoubleSignal(t *testing.T) {
- sh := modules.NewShell()
+ sh := newShell(t)
defer sh.Cleanup(os.Stdout, cstderr)
h, _ := sh.Start("complexServerProgram", nil)
s := expect.NewSession(t, h.Stdout(), time.Minute)
@@ -193,7 +201,7 @@
// TestComplexServerLocalForceStop verifies that sending a local ForceStop
// command to the complex server forces it to exit immediately.
func TestComplexServerLocalForceStop(t *testing.T) {
- sh := modules.NewShell()
+ sh := newShell(t)
defer sh.Cleanup(os.Stdout, cstderr)
h, _ := sh.Start("complexServerProgram", nil)
s := expect.NewSession(t, h.Stdout(), time.Minute)
@@ -212,7 +220,7 @@
// TestComplexServerKill demonstrates that a SIGKILL still forces the server to
// exit regardless of our signal handling.
func TestComplexServerKill(t *testing.T) {
- sh := modules.NewShell()
+ sh := newShell(t)
defer sh.Cleanup(os.Stdout, cstderr)
h, _ := sh.Start("complexServerProgram", nil)
s := expect.NewSession(t, h.Stdout(), time.Minute)
diff --git a/runtimes/google/rt/signal_test.go b/runtimes/google/rt/signal_test.go
index df93748..2d819a9 100644
--- a/runtimes/google/rt/signal_test.go
+++ b/runtimes/google/rt/signal_test.go
@@ -73,7 +73,10 @@
}
func TestWithRuntime(t *testing.T) {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(os.Stderr, os.Stderr)
h, err := sh.Start("withRuntime", nil)
if err != nil {
@@ -90,7 +93,10 @@
}
func TestWithoutRuntime(t *testing.T) {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
defer sh.Cleanup(os.Stderr, os.Stderr)
h, err := sh.Start("withoutRuntime", nil)
if err != nil {
diff --git a/runtimes/google/testing/mocks/naming/namespace.go b/runtimes/google/testing/mocks/naming/namespace.go
index bff47f6..e3403e5 100644
--- a/runtimes/google/testing/mocks/naming/namespace.go
+++ b/runtimes/google/testing/mocks/naming/namespace.go
@@ -8,7 +8,7 @@
"veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/naming"
- "veyron.io/veyron/veyron2/verror"
+ verror "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
)
@@ -57,7 +57,7 @@
return nil
}
-func (ns *namespace) Resolve(ctx context.T, name string) ([]string, error) {
+func (ns *namespace) Resolve(ctx context.T, name string, opts ...naming.ResolveOpt) ([]string, error) {
defer vlog.LogCall()()
if address, _ := naming.SplitAddressName(name); len(address) > 0 {
return []string{name}, nil
@@ -74,10 +74,10 @@
return ret, nil
}
}
- return nil, verror.NoExistf("Resolve name %q not found in %v", name, ns.mounts)
+ return nil, verror.Make(naming.ErrNoSuchName, ctx, fmt.Sprintf("Resolve name %q not found in %v", name, ns.mounts))
}
-func (ns *namespace) ResolveX(ctx context.T, name string) (*naming.MountEntry, error) {
+func (ns *namespace) ResolveX(ctx context.T, name string, opts ...naming.ResolveOpt) (*naming.MountEntry, error) {
defer vlog.LogCall()()
e := new(naming.MountEntry)
if address, _ := naming.SplitAddressName(name); len(address) > 0 {
@@ -95,17 +95,17 @@
return e, nil
}
}
- return nil, verror.NoExistf("Resolve name %q not found in %v", name, ns.mounts)
+ return nil, verror.Make(naming.ErrNoSuchName, ctx, fmt.Sprintf("Resolve name %q not found in %v", name, ns.mounts))
}
-func (ns *namespace) ResolveToMountTableX(ctx context.T, name string) (*naming.MountEntry, error) {
+func (ns *namespace) ResolveToMountTableX(ctx context.T, name string, opts ...naming.ResolveOpt) (*naming.MountEntry, error) {
defer vlog.LogCall()()
// TODO(mattr): Implement this method for tests that might need it.
panic("ResolveToMountTable not implemented")
return nil, nil
}
-func (ns *namespace) ResolveToMountTable(ctx context.T, name string) ([]string, error) {
+func (ns *namespace) ResolveToMountTable(ctx context.T, name string, opts ...naming.ResolveOpt) ([]string, error) {
defer vlog.LogCall()()
// TODO(mattr): Implement this method for tests that might need it.
panic("ResolveToMountTable not implemented")
diff --git a/runtimes/google/testing/mocks/runtime/panic_runtime.go b/runtimes/google/testing/mocks/runtime/panic_runtime.go
index d70852b..292beef 100644
--- a/runtimes/google/testing/mocks/runtime/panic_runtime.go
+++ b/runtimes/google/testing/mocks/runtime/panic_runtime.go
@@ -8,6 +8,7 @@
"veyron.io/veyron/veyron2/ipc/stream"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/uniqueid"
"veyron.io/veyron/veyron2/vlog"
"veyron.io/veyron/veyron2/vtrace"
)
@@ -17,20 +18,23 @@
// implementation but you don't want it to be used.
type PanicRuntime struct {
unique int // Make non-empty to ensure pointer instances are unique.
+
}
const badRuntime = "The runtime implmentation should not call methods on runtime intances."
-func (*PanicRuntime) Profile() veyron2.Profile { panic(badRuntime) }
-func (*PanicRuntime) AppCycle() veyron2.AppCycle { panic(badRuntime) }
-func (*PanicRuntime) Publisher() *config.Publisher { panic(badRuntime) }
-func (*PanicRuntime) Principal() security.Principal { panic(badRuntime) }
-func (*PanicRuntime) NewClient(opts ...ipc.ClientOpt) (ipc.Client, error) { panic(badRuntime) }
-func (*PanicRuntime) NewServer(opts ...ipc.ServerOpt) (ipc.Server, error) { panic(badRuntime) }
-func (*PanicRuntime) Client() ipc.Client { panic(badRuntime) }
-func (*PanicRuntime) NewContext() context.T { panic(badRuntime) }
-func (*PanicRuntime) WithNewSpan(context.T, string) (context.T, vtrace.Span) { panic(badRuntime) }
-func (*PanicRuntime) SpanFromContext(context.T) vtrace.Span { panic(badRuntime) }
+func (*PanicRuntime) Profile() veyron2.Profile { panic(badRuntime) }
+func (*PanicRuntime) AppCycle() veyron2.AppCycle { panic(badRuntime) }
+func (*PanicRuntime) Publisher() *config.Publisher { panic(badRuntime) }
+func (*PanicRuntime) Principal() security.Principal { panic(badRuntime) }
+func (*PanicRuntime) NewClient(opts ...ipc.ClientOpt) (ipc.Client, error) { panic(badRuntime) }
+func (*PanicRuntime) NewServer(opts ...ipc.ServerOpt) (ipc.Server, error) { panic(badRuntime) }
+func (*PanicRuntime) Client() ipc.Client { panic(badRuntime) }
+func (*PanicRuntime) NewContext() context.T { panic(badRuntime) }
+
+func (PanicRuntime) WithNewSpan(c context.T, m string) (context.T, vtrace.Span) { return c, &span{m} }
+
+func (*PanicRuntime) SpanFromContext(context.T) vtrace.Span { return &span{} }
func (*PanicRuntime) NewStreamManager(opts ...stream.ManagerOpt) (stream.Manager, error) {
panic(badRuntime)
}
@@ -45,3 +49,13 @@
}
func (*PanicRuntime) VtraceStore() vtrace.Store { panic(badRuntime) }
func (*PanicRuntime) Cleanup() { panic(badRuntime) }
+
+type span struct{ m string }
+
+func (s *span) Name() string { return s.m + ".panic" }
+func (*span) ID() uniqueid.ID { return uniqueid.ID{} }
+func (s *span) Parent() uniqueid.ID { return s.ID() }
+func (*span) Annotate(string) {}
+func (*span) Annotatef(string, ...interface{}) {}
+func (*span) Finish() {}
+func (*span) Trace() vtrace.Trace { return nil }
diff --git a/runtimes/google/vtrace/store.go b/runtimes/google/vtrace/store.go
index f70dc2a..7ab0a0a 100644
--- a/runtimes/google/vtrace/store.go
+++ b/runtimes/google/vtrace/store.go
@@ -62,7 +62,7 @@
s.mu.Lock()
defer s.mu.Unlock()
- out := make([]vtrace.TraceRecord, s.size)
+ out := make([]vtrace.TraceRecord, len(s.traces))
i := 0
for _, ts := range s.traces {
ts.traceRecord(&out[i])
diff --git a/runtimes/google/vtrace/vtrace.go b/runtimes/google/vtrace/vtrace.go
index 04da3e3..7f7a2a9 100644
--- a/runtimes/google/vtrace/vtrace.go
+++ b/runtimes/google/vtrace/vtrace.go
@@ -4,6 +4,7 @@
package vtrace
import (
+ "fmt"
"time"
"veyron.io/veyron/veyron2/context"
@@ -41,8 +42,13 @@
func (c *span) Parent() uniqueid.ID { return c.parent }
func (c *span) Name() string { return c.name }
func (c *span) Trace() vtrace.Trace { return c.collector }
-func (c *span) Annotate(msg string) { c.collector.annotate(c, msg) }
-func (c *span) Finish() { c.collector.finish(c) }
+func (c *span) Annotate(s string) {
+ c.collector.annotate(c, s)
+}
+func (c *span) Annotatef(format string, a ...interface{}) {
+ c.collector.annotate(c, fmt.Sprintf(format, a...))
+}
+func (c *span) Finish() { c.collector.finish(c) }
// Request generates a vtrace.Request from the active Span.
func Request(ctx context.T) vtrace.Request {
diff --git a/runtimes/google/vtrace/vtrace_test.go b/runtimes/google/vtrace/vtrace_test.go
index 07ebd2e..bc9cb87 100644
--- a/runtimes/google/vtrace/vtrace_test.go
+++ b/runtimes/google/vtrace/vtrace_test.go
@@ -134,7 +134,9 @@
}
func expectSequence(t *testing.T, trace vtrace.TraceRecord, expectedSpans []string) {
- if got, want := len(trace.Spans), len(expectedSpans); got != want {
+ // It's okay to have additional spans - someone may have inserted
+ // additional spans for more debugging.
+ if got, want := len(trace.Spans), len(expectedSpans); got < want {
t.Errorf("Found %d spans, want %d", got, want)
}
diff --git a/security/acl/acl.go b/security/acl/acl.go
deleted file mode 100644
index 3971530..0000000
--- a/security/acl/acl.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package acl
-
-import (
- "encoding/json"
- "io"
-
- "veyron.io/veyron/veyron2/security"
-)
-
-// Includes returns true iff the ACL grants access to a principal
-// that presents blessings.
-func (acl ACL) Includes(blessings ...string) bool {
- blessings = pruneBlacklisted(acl.NotIn, blessings)
- for _, pattern := range acl.In {
- if pattern.MatchedBy(blessings...) {
- return true
- }
- }
- return false
-}
-
-// WriteTo writes the JSON-encoded representation of a TaggedACLMap to w.
-func (m TaggedACLMap) WriteTo(w io.Writer) error {
- return json.NewEncoder(w).Encode(m)
-}
-
-// ReadTaggedACLMap reads the JSON-encoded representation of a TaggedACLMap from r.
-func ReadTaggedACLMap(r io.Reader) (m TaggedACLMap, err error) {
- err = json.NewDecoder(r).Decode(&m)
- return
-}
-
-func pruneBlacklisted(blacklist, blessings []string) []string {
- if len(blacklist) == 0 {
- return blessings
- }
- var filtered []string
- for _, b := range blessings {
- if !security.BlessingPattern(b).MatchedBy(blacklist...) {
- filtered = append(filtered, b)
- }
- }
- return filtered
-}
diff --git a/security/acl/acl.vdl b/security/acl/acl.vdl
deleted file mode 100644
index 51bc01c..0000000
--- a/security/acl/acl.vdl
+++ /dev/null
@@ -1,38 +0,0 @@
-// Package acl defines types and methods to represent Access Control Lists and enforce authorization policies based on them.
-package acl
-
-import "veyron.io/veyron/veyron2/security"
-
-// ACL represents an Access Control List - a set of blessings that should be
-// granted access.
-type ACL struct {
- // In denotes the set of blessings (represented as BlessingPatterns) that
- // should be granted access, unless blacklisted by an entry in NotIn.
- //
- // For example:
- // In: {"alice/family/..."}
- // grants access to a principal that presents at least one of "alice",
- // "alice/family", "alice/family/friend" etc. as a blessing.
- In []security.BlessingPattern
-
- // NotIn denotes the set of blessings (and their delegates) that
- // have been explicitly blacklisted from the In set.
- //
- // For example:
- // In: {"alice/friend/..."}, NotIn: {"alice/friend/bob"}
- // grants access to principals that present "alice", "alice/friend",
- // "alice/friend/carol" etc. but NOT to a principal that presents
- // "alice/friend/bob" or "alice/friend/bob/spouse" etc.
- NotIn []string
-
- // TODO(ashankar,ataly): At some point, introduce group identifiers here?
-}
-
-// TaggedACLMap maps string tags to access control lists specifying the
-// blessings required to invoke methods with that tag.
-//
-// These tags are meant to add a layer of interposition between set of users
-// (blessings, specifically) and the set of methods, much like "Roles" do in
-// Role Based Access Control (RBAC).
-// (http://en.wikipedia.org/wiki/Role-based_access_control)
-type TaggedACLMap map[string]ACL
diff --git a/security/acl/acl.vdl.go b/security/acl/acl.vdl.go
deleted file mode 100644
index 4a658de..0000000
--- a/security/acl/acl.vdl.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// This file was auto-generated by the veyron vdl tool.
-// Source: acl.vdl
-
-// Package acl defines types and methods to represent Access Control Lists and enforce authorization policies based on them.
-package acl
-
-import (
- "veyron.io/veyron/veyron2/security"
-)
-
-// ACL represents an Access Control List - a set of blessings that should be
-// granted access.
-type ACL struct {
- // In denotes the set of blessings (represented as BlessingPatterns) that
- // should be granted access, unless blacklisted by an entry in NotIn.
- //
- // For example:
- // In: {"alice/family/..."}
- // grants access to a principal that presents at least one of "alice",
- // "alice/family", "alice/family/friend" etc. as a blessing.
- In []security.BlessingPattern
- // NotIn denotes the set of blessings (and their delegates) that
- // have been explicitly blacklisted from the In set.
- //
- // For example:
- // In: {"alice/friend/..."}, NotIn: {"alice/friend/bob"}
- // grants access to principals that present "alice", "alice/friend",
- // "alice/friend/carol" etc. but NOT to a principal that presents
- // "alice/friend/bob" or "alice/friend/bob/spouse" etc.
- NotIn []string
-}
-
-// TaggedACLMap maps string tags to access control lists specifying the
-// blessings required to invoke methods with that tag.
-//
-// These tags are meant to add a layer of interposition between set of users
-// (blessings, specifically) and the set of methods, much like "Roles" do in
-// Role Based Access Control (RBAC).
-// (http://en.wikipedia.org/wiki/Role-based_access_control)
-type TaggedACLMap map[string]ACL
diff --git a/security/acl/acl_test.go b/security/acl/acl_test.go
deleted file mode 100644
index 4187b18..0000000
--- a/security/acl/acl_test.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package acl
-
-import (
- "bytes"
- "reflect"
- "testing"
-
- "veyron.io/veyron/veyron2/security"
-)
-
-func TestInclude(t *testing.T) {
- acl := ACL{
- In: []security.BlessingPattern{"alice", "alice/friend/...", "bob/family/..."},
- NotIn: []string{"alice/friend/carol", "bob/family/mallory"},
- }
- type V []string // shorthand
- tests := []struct {
- Blessings []string
- Want bool
- }{
- {nil, false}, // No blessings presented, cannot access
- {V{}, false},
- {V{"alice"}, true},
- {V{"bob"}, true},
- {V{"carol"}, false},
- {V{"alice/colleague"}, false},
- {V{"alice", "carol/friend"}, true}, // Presenting one blessing that grants access is sufficient
- {V{"alice/friend/bob"}, true},
- {V{"alice/friend/carol"}, false}, // alice/friend/carol is blacklisted
- {V{"alice/friend/carol/family"}, false}, // alice/friend/carol is blacklisted, thus her delegates must be too.
- {V{"alice/friend/bob", "alice/friend/carol"}, true},
- {V{"bob/family/eve", "bob/family/mallory"}, true},
- {V{"bob/family/mallory", "alice/friend/carol"}, false},
- }
- for _, test := range tests {
- if got, want := acl.Includes(test.Blessings...), test.Want; got != want {
- t.Errorf("Includes(%v): Got %v, want %v", test.Blessings, got, want)
- }
- }
-}
-
-func TestOpenACL(t *testing.T) {
- acl := ACL{In: []security.BlessingPattern{security.AllPrincipals}}
- if !acl.Includes() {
- t.Errorf("OpenACL should allow principals that present no blessings")
- }
- if !acl.Includes("frank") {
- t.Errorf("OpenACL should allow principals that present any blessings")
- }
-}
-
-func TestTaggedACLMapSerialization(t *testing.T) {
- obj := TaggedACLMap{
- "R": ACL{
- In: []security.BlessingPattern{"foo/...", "bar/..."},
- NotIn: []string{"bar/baz"},
- },
- "W": ACL{
- In: []security.BlessingPattern{"foo/...", "bar"},
- NotIn: []string{"foo/bar", "foo/baz/boz"},
- },
- }
- txt := `
-{
- "R": {
- "In":["foo/...","bar/..."],
- "NotIn":["bar/baz"]
- },
- "W": {
- "In":["foo/...","bar"],
- "NotIn":["foo/bar","foo/baz/boz"]
- }
-}
-`
- if got, err := ReadTaggedACLMap(bytes.NewBufferString(txt)); err != nil || !reflect.DeepEqual(got, obj) {
- t.Errorf("Got error %v, TaggedACLMap: %v, want %v", err, got, obj)
- }
- // And round-trip (don't compare with 'txt' because indentation/spacing might differ).
- var buf bytes.Buffer
- if err := obj.WriteTo(&buf); err != nil {
- t.Fatal(err)
- }
- if got, err := ReadTaggedACLMap(&buf); err != nil || !reflect.DeepEqual(got, obj) {
- t.Errorf("Got error %v, TaggedACLMap: %v, want %v", err, got, obj)
- }
-}
diff --git a/security/acl/authorizer.go b/security/acl/authorizer.go
deleted file mode 100644
index f4d60c9..0000000
--- a/security/acl/authorizer.go
+++ /dev/null
@@ -1,150 +0,0 @@
-package acl
-
-import (
- "fmt"
- "os"
- "reflect"
-
- "veyron.io/veyron/veyron2/security"
-)
-
-// TaggedACLAuthorizer implements an authorization policy where access is
-// granted if the remote end presents blessings included in the Access Control
-// Lists (ACLs) associated with the set of relevant tags.
-//
-// The set of relevant tags is the subset of tags associated with the
-// method (security.Context.MethodTags) that have the same type as tagType.
-// Currently, tagType.Kind must be reflect.String, i.e., only tags that are
-// named string types are supported.
-//
-// If multiple tags of tagType are associated with the method, then access is
-// granted if the peer presents blessings that match the ACLs of each one of
-// those tags. If no tags of tagType are associated with the method, then
-// access is denied.
-//
-// If the TaggedACLMap provided is nil, then a nil authorizer is returned.
-//
-// Sample usage:
-//
-// (1) Attach tags to methods in the VDL (eg. myservice.vdl)
-// package myservice
-//
-// type MyTag string
-// const (
-// ReadAccess = MyTag("R")
-// WriteAccess = MyTag("W")
-// )
-//
-// type MyService interface {
-// Get() ([]string, error) {ReadAccess}
-// GetIndex(int) (string, error) {ReadAccess}
-//
-// Set([]string) error {WriteAccess}
-// SetIndex(int, string) error {WriteAccess}
-//
-// GetAndSet([]string) ([]string, error) {ReadAccess, WriteAccess}
-// }
-//
-// (2) Setup the ipc.Dispatcher to use the TaggedACLAuthorizer
-// import (
-// "reflect"
-// "veyron.io/veyron/veyron/security/acl"
-//
-// "veyron.io/veyron/veyron2/ipc"
-// "veyron.io/veyron/veyron2/security"
-// )
-//
-// type dispatcher struct{}
-// func (d dispatcher) Lookup(suffix, method) (ipc.Invoker, security.Authorizer, error) {
-// acl := acl.TaggedACLMap{
-// "R": acl.ACL{In: []security.BlessingPattern{"alice/friends/...", "alice/family/..."} },
-// "W": acl.ACL{In: []security.BlessingPattern{"alice/family/...", "alice/colleagues/..." } },
-// }
-// typ := reflect.TypeOf(ReadAccess) // equivalently, reflect.TypeOf(WriteAccess)
-// return newInvoker(), acl.TaggedACLAuthorizer(acl, typ), nil
-// }
-//
-// With the above dispatcher, the server will grant access to a peer with the blessing
-// "alice/friend/bob" access only to the "Get" and "GetIndex" methods. A peer presenting
-// the blessing "alice/colleague/carol" will get access only to the "Set" and "SetIndex"
-// methods. A peer presenting "alice/family/mom" will get access to all methods, even
-// GetAndSet - which requires that the blessing appear in the ACLs for both the
-// ReadAccess and WriteAccess tags.
-func TaggedACLAuthorizer(acls TaggedACLMap, tagType reflect.Type) (security.Authorizer, error) {
- if tagType.Kind() != reflect.String {
- return nil, fmt.Errorf("tag type(%v) must be backed by a string not %v", tagType, tagType.Kind())
- }
- return &authorizer{acls, tagType}, nil
-}
-
-// TaggedACLAuthorizerFromFile applies the same authorization policy as
-// TaggedACLAuthorizer, with the TaggedACLMap to be used sourced from a file named
-// filename.
-//
-// Changes to the file are monitored and affect subsequent calls to Authorize.
-// Currently, this is achieved by re-reading the file on every call to
-// Authorize.
-// TODO(ashankar,ataly): Use inotify or a similar mechanism to watch for
-// changes.
-func TaggedACLAuthorizerFromFile(filename string, tagType reflect.Type) (security.Authorizer, error) {
- if tagType.Kind() != reflect.String {
- return nil, fmt.Errorf("tag type(%v) must be backed by a string not %v", tagType, tagType.Kind())
- }
- return &fileAuthorizer{filename, tagType}, nil
-}
-
-type authorizer struct {
- acls TaggedACLMap
- tagType reflect.Type
-}
-
-func (a *authorizer) Authorize(ctx security.Context) error {
- // "Self-RPCs" are always authorized.
- if l, r := ctx.LocalBlessings(), ctx.RemoteBlessings(); l != nil && r != nil && reflect.DeepEqual(l.PublicKey(), r.PublicKey()) {
- return nil
- }
- var blessings []string
- if ctx.RemoteBlessings() != nil {
- blessings = ctx.RemoteBlessings().ForContext(ctx)
- }
- grant := false
- for _, tag := range ctx.MethodTags() {
- if v := reflect.ValueOf(tag); v.Type() == a.tagType {
- if acl, exists := a.acls[v.String()]; !exists || !acl.Includes(blessings...) {
- return errACLMatch(blessings)
- }
- grant = true
- }
- }
- if grant {
- return nil
- }
- return errACLMatch(blessings)
-}
-
-type fileAuthorizer struct {
- filename string
- tagType reflect.Type
-}
-
-func (a *fileAuthorizer) Authorize(ctx security.Context) error {
- acl, err := loadTaggedACLMapFromFile(a.filename)
- if err != nil {
- // TODO(ashankar): Information leak?
- return fmt.Errorf("failed to read ACL from file: %v", err)
- }
- return (&authorizer{acl, a.tagType}).Authorize(ctx)
-}
-
-func loadTaggedACLMapFromFile(filename string) (TaggedACLMap, error) {
- file, err := os.Open(filename)
- if err != nil {
- return nil, err
- }
- defer file.Close()
- return ReadTaggedACLMap(file)
-}
-
-func errACLMatch(blessings []string) error {
- return fmt.Errorf("%v does not match ACL", blessings)
-}
diff --git a/security/acl/authorizer_test.go b/security/acl/authorizer_test.go
deleted file mode 100644
index 27803f0..0000000
--- a/security/acl/authorizer_test.go
+++ /dev/null
@@ -1,224 +0,0 @@
-package acl
-
-import (
- "io/ioutil"
- "reflect"
- "testing"
-
- vsecurity "veyron.io/veyron/veyron/security"
- "veyron.io/veyron/veyron/security/acl/test"
- "veyron.io/veyron/veyron2/security"
-)
-
-// TestTaggedACLAuthorizer is both a test and a demonstration of the use of the
-// TaggedACLAuthorizer and interaction with interface specification in VDL.
-func TestTaggedACLAuthorizer(t *testing.T) {
- type P []security.BlessingPattern
- type S []string
- // TaggedACLMap to test against.
- acl := TaggedACLMap{
- "R": {
- In: P{"..."},
- },
- "W": {
- In: P{"ali/family/...", "bob/...", "che"},
- NotIn: S{"bob/acquaintances"},
- },
- "X": {
- In: P{"ali/family/boss", "superman"},
- },
- }
- type testcase struct {
- Method string
- Client security.Blessings
- }
- var (
- authorizer, _ = TaggedACLAuthorizer(acl, reflect.TypeOf(test.Read))
- // Two principals: The "server" and the "client"
- pserver, _ = vsecurity.NewPrincipal()
- pclient, _ = vsecurity.NewPrincipal()
- server, _ = pserver.BlessSelf("server")
-
- // B generates the provided blessings for the client and ensures
- // that the server will recognize them.
- B = func(names ...string) security.Blessings {
- var ret security.Blessings
- for _, name := range names {
- b, err := pclient.BlessSelf(name)
- if err != nil {
- t.Fatalf("%q: %v", name, err)
- }
- if err := pserver.AddToRoots(b); err != nil {
- t.Fatalf("%q: %v", name, err)
- }
- if ret, err = security.UnionOfBlessings(ret, b); err != nil {
- t.Fatal(err)
- }
- }
- return ret
- }
-
- run = func(test testcase) error {
- ctx := security.NewContext(&security.ContextParams{
- LocalPrincipal: pserver,
- LocalBlessings: server,
- RemoteBlessings: test.Client,
- Method: test.Method,
- MethodTags: methodTags(test.Method),
- })
- return authorizer.Authorize(ctx)
- }
- )
-
- // Test cases where access should be granted to methods with tags on
- // them.
- for _, test := range []testcase{
- {"Get", nil},
- {"Get", B("ali")},
- {"Get", B("bob/friend", "che/enemy")},
-
- {"Put", B("ali")},
- {"Put", B("ali/family/mom")},
- {"Put", B("bob/friends")},
- {"Put", B("bob/acquantainces/carol", "che")}, // Access granted because of "che"
-
- {"Resolve", B("ali")},
- {"Resolve", B("ali/family/boss")},
-
- {"AllTags", B("ali/family/boss")},
- } {
- if err := run(test); err != nil {
- t.Errorf("Access denied to method %q to %v: %v", test.Method, test.Client, err)
- }
- }
- // Test cases where access should be denied.
- for _, test := range []testcase{
- // Nobody is denied access to "Get"
- {"Put", B("bob/acquaintances/dave", "che/friend", "dave")},
- {"Resolve", B("ali/family/friend")},
- // Since there are no tags on the NoTags method, it has an
- // empty ACL. No client will have access.
- {"NoTags", B("ali", "ali/family/boss", "bob")},
- // On a method with multiple tags on it, all must be satisfied.
- {"AllTags", B("superman")}, // Only in the X ACL, not in R or W
- {"AllTags", B("superman", "clark")}, // In X and in R, but not W
- } {
- if err := run(test); err == nil {
- t.Errorf("Access to %q granted to %v", test.Method, test.Client)
- }
- }
-}
-
-func TestTaggedACLAuthorizerSelfRPCs(t *testing.T) {
- var (
- // Client and server are the same principal, though have
- // different blessings.
- p, _ = vsecurity.NewPrincipal()
- client, _ = p.BlessSelf("client")
- server, _ = p.BlessSelf("server")
- // Authorizer with a TaggedACLMap that grants read access to
- // anyone, write/execute access to noone.
- typ test.MyTag
- authorizer, _ = TaggedACLAuthorizer(TaggedACLMap{"R": {In: []security.BlessingPattern{"nobody"}}}, reflect.TypeOf(typ))
- )
- for _, test := range []string{"Put", "Get", "Resolve", "NoTags", "AllTags"} {
- ctx := security.NewContext(&security.ContextParams{
- LocalPrincipal: p,
- LocalBlessings: server,
- RemoteBlessings: client,
- Method: test,
- MethodTags: methodTags(test),
- })
- if err := authorizer.Authorize(ctx); err != nil {
- t.Errorf("Got error %v for method %q", err, test)
- }
- }
-}
-
-func TestTaggedACLAuthorizerWithNilACL(t *testing.T) {
- var (
- authorizer, _ = TaggedACLAuthorizer(nil, reflect.TypeOf(test.Read))
- pserver, _ = vsecurity.NewPrincipal()
- pclient, _ = vsecurity.NewPrincipal()
- server, _ = pserver.BlessSelf("server")
- client, _ = pclient.BlessSelf("client")
- )
- for _, test := range []string{"Put", "Get", "Resolve", "NoTags", "AllTags"} {
- ctx := security.NewContext(&security.ContextParams{
- LocalPrincipal: pserver,
- LocalBlessings: server,
- RemoteBlessings: client,
- Method: test,
- MethodTags: methodTags(test),
- })
- if err := authorizer.Authorize(ctx); err == nil {
- t.Errorf("nil TaggedACLMap authorized method %q", test)
- }
- }
-}
-
-func TestTaggedACLAuthorizerFromFile(t *testing.T) {
- file, err := ioutil.TempFile("", "TestTaggedACLAuthorizerFromFile")
- if err != nil {
- t.Fatal(err)
- }
- filename := file.Name()
- file.Close()
-
- var (
- authorizer, _ = TaggedACLAuthorizerFromFile(filename, reflect.TypeOf(test.Read))
- pserver, _ = vsecurity.NewPrincipal()
- pclient, _ = vsecurity.NewPrincipal()
- server, _ = pserver.BlessSelf("alice")
- alicefriend, _ = pserver.Bless(pclient.PublicKey(), server, "friend/bob", security.UnconstrainedUse())
- ctx = security.NewContext(&security.ContextParams{
- LocalPrincipal: pserver,
- LocalBlessings: server,
- RemoteBlessings: alicefriend,
- Method: "Get",
- MethodTags: methodTags("Get"),
- })
- )
- // Make pserver recognize itself as an authority on "alice/..." blessings.
- if err := pserver.AddToRoots(server); err != nil {
- t.Fatal(err)
- }
- // "alice/friend/bob" should not have access to test.Read methods like Get.
- if err := authorizer.Authorize(ctx); err == nil {
- t.Fatalf("Expected authorization error as %v is not on the ACL for Read operations", ctx.RemoteBlessings())
- }
- // Rewrite the file giving access
- if err := ioutil.WriteFile(filename, []byte(`{"R": { "In":["alice/friend/..."] }}`), 0600); err != nil {
- t.Fatal(err)
- }
- // Now should have access
- if err := authorizer.Authorize(ctx); err != nil {
- t.Fatal(err)
- }
-}
-
-func TestTagTypeMustBeString(t *testing.T) {
- type I int
- if auth, err := TaggedACLAuthorizer(TaggedACLMap{}, reflect.TypeOf(I(0))); err == nil || auth != nil {
- t.Errorf("Got (%v, %v), wanted error since tag type is not a string", auth, err)
- }
- if auth, err := TaggedACLAuthorizerFromFile("does_not_matter", reflect.TypeOf(I(0))); err == nil || auth != nil {
- t.Errorf("Got (%v, %v), wanted error since tag type is not a string", auth, err)
- }
-}
-
-func methodTags(name string) []interface{} {
- server := test.MyObjectServer(nil)
- for _, iface := range server.Describe__() {
- for _, method := range iface.Methods {
- if method.Name == name {
- tags := make([]interface{}, len(method.Tags))
- for index, tag := range method.Tags {
- tags[index] = tag
- }
- return tags
- }
- }
- }
- return nil
-}
diff --git a/security/acl/test/vdl.vdl b/security/acl/test/vdl.vdl
deleted file mode 100644
index fd3a73f..0000000
--- a/security/acl/test/vdl.vdl
+++ /dev/null
@@ -1,23 +0,0 @@
-// Package test provides a VDL specification for a service used in the unittest of the acl package.
-package test
-
-// Any package can define tags (of arbitrary types) to be attached to methods.
-// This type can be used to index into a TaggedACLMap.
-type MyTag string
-
-const (
- // For this example/unittest, there are three possible values of MyTag,
- // each represented by a single-character string.
- Read = MyTag("R")
- Write = MyTag("W")
- Execute = MyTag("X")
-)
-
-// MyObject demonstrates how tags are attached to methods.
-type MyObject interface {
- Get() error {Read}
- Put() error {Write}
- Resolve() error {Execute}
- NoTags() error // No tags attached to this.
- AllTags() error {Read, Write, Execute}
-}
diff --git a/security/acl/test/vdl.vdl.go b/security/acl/test/vdl.vdl.go
deleted file mode 100644
index 284a509..0000000
--- a/security/acl/test/vdl.vdl.go
+++ /dev/null
@@ -1,301 +0,0 @@
-// This file was auto-generated by the veyron vdl tool.
-// Source: vdl.vdl
-
-// Package test provides a VDL specification for a service used in the unittest of the acl package.
-package test
-
-import (
- // The non-user imports are prefixed with "__" to prevent collisions.
- __veyron2 "veyron.io/veyron/veyron2"
- __context "veyron.io/veyron/veyron2/context"
- __ipc "veyron.io/veyron/veyron2/ipc"
- __vdlutil "veyron.io/veyron/veyron2/vdl/vdlutil"
- __wiretype "veyron.io/veyron/veyron2/wiretype"
-)
-
-// TODO(toddw): Remove this line once the new signature support is done.
-// It corrects a bug where __wiretype is unused in VDL pacakges where only
-// bootstrap types are used on interfaces.
-const _ = __wiretype.TypeIDInvalid
-
-// Any package can define tags (of arbitrary types) to be attached to methods.
-// This type can be used to index into a TaggedACLMap.
-type MyTag string
-
-// For this example/unittest, there are three possible values of MyTag,
-// each represented by a single-character string.
-const Read = MyTag("R")
-
-const Write = MyTag("W")
-
-const Execute = MyTag("X")
-
-// MyObjectClientMethods is the client interface
-// containing MyObject methods.
-//
-// MyObject demonstrates how tags are attached to methods.
-type MyObjectClientMethods interface {
- Get(__context.T, ...__ipc.CallOpt) error
- Put(__context.T, ...__ipc.CallOpt) error
- Resolve(__context.T, ...__ipc.CallOpt) error
- NoTags(__context.T, ...__ipc.CallOpt) error // No tags attached to this.
- AllTags(__context.T, ...__ipc.CallOpt) error
-}
-
-// MyObjectClientStub adds universal methods to MyObjectClientMethods.
-type MyObjectClientStub interface {
- MyObjectClientMethods
- __ipc.UniversalServiceMethods
-}
-
-// MyObjectClient returns a client stub for MyObject.
-func MyObjectClient(name string, opts ...__ipc.BindOpt) MyObjectClientStub {
- var client __ipc.Client
- for _, opt := range opts {
- if clientOpt, ok := opt.(__ipc.Client); ok {
- client = clientOpt
- }
- }
- return implMyObjectClientStub{name, client}
-}
-
-type implMyObjectClientStub struct {
- name string
- client __ipc.Client
-}
-
-func (c implMyObjectClientStub) c(ctx __context.T) __ipc.Client {
- if c.client != nil {
- return c.client
- }
- return __veyron2.RuntimeFromContext(ctx).Client()
-}
-
-func (c implMyObjectClientStub) Get(ctx __context.T, opts ...__ipc.CallOpt) (err error) {
- var call __ipc.Call
- if call, err = c.c(ctx).StartCall(ctx, c.name, "Get", nil, opts...); err != nil {
- return
- }
- if ierr := call.Finish(&err); ierr != nil {
- err = ierr
- }
- return
-}
-
-func (c implMyObjectClientStub) Put(ctx __context.T, opts ...__ipc.CallOpt) (err error) {
- var call __ipc.Call
- if call, err = c.c(ctx).StartCall(ctx, c.name, "Put", nil, opts...); err != nil {
- return
- }
- if ierr := call.Finish(&err); ierr != nil {
- err = ierr
- }
- return
-}
-
-func (c implMyObjectClientStub) Resolve(ctx __context.T, opts ...__ipc.CallOpt) (err error) {
- var call __ipc.Call
- if call, err = c.c(ctx).StartCall(ctx, c.name, "Resolve", nil, opts...); err != nil {
- return
- }
- if ierr := call.Finish(&err); ierr != nil {
- err = ierr
- }
- return
-}
-
-func (c implMyObjectClientStub) NoTags(ctx __context.T, opts ...__ipc.CallOpt) (err error) {
- var call __ipc.Call
- if call, err = c.c(ctx).StartCall(ctx, c.name, "NoTags", nil, opts...); err != nil {
- return
- }
- if ierr := call.Finish(&err); ierr != nil {
- err = ierr
- }
- return
-}
-
-func (c implMyObjectClientStub) AllTags(ctx __context.T, opts ...__ipc.CallOpt) (err error) {
- var call __ipc.Call
- if call, err = c.c(ctx).StartCall(ctx, c.name, "AllTags", nil, opts...); err != nil {
- return
- }
- if ierr := call.Finish(&err); ierr != nil {
- err = ierr
- }
- return
-}
-
-func (c implMyObjectClientStub) Signature(ctx __context.T, opts ...__ipc.CallOpt) (o0 __ipc.ServiceSignature, err error) {
- var call __ipc.Call
- if call, err = c.c(ctx).StartCall(ctx, c.name, "Signature", nil, opts...); err != nil {
- return
- }
- if ierr := call.Finish(&o0, &err); ierr != nil {
- err = ierr
- }
- return
-}
-
-// MyObjectServerMethods is the interface a server writer
-// implements for MyObject.
-//
-// MyObject demonstrates how tags are attached to methods.
-type MyObjectServerMethods interface {
- Get(__ipc.ServerContext) error
- Put(__ipc.ServerContext) error
- Resolve(__ipc.ServerContext) error
- NoTags(__ipc.ServerContext) error // No tags attached to this.
- AllTags(__ipc.ServerContext) error
-}
-
-// MyObjectServerStubMethods is the server interface containing
-// MyObject methods, as expected by ipc.Server.
-// There is no difference between this interface and MyObjectServerMethods
-// since there are no streaming methods.
-type MyObjectServerStubMethods MyObjectServerMethods
-
-// MyObjectServerStub adds universal methods to MyObjectServerStubMethods.
-type MyObjectServerStub interface {
- MyObjectServerStubMethods
- // Describe the MyObject interfaces.
- Describe__() []__ipc.InterfaceDesc
- // Signature will be replaced with Describe__.
- Signature(ctx __ipc.ServerContext) (__ipc.ServiceSignature, error)
-}
-
-// MyObjectServer returns a server stub for MyObject.
-// It converts an implementation of MyObjectServerMethods into
-// an object that may be used by ipc.Server.
-func MyObjectServer(impl MyObjectServerMethods) MyObjectServerStub {
- stub := implMyObjectServerStub{
- impl: impl,
- }
- // Initialize GlobState; always check the stub itself first, to handle the
- // case where the user has the Glob method defined in their VDL source.
- if gs := __ipc.NewGlobState(stub); gs != nil {
- stub.gs = gs
- } else if gs := __ipc.NewGlobState(impl); gs != nil {
- stub.gs = gs
- }
- return stub
-}
-
-type implMyObjectServerStub struct {
- impl MyObjectServerMethods
- gs *__ipc.GlobState
-}
-
-func (s implMyObjectServerStub) Get(ctx __ipc.ServerContext) error {
- return s.impl.Get(ctx)
-}
-
-func (s implMyObjectServerStub) Put(ctx __ipc.ServerContext) error {
- return s.impl.Put(ctx)
-}
-
-func (s implMyObjectServerStub) Resolve(ctx __ipc.ServerContext) error {
- return s.impl.Resolve(ctx)
-}
-
-func (s implMyObjectServerStub) NoTags(ctx __ipc.ServerContext) error {
- return s.impl.NoTags(ctx)
-}
-
-func (s implMyObjectServerStub) AllTags(ctx __ipc.ServerContext) error {
- return s.impl.AllTags(ctx)
-}
-
-func (s implMyObjectServerStub) VGlob() *__ipc.GlobState {
- return s.gs
-}
-
-func (s implMyObjectServerStub) Describe__() []__ipc.InterfaceDesc {
- return []__ipc.InterfaceDesc{MyObjectDesc}
-}
-
-// MyObjectDesc describes the MyObject interface.
-var MyObjectDesc __ipc.InterfaceDesc = descMyObject
-
-// descMyObject hides the desc to keep godoc clean.
-var descMyObject = __ipc.InterfaceDesc{
- Name: "MyObject",
- PkgPath: "veyron.io/veyron/veyron/security/acl/test",
- Doc: "// MyObject demonstrates how tags are attached to methods.",
- Methods: []__ipc.MethodDesc{
- {
- Name: "Get",
- OutArgs: []__ipc.ArgDesc{
- {"", ``}, // error
- },
- Tags: []__vdlutil.Any{MyTag("R")},
- },
- {
- Name: "Put",
- OutArgs: []__ipc.ArgDesc{
- {"", ``}, // error
- },
- Tags: []__vdlutil.Any{MyTag("W")},
- },
- {
- Name: "Resolve",
- OutArgs: []__ipc.ArgDesc{
- {"", ``}, // error
- },
- Tags: []__vdlutil.Any{MyTag("X")},
- },
- {
- Name: "NoTags",
- OutArgs: []__ipc.ArgDesc{
- {"", ``}, // error
- },
- },
- {
- Name: "AllTags",
- OutArgs: []__ipc.ArgDesc{
- {"", ``}, // error
- },
- Tags: []__vdlutil.Any{MyTag("R"), MyTag("W"), MyTag("X")},
- },
- },
-}
-
-func (s implMyObjectServerStub) Signature(ctx __ipc.ServerContext) (__ipc.ServiceSignature, error) {
- // TODO(toddw): Replace with new Describe__ implementation.
- result := __ipc.ServiceSignature{Methods: make(map[string]__ipc.MethodSignature)}
- result.Methods["AllTags"] = __ipc.MethodSignature{
- InArgs: []__ipc.MethodArgument{},
- OutArgs: []__ipc.MethodArgument{
- {Name: "", Type: 65},
- },
- }
- result.Methods["Get"] = __ipc.MethodSignature{
- InArgs: []__ipc.MethodArgument{},
- OutArgs: []__ipc.MethodArgument{
- {Name: "", Type: 65},
- },
- }
- result.Methods["NoTags"] = __ipc.MethodSignature{
- InArgs: []__ipc.MethodArgument{},
- OutArgs: []__ipc.MethodArgument{
- {Name: "", Type: 65},
- },
- }
- result.Methods["Put"] = __ipc.MethodSignature{
- InArgs: []__ipc.MethodArgument{},
- OutArgs: []__ipc.MethodArgument{
- {Name: "", Type: 65},
- },
- }
- result.Methods["Resolve"] = __ipc.MethodSignature{
- InArgs: []__ipc.MethodArgument{},
- OutArgs: []__ipc.MethodArgument{
- {Name: "", Type: 65},
- },
- }
-
- result.TypeDefs = []__vdlutil.Any{
- __wiretype.NamedPrimitiveType{Type: 0x1, Name: "error", Tags: []string(nil)}}
-
- return result, nil
-}
diff --git a/security/acl_authorizer.go b/security/acl_authorizer.go
deleted file mode 100644
index 2adfa80..0000000
--- a/security/acl_authorizer.go
+++ /dev/null
@@ -1,104 +0,0 @@
-package security
-
-// This file provides an implementation of security.Authorizer.
-//
-// Definitions
-// * Self-RPC: An RPC request is said to be a "self-RPC" if the identities
-// at the local and remote ends are identical.
-
-import (
- "errors"
- "os"
- "reflect"
-
- "veyron.io/veyron/veyron2/security"
-)
-
-var (
- errACL = errors.New("no matching ACL entry found")
- errInvalidLabel = errors.New("label is invalid")
-)
-
-// aclAuthorizer implements Authorizer.
-type aclAuthorizer security.ACL
-
-// Authorize verifies a request iff the identity at the remote end has a name authorized by
-// the aclAuthorizer's ACL for the request's label, or the request corresponds to a self-RPC.
-func (a aclAuthorizer) Authorize(ctx security.Context) error {
- // Test if the request corresponds to a self-RPC.
- if ctx.LocalBlessings() != nil && ctx.RemoteBlessings() != nil && reflect.DeepEqual(ctx.LocalBlessings().PublicKey(), ctx.RemoteBlessings().PublicKey()) {
- return nil
- }
- var blessings []string
- if ctx.RemoteBlessings() != nil {
- blessings = ctx.RemoteBlessings().ForContext(ctx)
- }
- return matchesACL(blessings, ctx.Label(), security.ACL(a))
-}
-
-// NewACLAuthorizer creates an authorizer from the provided ACL. The
-// authorizer authorizes a request iff the identity at the remote end has a name
-// authorized by the provided ACL for the request's label, or the request
-// corresponds to a self-RPC.
-func NewACLAuthorizer(acl security.ACL) security.Authorizer { return aclAuthorizer(acl) }
-
-// fileACLAuthorizer implements Authorizer.
-type fileACLAuthorizer string
-
-// Authorize reads and decodes the fileACLAuthorizer's ACL file into a ACL and
-// then verifies the request according to an aclAuthorizer based on the ACL. If
-// reading or decoding the file fails then no requests are authorized.
-func (a fileACLAuthorizer) Authorize(ctx security.Context) error {
- acl, err := loadACLFromFile(string(a))
- if err != nil {
- return err
- }
- return aclAuthorizer(acl).Authorize(ctx)
-}
-
-// NewFileACLAuthorizer creates an authorizer from the provided path to a file
-// containing a JSON-encoded ACL. Each call to "Authorize" involves reading and
-// decoding a ACL from the file and then authorizing the request according to the
-// ACL. The authorizer monitors the file so out of band changes to the contents of
-// the file are reflected in the ACL. If reading or decoding the file fails then
-// no requests are authorized.
-//
-// The JSON-encoding of a ACL is essentially a JSON object describing a map from
-// BlessingPatterns to encoded LabelSets (see LabelSet.MarshalJSON).
-// Examples:
-// * `{"In": {"..." : "RW"}}` encodes an ACL that allows all principals to access all methods with
-// ReadLabel or WriteLabel.
-// * `{"In":{"veyron/alice": "RW", "veyron/bob/...": "R"}}` encodes an ACL that allows all principals
-// matched by "veyron/alice" to access methods with ReadLabel or WriteLabel, and all
-// principals matched by "veyron/bob/..." to access methods with ReadLabel.
-// * `{"In": {"...": "RW"}, "NotIn": {"veyron/alice": "W"}}` encodes an ACL that allows all principals
-// access to all ReadLabel or WriteLabel methods, EXCEPT that methods with a WriteLabel are not
-// accessible to veyron/alice and her delegates.
-// (Also see BlessingPattern.MatchedBy)
-//
-// TODO(ataly, ashankar): Instead of reading the file on each call we should use the "inotify"
-// mechanism to watch the file. Eventually we should also support ACLs stored in the Veyron
-// store.
-func NewFileACLAuthorizer(filePath string) security.Authorizer { return fileACLAuthorizer(filePath) }
-
-func matchesACL(blessings []string, label security.Label, acl security.ACL) error {
- if len(blessings) == 0 && acl.CanAccess("", label) {
- // No blessings, check if that satisfies the ACL (it will be if AllPrincipals appears in the ACL).
- return nil
- }
- for _, b := range blessings {
- if acl.CanAccess(b, label) {
- return nil
- }
- }
- return errACL
-}
-
-func loadACLFromFile(filePath string) (security.ACL, error) {
- f, err := os.Open(filePath)
- if err != nil {
- return nullACL, err
- }
- defer f.Close()
- return LoadACL(f)
-}
diff --git a/security/acl_authorizer_test.go b/security/acl_authorizer_test.go
deleted file mode 100644
index 98b0e2a..0000000
--- a/security/acl_authorizer_test.go
+++ /dev/null
@@ -1,238 +0,0 @@
-package security
-
-import (
- "io/ioutil"
- "os"
- "runtime"
- "testing"
-
- "veyron.io/veyron/veyron2/security"
-)
-
-func saveACLToTempFile(acl security.ACL) string {
- f, err := ioutil.TempFile("", "saved_acl")
- if err != nil {
- panic(err)
- }
- defer f.Close()
- if err := SaveACL(f, acl); err != nil {
- defer os.Remove(f.Name())
- panic(err)
- }
- return f.Name()
-}
-
-func updateACLInFile(fileName string, acl security.ACL) {
- f, err := os.OpenFile(fileName, os.O_WRONLY, 0600)
- if err != nil {
- panic(err)
- }
- defer f.Close()
- if err := SaveACL(f, acl); err != nil {
- panic(err)
- }
-}
-
-func testSelfRPCs(t *testing.T, authorizer security.Authorizer) {
- _, file, line, _ := runtime.Caller(1)
- var (
- pserver, server = newPrincipal("server")
- _, imposter = newPrincipal("server")
- palice, alice = newPrincipal("alice")
- aliceServer = bless(palice, pserver, alice, "server")
-
- ctxp = &security.ContextParams{LocalPrincipal: pserver, LocalBlessings: server}
- tests = []struct {
- remote security.Blessings
- isAuthorized bool
- }{
- {server, true},
- {imposter, false},
- // A principal talking to itself (even if with a different blessing) is authorized.
- // TODO(ashankar,ataly): Is this a desired property?
- {aliceServer, true},
- }
- )
- for _, test := range tests {
- ctxp.RemoteBlessings = test.remote
- ctx := security.NewContext(ctxp)
- if got, want := authorizer.Authorize(ctx), test.isAuthorized; (got == nil) != want {
- t.Errorf("%s:%d: %+v.Authorize(%v) returned error: %v, want error: %v", file, line, authorizer, ctx, got, !want)
- }
- }
-}
-
-func testNothingPermitted(t *testing.T, authorizer security.Authorizer) {
- _, file, line, _ := runtime.Caller(1)
- var (
- pserver, server = newPrincipal("server")
- palice, alice = newPrincipal("alice")
- pbob, bob = newPrincipal("random")
-
- serverAlice = bless(pserver, palice, server, "alice")
- serverAliceFriend = bless(palice, pbob, serverAlice, "friend")
- serverBob = bless(pserver, pbob, server, "bob")
-
- users = []security.Blessings{
- // blessings not recognized by "server" (since they are rooted at public
- // keys not recognized as roots by pserver)
- alice,
- bob,
- // blessings recognized by "server" (since they are its delegates)
- serverAlice,
- serverAliceFriend,
- serverBob,
- }
-
- invalidLabel = security.Label(3)
- )
- // No principal (other than the server itself - self-RPCs are allowed) should have access to any
- // valid or invalid label.
- for _, u := range users {
- for _, l := range security.ValidLabels {
- ctx := security.NewContext(&security.ContextParams{
- LocalPrincipal: pserver,
- LocalBlessings: server,
- RemoteBlessings: u,
- MethodTags: []interface{}{l},
- })
- if got := authorizer.Authorize(ctx); got == nil {
- t.Errorf("%s:%d: %+v.Authorize(%v) returns nil, want error", file, line, authorizer, ctx)
- }
- }
-
- ctx := security.NewContext(&security.ContextParams{
- LocalPrincipal: pserver,
- LocalBlessings: server,
- RemoteBlessings: u,
- MethodTags: []interface{}{invalidLabel},
- })
- if got := authorizer.Authorize(ctx); got == nil {
- t.Errorf("%s:%d: %+v.Authorize(%v) returns nil, want error", file, line, authorizer, ctx)
- }
- }
-}
-
-func TestACLAuthorizer(t *testing.T) {
- const (
- // Shorthands
- R = security.ReadLabel
- W = security.WriteLabel
- A = security.AdminLabel
- D = security.DebugLabel
- M = security.MonitoringLabel
- X = security.ResolveLabel
- )
- type Expectations map[security.Blessings]security.LabelSet
- // Principals to test
- var (
- // Principals
- pserver, server = newPrincipal("server")
- palice, alice = newPrincipal("alice")
- pbob, bob = newPrincipal("bob")
- pche, che = newPrincipal("che")
-
- // Blessings from the server
- serverAlice = bless(pserver, palice, server, "alice")
- serverBob = bless(pserver, pbob, server, "bob")
- serverChe = bless(pserver, pche, server, "che")
- serverAliceFriend = bless(palice, pbob, serverAlice, "friend")
- serverCheFriend = bless(pche, pbob, serverChe, "friend")
-
- authorizer security.Authorizer // the authorizer to test.
-
- runTests = func(expectations Expectations) {
- _, file, line, _ := runtime.Caller(1)
- for user, labels := range expectations {
- for _, l := range security.ValidLabels {
- ctx := security.NewContext(&security.ContextParams{
- LocalPrincipal: pserver,
- LocalBlessings: server,
- RemoteBlessings: user,
- MethodTags: []interface{}{l},
- })
- if got, want := authorizer.Authorize(ctx), labels.HasLabel(l); (got == nil) != want {
- t.Errorf("%s:%d: %+v.Authorize(%v) returned error: %v, want error: %v", file, line, authorizer, ctx, got, !want)
- }
- }
- }
- }
- )
- // Convenience function for combining Labels into a LabelSet.
- LS := func(labels ...security.Label) security.LabelSet {
- var ret security.LabelSet
- for _, l := range labels {
- ret = ret | security.LabelSet(l)
- }
- return ret
- }
-
- // ACL for testing
- acl := security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{
- "...": LS(R),
- "server/alice/...": LS(W, R),
- "server/alice": LS(A, D, M),
- "server/bob": LS(D, M),
- "server/che/...": LS(W, R),
- "server/che": LS(W, R),
- },
- NotIn: map[string]security.LabelSet{
- "server/che/friend": LS(W),
- },
- }
-
- // Authorizations for the above ACL.
- expectations := Expectations{
- alice: LS(R), // "..." ACL entry.
- bob: LS(R), // "..." ACL entry.
- che: LS(R), // "..." ACL entry.
- server: security.AllLabels, // self RPC
- serverAlice: LS(R, W, A, D, M), // "server/alice/..." ACL entry
- serverBob: LS(R, D, M), // "..." and "server/bob" ACL entries
- serverAliceFriend: LS(W, R), // "server/alice/..." ACL entry.
- serverChe: LS(W, R), // "server/che" ACL entry.
- serverCheFriend: LS(R), // "server/che/..." ACL entry, with the "server/che/friend" NotIn exception.
- nil: LS(R), // No blessings presented, same authorizations as "..." ACL entry.
- }
- // Create an aclAuthorizer based on the ACL and verify the authorizations.
- authorizer = NewACLAuthorizer(acl)
- runTests(expectations)
- testSelfRPCs(t, authorizer)
-
- // Create a fileACLAuthorizer by saving the ACL in a file, and verify the
- // authorizations.
- fileName := saveACLToTempFile(acl)
- defer os.Remove(fileName)
- authorizer = NewFileACLAuthorizer(fileName)
- runTests(expectations)
- testSelfRPCs(t, authorizer)
-
- // Modify the ACL stored in the file and verify that the authorizations appropriately
- // change for the fileACLAuthorizer.
- acl.In["server/bob"] = LS(R, W, A, D, M)
- expectations[serverBob] = LS(R, W, A, D, M)
- updateACLInFile(fileName, acl)
- runTests(expectations)
- testSelfRPCs(t, authorizer)
-
- // Update the ACL file with invalid contents and verify that no requests are
- // authorized.
- f, err := os.OpenFile(fileName, os.O_WRONLY, 0600)
- if err != nil {
- panic(err)
- }
- f.Write([]byte("invalid ACL"))
- f.Close()
- testNothingPermitted(t, authorizer)
-}
-
-func TestFileACLAuthorizerOnNonExistentFile(t *testing.T) {
- testNothingPermitted(t, NewFileACLAuthorizer("fileDoesNotExist"))
-}
-
-func TestNilACLAuthorizer(t *testing.T) {
- authorizer := NewACLAuthorizer(nullACL)
- testNothingPermitted(t, authorizer)
- testSelfRPCs(t, authorizer)
-}
diff --git a/security/agent/agent_test.go b/security/agent/agent_test.go
index d3a0b39..3d9fd8c 100644
--- a/security/agent/agent_test.go
+++ b/security/agent/agent_test.go
@@ -4,7 +4,6 @@
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
- "errors"
"reflect"
"testing"
@@ -15,6 +14,7 @@
"veyron.io/veyron/veyron2/options"
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/verror2"
)
func setupAgent(t *testing.T, p security.Principal) security.Principal {
@@ -39,13 +39,17 @@
Method string
Args V
Result interface{} // Result returned by the Method call.
- Error error // If Error is not nil will be compared to the last result.
+ Error verror2.E // If Error is not nil will be compared to the last result.
}
-var addToRootsErr = errors.New("AddToRoots")
-var storeSetDefaultErr = errors.New("StoreSetDefault")
-var rootsAddErr = errors.New("RootsAdd")
-var rootsRecognizedErr = errors.New("RootsRecognized")
+const pkgPath = "veyron.io/veyron/veyron/security/agent/"
+
+var (
+ addToRootsErr = verror2.Register(pkgPath+".addToRoots", verror2.NoRetry, "")
+ storeSetDefaultErr = verror2.Register(pkgPath+".storeSetDefault", verror2.NoRetry, "")
+ rootsAddErr = verror2.Register(pkgPath+".rootsAdd", verror2.NoRetry, "")
+ rootsRecognizedErr = verror2.Register(pkgPath+".rootsRecognized", verror2.NoRetry, "")
+)
func TestAgent(t *testing.T) {
var (
@@ -56,10 +60,12 @@
tests := []testInfo{
{"BlessSelf", V{"self"}, newBlessing(t, "blessing"), nil},
{"Bless", V{newPrincipal(t).PublicKey(), newBlessing(t, "root"), "extension", security.UnconstrainedUse()}, newBlessing(t, "root/extension"), nil},
+ // TODO(toddw): This change is necessary for vom2:
+ //{"Sign", V{make([]byte, 10)}, security.Signature{Purpose: []byte{}, R: []byte{1}, S: []byte{1}}, nil},
{"Sign", V{make([]byte, 10)}, security.Signature{R: []byte{1}, S: []byte{1}}, nil},
{"MintDischarge", V{thirdPartyCaveat, security.UnconstrainedUse()}, discharge, nil},
{"PublicKey", V{}, mockP.PublicKey(), nil},
- {"AddToRoots", V{newBlessing(t, "blessing")}, nil, addToRootsErr},
+ {"AddToRoots", V{newBlessing(t, "blessing")}, nil, verror2.Make(addToRootsErr, nil)},
}
for _, test := range tests {
mockP.NextResult = test.Result
@@ -71,7 +77,7 @@
storeTests := []testInfo{
{"Set", V{newBlessing(t, "blessing"), security.BlessingPattern("test")}, newBlessing(t, "root/extension"), nil},
{"ForPeer", V{"test", "oink"}, newBlessing(t, "for/peer"), nil},
- {"SetDefault", V{newBlessing(t, "blessing")}, nil, storeSetDefaultErr},
+ {"SetDefault", V{newBlessing(t, "blessing")}, nil, verror2.Make(storeSetDefaultErr, nil)},
{"Default", V{}, newBlessing(t, "root/extension"), nil},
{"PublicKey", V{}, mockP.PublicKey(), nil},
{"DebugString", V{}, "StoreString", nil},
@@ -84,8 +90,8 @@
roots := agent.Roots()
rootTests := []testInfo{
- {"Add", V{newPrincipal(t).PublicKey(), security.BlessingPattern("test")}, nil, rootsAddErr},
- {"Recognized", V{newPrincipal(t).PublicKey(), "blessing"}, nil, rootsRecognizedErr},
+ {"Add", V{newPrincipal(t).PublicKey(), security.BlessingPattern("test")}, nil, verror2.Make(rootsAddErr, nil)},
+ {"Recognized", V{newPrincipal(t).PublicKey(), "blessing"}, nil, verror2.Make(rootsRecognizedErr, nil)},
{"DebugString", V{}, "RootsString", nil},
}
for _, test := range rootTests {
@@ -103,7 +109,7 @@
}
// We only set the error value when error is the only output to ensure the real function gets called.
if test.Error != nil {
- if got := results[len(results)-1]; got == nil || got.(error).Error() != test.Error.Error() {
+ if got := results[len(results)-1]; got == nil || !verror2.Is(got.(error), test.Error.ErrorID()) {
t.Errorf("p.%v(%#v) returned an incorrect error: %v, expected %v", test.Method, test.Args, got, test.Error)
}
if len(results) == 1 {
@@ -111,7 +117,7 @@
}
}
if got := results[0]; !reflect.DeepEqual(got, test.Result) {
- t.Errorf("p.%v(%#v) returned %v(%T) want %v(%T)", test.Method, test.Args, got, got, test.Result, test.Result)
+ t.Errorf("p.%v(%#v) returned %#v want %#v", test.Method, test.Args, got, test.Result)
}
}
diff --git a/security/agent/pingpong/main.go b/security/agent/pingpong/main.go
index e09ef7b..31fcd8a 100644
--- a/security/agent/pingpong/main.go
+++ b/security/agent/pingpong/main.go
@@ -6,7 +6,6 @@
"veyron.io/veyron/veyron/lib/signals"
_ "veyron.io/veyron/veyron/profiles"
- vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/rt"
@@ -23,6 +22,8 @@
func clientMain() {
runtime := rt.Init()
+ defer runtime.Cleanup()
+
log := runtime.Logger()
log.Info("Pinging...")
@@ -51,17 +52,18 @@
log.Fatal("error listening to service: ", err)
}
- auth := vsecurity.NewACLAuthorizer(security.ACL{In: map[security.BlessingPattern]security.LabelSet{
- security.AllPrincipals: security.AllLabels,
- }})
- if err := s.Serve("pingpong", serverPong, auth); err != nil {
+ if err := s.Serve("pingpong", serverPong, allowEveryone{}); err != nil {
log.Fatal("error serving service: ", err)
}
// Wait forever.
- <-signals.ShutdownOnSignals()
+ <-signals.ShutdownOnSignals(r)
}
+type allowEveryone struct{}
+
+func (allowEveryone) Authorize(security.Context) error { return nil }
+
func main() {
flag.Parse()
if *runServer {
diff --git a/security/flag/flag.go b/security/flag/flag.go
index 91b54c2..cd1086e 100644
--- a/security/flag/flag.go
+++ b/security/flag/flag.go
@@ -7,14 +7,13 @@
"errors"
"flag"
- vsecurity "veyron.io/veyron/veyron/security"
-
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/services/security/access"
)
var (
- acl = flag.String("acl", "", `acl is an optional JSON-encoded security.ACL that is used to construct a security.Authorizer. Example: {"In":{"veyron/alice/...":"RW"}} is a JSON-encoded ACL that allows all delegates of "veyron/alice" to access all methods with ReadLabel or WriteLabel. If this flag is provided then the \"--acl_file\" must be absent.`)
- aclFile = flag.String("acl_file", "", "acl_file is an optional path to a file containing a JSON-encoded security.ACL that is used to construct a security.Authorizer. If this flag is provided then the \"--acl_file\" flag must be absent.")
+ acl = flag.String("acl", "", `acl is an optional JSON-encoded access.TaggedACLMap that is used to construct a security.Authorizer using the tags defined in the access package. Example: {"Read": {"In": ["veyron/alice/..."]}} allows all delegates of "veyron/alice" to access all methods with the "Read" access tag on them. If this flag is set then the --acl_file flag must not be set.`)
+ aclFile = flag.String("acl_file", "", "acl_file is an optional path to a file containing a JSON-encoded access.TaggedACLMap that is used to construct a security.Authorizer (with the set of tags defined in the access package). If this flag is set then --acl must not be set")
)
// NewAuthorizerOrDie constructs an Authorizer based on the provided "--acl" or
@@ -28,12 +27,18 @@
if len(*acl) != 0 && len(*aclFile) != 0 {
panic(errors.New("only one of the flags \"--acl\" or \"--acl_file\" must be provided"))
}
+ var a security.Authorizer
+ var err error
if len(*aclFile) != 0 {
- return vsecurity.NewFileACLAuthorizer(*aclFile)
+ a, err = access.TaggedACLAuthorizerFromFile(*aclFile, access.TypicalTagType())
+ } else {
+ var tam access.TaggedACLMap
+ if tam, err = access.ReadTaggedACLMap(bytes.NewBufferString(*acl)); err == nil {
+ a, err = access.TaggedACLAuthorizer(tam, access.TypicalTagType())
+ }
}
- a, err := vsecurity.LoadACL(bytes.NewBufferString(*acl))
if err != nil {
panic(err)
}
- return vsecurity.NewACLAuthorizer(a)
+ return a
}
diff --git a/security/flag/flag_test.go b/security/flag/flag_test.go
index de56a92..0da0bde 100644
--- a/security/flag/flag_test.go
+++ b/security/flag/flag_test.go
@@ -7,9 +7,9 @@
"testing"
tsecurity "veyron.io/veyron/veyron/lib/testutil/security"
- vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/services/security/access"
)
func TestNewAuthorizerOrDie(t *testing.T) {
@@ -29,11 +29,22 @@
flag.Set("acl_file", "")
}
var (
- acl1 = security.ACL{}
- acl2 = security.ACL{In: map[security.BlessingPattern]security.LabelSet{
- "veyron/alice": security.LabelSet(security.ReadLabel | security.WriteLabel),
- "veyron/bob": security.LabelSet(security.ReadLabel),
- }}
+ acl1 = access.TaggedACLMap{}
+ acl2 = access.TaggedACLMap{
+ string(access.Read): access.ACL{
+ In: []security.BlessingPattern{"veyron/alice", "veyron/bob"},
+ },
+ string(access.Write): access.ACL{
+ In: []security.BlessingPattern{"veyron/alice"},
+ },
+ }
+
+ auth = func(a security.Authorizer, err error) security.Authorizer {
+ if err != nil {
+ panic(err)
+ }
+ return a
+ }
)
acl2File := tsecurity.SaveACLToFile(acl2)
defer os.Remove(acl2File)
@@ -49,19 +60,19 @@
},
{
flags: flagValue{"acl": "{}"},
- wantAuth: vsecurity.NewACLAuthorizer(acl1),
+ wantAuth: auth(access.TaggedACLAuthorizer(acl1, access.TypicalTagType())),
},
{
flags: flagValue{"acl": `{"In":{"veyron/alice":"RW", "veyron/bob": "R"}}`},
- wantAuth: vsecurity.NewACLAuthorizer(acl2),
+ wantAuth: auth(access.TaggedACLAuthorizer(acl2, access.TypicalTagType())),
},
{
flags: flagValue{"acl": `{"In":{"veyron/bob":"R", "veyron/alice": "WR"}}`},
- wantAuth: vsecurity.NewACLAuthorizer(acl2),
+ wantAuth: auth(access.TaggedACLAuthorizer(acl2, access.TypicalTagType())),
},
{
flags: flagValue{"acl_file": acl2File},
- wantAuth: vsecurity.NewFileACLAuthorizer(acl2File),
+ wantAuth: auth(access.TaggedACLAuthorizerFromFile(acl2File, access.TypicalTagType())),
},
{
flags: flagValue{"acl_file": acl2File, "acl": `{"In":{"veyron/alice":"RW", "veyron/bob": "R"}}`},
diff --git a/security/serialization/verifying_reader.go b/security/serialization/verifying_reader.go
index 2a240b6..e8f1f44 100644
--- a/security/serialization/verifying_reader.go
+++ b/security/serialization/verifying_reader.go
@@ -9,6 +9,7 @@
"io"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/vdl/vdlutil"
"veyron.io/veyron/veyron2/vom"
)
@@ -119,5 +120,5 @@
}
func init() {
- vom.Register([sha256.Size]byte{})
+ vdlutil.Register([sha256.Size]byte{})
}
diff --git a/security/testdata/blessingstore.sig b/security/testdata/blessingstore.sig
index 1040fb8..b06ba85 100644
--- a/security/testdata/blessingstore.sig
+++ b/security/testdata/blessingstore.sig
Binary files differ
diff --git a/security/util.go b/security/util.go
index ea9df1c..1ce53ef 100644
--- a/security/util.go
+++ b/security/util.go
@@ -6,7 +6,6 @@
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
- "encoding/json"
"encoding/pem"
"errors"
"fmt"
@@ -19,15 +18,6 @@
const ecPrivateKeyPEMType = "EC PRIVATE KEY"
-var nullACL security.ACL
-
-// OpenACL creates an ACL that grants access to all principals.
-func OpenACL() security.ACL {
- acl := security.ACL{}
- acl.In = map[security.BlessingPattern]security.LabelSet{security.AllPrincipals: security.AllLabels}
- return acl
-}
-
var PassphraseErr = errors.New("passphrase incorrect for decrypting private key")
// NewPrincipalKey generates an ECDSA (public, private) key pair.
@@ -104,20 +94,6 @@
return pem.Encode(w, pemKey)
}
-// LoadACL reads an ACL from the provided Reader containing a JSON encoded ACL.
-func LoadACL(r io.Reader) (security.ACL, error) {
- var acl security.ACL
- if err := json.NewDecoder(r).Decode(&acl); err != nil {
- return nullACL, err
- }
- return acl, nil
-}
-
-// SaveACL encodes an ACL in JSON format and writes it to the provided Writer.
-func SaveACL(w io.Writer, acl security.ACL) error {
- return json.NewEncoder(w).Encode(acl)
-}
-
// ThirdPartyCaveats returns the set of security.ThirdPartyCaveats
// that could be successfully decoded from the provided caveat bytes.
func ThirdPartyCaveats(caveats ...security.Caveat) []security.ThirdPartyCaveat {
diff --git a/security/util_test.go b/security/util_test.go
index 2274a70..194443f 100644
--- a/security/util_test.go
+++ b/security/util_test.go
@@ -9,7 +9,7 @@
"testing"
"veyron.io/veyron/veyron2/security"
- "veyron.io/veyron/veyron2/vom"
+ "veyron.io/veyron/veyron2/vdl/vdlutil"
)
func TestLoadSavePEMKey(t *testing.T) {
@@ -65,31 +65,6 @@
}
}
-func TestLoadSaveACL(t *testing.T) {
- acl := security.ACL{}
- acl.In = map[security.BlessingPattern]security.LabelSet{
- "veyron/...": security.LabelSet(security.ReadLabel),
- "veyron/alice": security.LabelSet(security.ReadLabel | security.WriteLabel),
- "veyron/bob": security.LabelSet(security.AdminLabel),
- }
- acl.NotIn = map[string]security.LabelSet{
- "veyron/che": security.LabelSet(security.ReadLabel),
- }
-
- var buf bytes.Buffer
- if err := SaveACL(&buf, acl); err != nil {
- t.Fatalf("Failed to save ACL %q: %v", acl, err)
- }
-
- loadedACL, err := LoadACL(&buf)
- if err != nil {
- t.Fatalf("Failed to load ACL: %v", err)
- }
- if !reflect.DeepEqual(loadedACL, acl) {
- t.Fatalf("Got ACL %v, but want %v", loadedACL, acl)
- }
-}
-
// fpCaveat implements security.CaveatValidator.
type fpCaveat struct{}
@@ -146,6 +121,6 @@
}
func init() {
- vom.Register(&fpCaveat{})
- vom.Register(&tpCaveat{})
+ vdlutil.Register(&fpCaveat{})
+ vdlutil.Register(&tpCaveat{})
}
diff --git a/services/identity/identityd/main.go b/services/identity/identityd/main.go
index 55e0b87..5e9f3dc 100644
--- a/services/identity/identityd/main.go
+++ b/services/identity/identityd/main.go
@@ -142,7 +142,7 @@
})
vlog.Infof("Running HTTP server at: %v", httpaddress())
go runHTTPSServer(*httpaddr)
- <-signals.ShutdownOnSignals()
+ <-signals.ShutdownOnSignals(r)
}
func appendSuffixTo(objectname []string, suffix string) []string {
diff --git a/services/identity/revocation/revocation_manager.go b/services/identity/revocation/revocation_manager.go
index b4b2178..4a45a9a 100644
--- a/services/identity/revocation/revocation_manager.go
+++ b/services/identity/revocation/revocation_manager.go
@@ -9,7 +9,7 @@
"time"
"veyron.io/veyron/veyron2/security"
- "veyron.io/veyron/veyron2/vom"
+ "veyron.io/veyron/veyron2/vdl/vdlutil"
)
// RevocationManager persists information for revocation caveats to provided discharges and allow for future revocations.
@@ -89,5 +89,5 @@
}
func init() {
- vom.Register(revocationCaveat{})
+ vdlutil.Register(revocationCaveat{})
}
diff --git a/services/mgmt/application/applicationd/main.go b/services/mgmt/application/applicationd/main.go
index ab42449..af11153 100644
--- a/services/mgmt/application/applicationd/main.go
+++ b/services/mgmt/application/applicationd/main.go
@@ -50,5 +50,5 @@
vlog.Infof("Application repository serving at %q", epName)
}
// Wait until shutdown.
- <-signals.ShutdownOnSignals()
+ <-signals.ShutdownOnSignals(runtime)
}
diff --git a/services/mgmt/application/impl/impl_test.go b/services/mgmt/application/impl/impl_test.go
index 50e9ce2..d8c158c 100644
--- a/services/mgmt/application/impl/impl_test.go
+++ b/services/mgmt/application/impl/impl_test.go
@@ -9,6 +9,7 @@
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/services/mgmt/application"
+ "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron/lib/testutil"
"veyron.io/veyron/veyron/profiles"
@@ -69,7 +70,7 @@
if err := stubV2.Put(ctx, []string{"base"}, envelopeV2); err != nil {
t.Fatalf("Put() failed: %v", err)
}
- if err := stub.Put(ctx, []string{"base", "media"}, envelopeV1); err == nil || err.Error() != errInvalidSuffix.Error() {
+ if err := stub.Put(ctx, []string{"base", "media"}, envelopeV1); err == nil || !verror2.Is(err, errInvalidSuffix.ID) {
t.Fatalf("Unexpected error: expected %v, got %v", errInvalidSuffix, err)
}
@@ -88,18 +89,18 @@
if !reflect.DeepEqual(envelopeV1, output) {
t.Fatalf("Unexpected output: expected %v, got %v", envelopeV1, output)
}
- if _, err := stubV2.Match(ctx, []string{"media"}); err == nil || err.Error() != errNotFound.Error() {
+ if _, err := stubV2.Match(ctx, []string{"media"}); err == nil || !verror2.Is(err, errNotFound.ID) {
t.Fatalf("Unexpected error: expected %v, got %v", errNotFound, err)
}
- if _, err := stubV2.Match(ctx, []string{}); err == nil || err.Error() != errNotFound.Error() {
+ if _, err := stubV2.Match(ctx, []string{}); err == nil || !verror2.Is(err, errNotFound.ID) {
t.Fatalf("Unexpected error: expected %v, got %v", errNotFound, err)
}
- if _, err := stub.Match(ctx, []string{"media"}); err == nil || err.Error() != errInvalidSuffix.Error() {
+ if _, err := stub.Match(ctx, []string{"media"}); err == nil || !verror2.Is(err, errInvalidSuffix.ID) {
t.Fatalf("Unexpected error: expected %v, got %v", errInvalidSuffix, err)
}
// Test Glob
- matches, err := testutil.GlobName(naming.JoinAddressName(endpoint.String(), ""), "...")
+ matches, err := testutil.GlobName(ctx, naming.JoinAddressName(endpoint.String(), ""), "...")
if err != nil {
t.Errorf("Unexpected Glob error: %v", err)
}
@@ -121,13 +122,13 @@
if output, err = stubV1.Match(ctx, []string{"media"}); err != nil {
t.Fatalf("Match() failed: %v", err)
}
- if err := stubV1.Remove(ctx, "base"); err == nil || err.Error() != errNotFound.Error() {
+ if err := stubV1.Remove(ctx, "base"); err == nil || !verror2.Is(err, errNotFound.ID) {
t.Fatalf("Unexpected error: expected %v, got %v", errNotFound, err)
}
if err := stub.Remove(ctx, "base"); err != nil {
t.Fatalf("Remove() failed: %v", err)
}
- if err := stubV2.Remove(ctx, "media"); err == nil || err.Error() != errNotFound.Error() {
+ if err := stubV2.Remove(ctx, "media"); err == nil || !verror2.Is(err, errNotFound.ID) {
t.Fatalf("Unexpected error: expected %v, got %v", errNotFound, err)
}
if err := stubV1.Remove(ctx, "media"); err != nil {
@@ -136,13 +137,13 @@
// Finally, use Match() to test that Remove really removed the
// application envelopes.
- if _, err := stubV1.Match(ctx, []string{"base"}); err == nil || err.Error() != errNotFound.Error() {
+ if _, err := stubV1.Match(ctx, []string{"base"}); err == nil || !verror2.Is(err, errNotFound.ID) {
t.Fatalf("Unexpected error: expected %v, got %v", errNotFound, err)
}
- if _, err := stubV1.Match(ctx, []string{"media"}); err == nil || err.Error() != errNotFound.Error() {
+ if _, err := stubV1.Match(ctx, []string{"media"}); err == nil || !verror2.Is(err, errNotFound.ID) {
t.Fatalf("Unexpected error: expected %v, got %v", errNotFound, err)
}
- if _, err := stubV2.Match(ctx, []string{"base"}); err == nil || err.Error() != errNotFound.Error() {
+ if _, err := stubV2.Match(ctx, []string{"base"}); err == nil || !verror2.Is(err, errNotFound.ID) {
t.Fatalf("Unexpected error: expected %v, got %v", errNotFound, err)
}
diff --git a/services/mgmt/application/impl/service.go b/services/mgmt/application/impl/service.go
index b5cdf01..a42dcab 100644
--- a/services/mgmt/application/impl/service.go
+++ b/services/mgmt/application/impl/service.go
@@ -1,13 +1,13 @@
package impl
import (
- "errors"
"strings"
"veyron.io/veyron/veyron/services/mgmt/lib/fs"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/services/mgmt/application"
+ "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
)
@@ -24,10 +24,12 @@
suffix string
}
+const pkgPath = "veyron.io/veyron/veyron/services/mgmt/application/impl/"
+
var (
- errInvalidSuffix = errors.New("invalid suffix")
- errOperationFailed = errors.New("operation failed")
- errNotFound = errors.New("not found")
+ errInvalidSuffix = verror2.Register(pkgPath+".invalidSuffix", verror2.NoRetry, "")
+ errOperationFailed = verror2.Register(pkgPath+".operationFailed", verror2.NoRetry, "")
+ errNotFound = verror2.Register(pkgPath+".notFound", verror2.NoRetry, "")
)
// NewApplicationService returns a new Application service implementation.
@@ -35,7 +37,7 @@
return &appRepoService{store: store, storeRoot: storeRoot, suffix: suffix}
}
-func parse(suffix string) (string, string, error) {
+func parse(context ipc.ServerContext, suffix string) (string, string, error) {
tokens := strings.Split(suffix, "/")
switch len(tokens) {
case 2:
@@ -43,19 +45,19 @@
case 1:
return tokens[0], "", nil
default:
- return "", "", errInvalidSuffix
+ return "", "", verror2.Make(errInvalidSuffix, context)
}
}
func (i *appRepoService) Match(context ipc.ServerContext, profiles []string) (application.Envelope, error) {
vlog.VI(0).Infof("%v.Match(%v)", i.suffix, profiles)
empty := application.Envelope{}
- name, version, err := parse(i.suffix)
+ name, version, err := parse(context, i.suffix)
if err != nil {
return empty, err
}
if version == "" {
- return empty, errInvalidSuffix
+ return empty, verror2.Make(errInvalidSuffix, context)
}
i.store.Lock()
@@ -73,17 +75,17 @@
}
return envelope, nil
}
- return empty, errNotFound
+ return empty, verror2.Make(errNotFound, context)
}
func (i *appRepoService) Put(context ipc.ServerContext, profiles []string, envelope application.Envelope) error {
vlog.VI(0).Infof("%v.Put(%v, %v)", i.suffix, profiles, envelope)
- name, version, err := parse(i.suffix)
+ name, version, err := parse(context, i.suffix)
if err != nil {
return err
}
if version == "" {
- return errInvalidSuffix
+ return verror2.Make(errInvalidSuffix, context)
}
i.store.Lock()
defer i.store.Unlock()
@@ -99,18 +101,18 @@
object := i.store.BindObject(path)
_, err := object.Put(context, envelope)
if err != nil {
- return errOperationFailed
+ return verror2.Make(errOperationFailed, context)
}
}
if err := i.store.BindTransaction(tname).Commit(context); err != nil {
- return errOperationFailed
+ return verror2.Make(errOperationFailed, context)
}
return nil
}
func (i *appRepoService) Remove(context ipc.ServerContext, profile string) error {
vlog.VI(0).Infof("%v.Remove(%v)", i.suffix, profile)
- name, version, err := parse(i.suffix)
+ name, version, err := parse(context, i.suffix)
if err != nil {
return err
}
@@ -128,16 +130,16 @@
object := i.store.BindObject(path)
found, err := object.Exists(context)
if err != nil {
- return errOperationFailed
+ return verror2.Make(errOperationFailed, context)
}
if !found {
- return errNotFound
+ return verror2.Make(errNotFound, context)
}
if err := object.Remove(context); err != nil {
- return errOperationFailed
+ return verror2.Make(errOperationFailed, context)
}
if err := i.store.BindTransaction(tname).Commit(context); err != nil {
- return errOperationFailed
+ return verror2.Make(errOperationFailed, context)
}
return nil
}
@@ -174,8 +176,8 @@
return versions, nil
}
-func (i *appRepoService) VGlobChildren() ([]string, error) {
- vlog.VI(0).Infof("%v.VGlobChildren()", i.suffix)
+func (i *appRepoService) GlobChildren__() (<-chan string, error) {
+ vlog.VI(0).Infof("%v.GlobChildren__()", i.suffix)
i.store.Lock()
defer i.store.Unlock()
@@ -184,11 +186,19 @@
elems = strings.Split(i.suffix, "/")
}
+ var results []string
+ var err error
switch len(elems) {
case 0:
- return i.allApplications()
+ results, err = i.allApplications()
+ if err != nil {
+ return nil, err
+ }
case 1:
- return i.allAppVersions(elems[0])
+ results, err = i.allAppVersions(elems[0])
+ if err != nil {
+ return nil, err
+ }
case 2:
versions, err := i.allAppVersions(elems[0])
if err != nil {
@@ -199,8 +209,15 @@
return nil, nil
}
}
- return nil, errNotFound
+ return nil, verror2.Make(errNotFound, nil)
default:
- return nil, errNotFound
+ return nil, verror2.Make(errNotFound, nil)
}
+
+ ch := make(chan string, len(results))
+ for _, r := range results {
+ ch <- r
+ }
+ close(ch)
+ return ch, nil
}
diff --git a/services/mgmt/binary/binaryd/main.go b/services/mgmt/binary/binaryd/main.go
index 9b87655..d65984f 100644
--- a/services/mgmt/binary/binaryd/main.go
+++ b/services/mgmt/binary/binaryd/main.go
@@ -129,5 +129,5 @@
vlog.Infof("Binary repository serving at %q", epName)
}
// Wait until shutdown.
- <-signals.ShutdownOnSignals()
+ <-signals.ShutdownOnSignals(runtime)
}
diff --git a/services/mgmt/binary/binaryd/test.sh b/services/mgmt/binary/binaryd/test.sh
index 51b7201..17347fa 100755
--- a/services/mgmt/binary/binaryd/test.sh
+++ b/services/mgmt/binary/binaryd/test.sh
@@ -30,29 +30,49 @@
# Create a binary file.
local -r BINARY_SUFFIX="test-binary"
local -r BINARY="${REPO}/${BINARY_SUFFIX}"
- local -r BINARY_FILE=$(shell::tmp_file)
+ local -r BINARY_FILE="${WORKDIR}/bin1"
dd if=/dev/urandom of="${BINARY_FILE}" bs=1000000 count=16 \
|| shell_test::fail "line ${LINENO}: faile to create a random binary file"
"${BINARY_BIN}" upload "${BINARY}" "${BINARY_FILE}" || shell_test::fail "line ${LINENO}: 'upload' failed"
+ # Create TAR file.
+ local -r TAR="${REPO}/tarobj"
+ local -r TAR_FILE="${WORKDIR}/bin1.tar.gz"
+ tar zcvf "${TAR_FILE}" "${BINARY_FILE}"
+ "${BINARY_BIN}" upload "${TAR}" "${TAR_FILE}" || shell_test::fail "line ${LINENO}: 'upload' failed"
+
# Download the binary file.
- local -r BINARY_FILE2=$(shell::tmp_file)
+ local -r BINARY_FILE2="${WORKDIR}/bin2"
"${BINARY_BIN}" download "${BINARY}" "${BINARY_FILE2}" || shell_test::fail "line ${LINENO}: 'RPC download' failed"
if [[ $(cmp "${BINARY_FILE}" "${BINARY_FILE2}" &> /dev/null) ]]; then
shell_test::fail "mismatching binary file downloaded via RPC"
fi
+ local -r BINARY_FILE2_INFO=$(cat "${BINARY_FILE2}.__info")
+ shell_test::assert_eq "${BINARY_FILE2_INFO}" '{"Type":"application/octet-stream","Encoding":""}' "${LINENO}"
- local -r BINARY_FILE3=$(shell::tmp_file)
+ # Download the tar file.
+ local -r TAR_FILE2="${WORKDIR}/downloadedtar"
+ "${BINARY_BIN}" download "${TAR}" "${TAR_FILE2}" || shell_test::fail "line ${LINENO}: 'RPC download' failed"
+ if [[ $(cmp "${TAR_FILE}" "${TAR_FILE2}" &> /dev/null) ]]; then
+ shell_test::fail "mismatching tar file downloaded via RPC"
+ fi
+ local -r TAR_FILE2_INFO=$(cat "${TAR_FILE2}.__info")
+ shell_test::assert_eq "${TAR_FILE2_INFO}" '{"Type":"application/x-tar","Encoding":"gzip"}' "${LINENO}"
+
+ local -r BINARY_FILE3="${WORKDIR}/bin3"
curl -f -o "${BINARY_FILE3}" "http://${HTTP_ADDR}/${BINARY_SUFFIX}" || shell_test::fail "line ${LINENO}: 'HTTP download' failed"
if [[ $(cmp "${BINARY_FILE}" "${BINARY_FILE3}" &> /dev/null) ]]; then
shell_test::fail "mismatching binary file downloaded via HTTP"
fi
- # Remove the binary file.
+ # Remove the files.
"${BINARY_BIN}" delete "${BINARY}" || shell_test::fail "line ${LINENO}: 'delete' failed"
+ "${BINARY_BIN}" delete "${TAR}" || shell_test::fail "line ${LINENO}: 'delete' failed"
- # Check the binary no longer exists.
- local -r RESULT=$(shell::check_result "${BINARY_BIN}" download "${BINARY}" "${BINARY_FILE2}")
+ # Check the files no longer exist.
+ local RESULT=$(shell::check_result "${BINARY_BIN}" download "${BINARY}" "${BINARY_FILE2}")
+ shell_test::assert_ne "${RESULT}" "0" "${LINENO}"
+ RESULT=$(shell::check_result "${BINARY_BIN}" download "${TAR}" "${TAR_FILE2}")
shell_test::assert_ne "${RESULT}" "0" "${LINENO}"
shell_test::pass
diff --git a/services/mgmt/binary/impl/fs_utils.go b/services/mgmt/binary/impl/fs_utils.go
index 431f434..62eaab8 100644
--- a/services/mgmt/binary/impl/fs_utils.go
+++ b/services/mgmt/binary/impl/fs_utils.go
@@ -79,7 +79,7 @@
}
result[idx] = filepath.Join(path, partName)
} else {
- if info.Name() == "name" {
+ if info.Name() == "name" || info.Name() == "mediainfo" {
continue
}
// The only entries should correspond to the part dirs.
diff --git a/services/mgmt/binary/impl/http_test.go b/services/mgmt/binary/impl/http_test.go
index 123540e..bde93b3 100644
--- a/services/mgmt/binary/impl/http_test.go
+++ b/services/mgmt/binary/impl/http_test.go
@@ -9,6 +9,7 @@
"testing"
"veyron.io/veyron/veyron2/rt"
+ "veyron.io/veyron/veyron2/services/mgmt/repository"
"veyron.io/veyron/veyron/lib/testutil"
)
@@ -27,7 +28,8 @@
size := testutil.Rand.Intn(1000*bufferLength) + 1
data[i] = testutil.RandomBytes(size)
}
- if err := binary.Create(rt.R().NewContext(), int32(length)); err != nil {
+ mediaInfo := repository.MediaInfo{Type: "application/octet-stream"}
+ if err := binary.Create(rt.R().NewContext(), int32(length), mediaInfo); err != nil {
t.Fatalf("Create() failed: %v", err)
}
for i := 0; i < length; i++ {
@@ -35,7 +37,7 @@
t.FailNow()
}
}
- parts, err := binary.Stat(rt.R().NewContext())
+ parts, _, err := binary.Stat(rt.R().NewContext())
if err != nil {
t.Fatalf("Stat() failed: %v", err)
}
diff --git a/services/mgmt/binary/impl/impl_test.go b/services/mgmt/binary/impl/impl_test.go
index 75bb0e0..35aa2e3 100644
--- a/services/mgmt/binary/impl/impl_test.go
+++ b/services/mgmt/binary/impl/impl_test.go
@@ -162,13 +162,13 @@
size := testutil.Rand.Intn(1000 * bufferLength)
data := testutil.RandomBytes(size)
// Test the binary repository interface.
- if err := binary.Create(rt.R().NewContext(), 1); err != nil {
+ if err := binary.Create(rt.R().NewContext(), 1, repository.MediaInfo{Type: "application/octet-stream"}); err != nil {
t.Fatalf("Create() failed: %v", err)
}
if streamErr, err := invokeUpload(t, binary, data, 0); streamErr != nil || err != nil {
t.FailNow()
}
- parts, err := binary.Stat(rt.R().NewContext())
+ parts, _, err := binary.Stat(rt.R().NewContext())
if err != nil {
t.Fatalf("Stat() failed: %v", err)
}
@@ -188,7 +188,7 @@
if bytes.Compare(output, data) != 0 {
t.Fatalf("Unexpected output: expected %v, got %v", data, output)
}
- results, err := testutil.GlobName(naming.JoinAddressName(ep, ""), "...")
+ results, err := testutil.GlobName(rt.R().NewContext(), naming.JoinAddressName(ep, ""), "...")
if err != nil {
t.Fatalf("GlobName failed: %v", err)
}
@@ -215,7 +215,7 @@
data[i] = testutil.RandomBytes(size)
}
// Test the binary repository interface.
- if err := binary.Create(rt.R().NewContext(), int32(length)); err != nil {
+ if err := binary.Create(rt.R().NewContext(), int32(length), repository.MediaInfo{Type: "application/octet-stream"}); err != nil {
t.Fatalf("Create() failed: %v", err)
}
for i := 0; i < length; i++ {
@@ -223,7 +223,7 @@
t.FailNow()
}
}
- parts, err := binary.Stat(rt.R().NewContext())
+ parts, _, err := binary.Stat(rt.R().NewContext())
if err != nil {
t.Fatalf("Stat() failed: %v", err)
}
@@ -264,13 +264,13 @@
size := testutil.Rand.Intn(1000 * bufferLength)
data[i] = testutil.RandomBytes(size)
}
- if err := binary.Create(rt.R().NewContext(), int32(length)); err != nil {
+ if err := binary.Create(rt.R().NewContext(), int32(length), repository.MediaInfo{Type: "application/octet-stream"}); err != nil {
t.Fatalf("Create() failed: %v", err)
}
// Simulate a flaky upload client that keeps uploading parts until
// finished.
for {
- parts, err := binary.Stat(rt.R().NewContext())
+ parts, _, err := binary.Stat(rt.R().NewContext())
if err != nil {
t.Fatalf("Stat() failed: %v", err)
}
@@ -309,10 +309,10 @@
data[i][j] = byte(testutil.Rand.Int())
}
}
- if err := binary.Create(rt.R().NewContext(), int32(length)); err != nil {
+ if err := binary.Create(rt.R().NewContext(), int32(length), repository.MediaInfo{Type: "application/octet-stream"}); err != nil {
t.Fatalf("Create() failed: %v", err)
}
- if err := binary.Create(rt.R().NewContext(), int32(length)); err == nil {
+ if err := binary.Create(rt.R().NewContext(), int32(length), repository.MediaInfo{Type: "application/octet-stream"}); err == nil {
t.Fatalf("Create() did not fail when it should have")
} else if want := verror.Exists; !verror.Is(err, want) {
t.Fatalf("Unexpected error: %v, expected error id %v", err, want)
@@ -372,14 +372,14 @@
name := naming.JoinAddressName(ep, obj)
binary := repository.BinaryClient(name)
- if err := binary.Create(rt.R().NewContext(), 1); err != nil {
+ if err := binary.Create(rt.R().NewContext(), 1, repository.MediaInfo{Type: "application/octet-stream"}); err != nil {
t.Fatalf("Create() failed: %v", err)
}
if streamErr, err := invokeUpload(t, binary, data, 0); streamErr != nil || err != nil {
t.FailNow()
}
}
- results, err := testutil.GlobName(naming.JoinAddressName(ep, ""), "...")
+ results, err := testutil.GlobName(rt.R().NewContext(), naming.JoinAddressName(ep, ""), "...")
if err != nil {
t.Fatalf("GlobName failed: %v", err)
}
diff --git a/services/mgmt/binary/impl/service.go b/services/mgmt/binary/impl/service.go
index 8a01122..a1999c6 100644
--- a/services/mgmt/binary/impl/service.go
+++ b/services/mgmt/binary/impl/service.go
@@ -22,6 +22,7 @@
import (
"crypto/md5"
"encoding/hex"
+ "encoding/json"
"io"
"io/ioutil"
"os"
@@ -75,8 +76,8 @@
const bufferLength = 4096
-func (i *binaryService) Create(_ ipc.ServerContext, nparts int32) error {
- vlog.Infof("%v.Create(%v)", i.suffix, nparts)
+func (i *binaryService) Create(_ ipc.ServerContext, nparts int32, mediaInfo repository.MediaInfo) error {
+ vlog.Infof("%v.Create(%v, %v)", i.suffix, nparts, mediaInfo)
if nparts < 1 {
return errInvalidParts
}
@@ -96,6 +97,16 @@
vlog.Errorf("WriteFile(%q) failed: %v", nameFile)
return errOperationFailed
}
+ infoFile := filepath.Join(tmpDir, "mediainfo")
+ jInfo, err := json.Marshal(mediaInfo)
+ if err != nil {
+ vlog.Errorf("json.Marshal(%v) failed: %v", mediaInfo, err)
+ return errOperationFailed
+ }
+ if err := ioutil.WriteFile(infoFile, jInfo, os.FileMode(0600)); err != nil {
+ vlog.Errorf("WriteFile(%q) failed: %v", infoFile, err)
+ return errOperationFailed
+ }
for j := 0; j < int(nparts); j++ {
partPath, partPerm := generatePartPath(tmpDir, j), os.FileMode(0700)
if err := os.MkdirAll(partPath, partPerm); err != nil {
@@ -199,12 +210,12 @@
return "", 0, nil
}
-func (i *binaryService) Stat(ipc.ServerContext) ([]binary.PartInfo, error) {
+func (i *binaryService) Stat(ipc.ServerContext) ([]binary.PartInfo, repository.MediaInfo, error) {
vlog.Infof("%v.Stat()", i.suffix)
result := make([]binary.PartInfo, 0)
parts, err := getParts(i.path)
if err != nil {
- return []binary.PartInfo{}, err
+ return []binary.PartInfo{}, repository.MediaInfo{}, err
}
for _, part := range parts {
checksumFile := filepath.Join(part, checksum)
@@ -215,7 +226,7 @@
continue
}
vlog.Errorf("ReadFile(%v) failed: %v", checksumFile, err)
- return []binary.PartInfo{}, errOperationFailed
+ return []binary.PartInfo{}, repository.MediaInfo{}, errOperationFailed
}
dataFile := filepath.Join(part, data)
fi, err := os.Stat(dataFile)
@@ -225,11 +236,22 @@
continue
}
vlog.Errorf("Stat(%v) failed: %v", dataFile, err)
- return []binary.PartInfo{}, errOperationFailed
+ return []binary.PartInfo{}, repository.MediaInfo{}, errOperationFailed
}
result = append(result, binary.PartInfo{Checksum: string(bytes), Size: fi.Size()})
}
- return result, nil
+ infoFile := filepath.Join(i.path, "mediainfo")
+ jInfo, err := ioutil.ReadFile(infoFile)
+ if err != nil {
+ vlog.Errorf("ReadFile(%q) failed: %v", infoFile)
+ return []binary.PartInfo{}, repository.MediaInfo{}, errOperationFailed
+ }
+ var mediaInfo repository.MediaInfo
+ if err := json.Unmarshal(jInfo, &mediaInfo); err != nil {
+ vlog.Errorf("json.Unmarshal(%v) failed: %v", jInfo, err)
+ return []binary.PartInfo{}, repository.MediaInfo{}, errOperationFailed
+ }
+ return result, mediaInfo, nil
}
func (i *binaryService) Upload(context repository.BinaryUploadContext, part int32) error {
@@ -303,7 +325,7 @@
return nil
}
-func (i *binaryService) VGlobChildren() ([]string, error) {
+func (i *binaryService) GlobChildren__() (<-chan string, error) {
elems := strings.Split(i.suffix, "/")
if len(elems) == 1 && elems[0] == "" {
elems = nil
@@ -312,11 +334,12 @@
if n == nil {
return nil, errOperationFailed
}
- results := make([]string, len(n.children))
- index := 0
- for k, _ := range n.children {
- results[index] = k
- index++
- }
- return results, nil
+ ch := make(chan string, 100)
+ go func() {
+ for k, _ := range n.children {
+ ch <- k
+ }
+ close(ch)
+ }()
+ return ch, nil
}
diff --git a/services/mgmt/build/buildd/main.go b/services/mgmt/build/buildd/main.go
index bba50c4..deb5f37 100644
--- a/services/mgmt/build/buildd/main.go
+++ b/services/mgmt/build/buildd/main.go
@@ -42,5 +42,5 @@
vlog.Infof("Build server running at endpoint=%q", endpoint)
// Wait until shutdown.
- <-signals.ShutdownOnSignals()
+ <-signals.ShutdownOnSignals(runtime)
}
diff --git a/services/mgmt/debug/dispatcher.go b/services/mgmt/debug/dispatcher.go
index e11cf1e..6f0f3b6 100644
--- a/services/mgmt/debug/dispatcher.go
+++ b/services/mgmt/debug/dispatcher.go
@@ -32,7 +32,7 @@
func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
if suffix == "" {
- return ipc.VChildrenGlobberInvoker(rootName), d.auth, nil
+ return ipc.ChildrenGlobberInvoker(rootName), d.auth, nil
}
if !strings.HasPrefix(suffix, rootName) {
return nil, nil, nil
@@ -41,7 +41,7 @@
suffix = strings.TrimLeft(suffix, "/")
if suffix == "" {
- return ipc.VChildrenGlobberInvoker("logs", "pprof", "stats", "vtrace"), d.auth, nil
+ return ipc.ChildrenGlobberInvoker("logs", "pprof", "stats", "vtrace"), d.auth, nil
}
parts := strings.SplitN(suffix, "/", 2)
if len(parts) == 2 {
diff --git a/services/mgmt/debug/dispatcher_test.go b/services/mgmt/debug/dispatcher_test.go
index 2d9236b..46fd799 100644
--- a/services/mgmt/debug/dispatcher_test.go
+++ b/services/mgmt/debug/dispatcher_test.go
@@ -13,13 +13,15 @@
"time"
"veyron.io/veyron/veyron2"
+ "veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/services/mgmt/logreader"
"veyron.io/veyron/veyron2/services/mgmt/stats"
- "veyron.io/veyron/veyron2/services/mgmt/vtrace"
+ vtracesvc "veyron.io/veyron/veyron2/services/mgmt/vtrace"
"veyron.io/veyron/veyron2/verror"
+ "veyron.io/veyron/veyron2/vtrace"
libstats "veyron.io/veyron/veyron/lib/stats"
"veyron.io/veyron/veyron/lib/testutil"
@@ -49,6 +51,11 @@
func TestDebugServer(t *testing.T) {
runtime := rt.Init()
+ tracedContext := func() context.T {
+ ctx := runtime.NewContext()
+ vtrace.FromContext(ctx).Trace().ForceCollect()
+ return ctx
+ }
rootName = "debug"
workdir, err := ioutil.TempDir("", "logreadertest")
@@ -68,7 +75,7 @@
// Access a logs directory that exists.
{
- results, err := testutil.GlobName(naming.JoinAddressName(endpoint, "debug/logs"), "*")
+ results, err := testutil.GlobName(runtime.NewContext(), naming.JoinAddressName(endpoint, "debug/logs"), "*")
if err != nil {
t.Errorf("Glob failed: %v", err)
}
@@ -79,7 +86,7 @@
// Access a logs directory that doesn't exist.
{
- results, err := testutil.GlobName(naming.JoinAddressName(endpoint, "debug/logs/nowheretobefound"), "*")
+ results, err := testutil.GlobName(runtime.NewContext(), naming.JoinAddressName(endpoint, "debug/logs/nowheretobefound"), "*")
if len(results) != 0 {
t.Errorf("unexpected result. Got %v, want ''", results)
}
@@ -91,7 +98,7 @@
// Access a log file that exists.
{
lf := logreader.LogFileClient(naming.JoinAddressName(endpoint, "debug/logs/test.INFO"))
- size, err := lf.Size(runtime.NewContext())
+ size, err := lf.Size(tracedContext())
if err != nil {
t.Errorf("Size failed: %v", err)
}
@@ -103,7 +110,7 @@
// Access a log file that doesn't exist.
{
lf := logreader.LogFileClient(naming.JoinAddressName(endpoint, "debug/logs/nosuchfile.INFO"))
- _, err = lf.Size(runtime.NewContext())
+ _, err = lf.Size(tracedContext())
if expected := verror.NoExist; !verror.Is(err, expected) {
t.Errorf("unexpected error value, got %v, want: %v", err, expected)
}
@@ -115,7 +122,7 @@
foo.Set(123)
st := stats.StatsClient(naming.JoinAddressName(endpoint, "debug/stats/testing/foo"))
- v, err := st.Value(runtime.NewContext())
+ v, err := st.Value(tracedContext())
if err != nil {
t.Errorf("Value failed: %v", err)
}
@@ -127,7 +134,7 @@
// Access a stats object that doesn't exists.
{
st := stats.StatsClient(naming.JoinAddressName(endpoint, "debug/stats/testing/nobodyhome"))
- _, err = st.Value(runtime.NewContext())
+ _, err = st.Value(tracedContext())
if expected := verror.NoExist; !verror.Is(err, expected) {
t.Errorf("unexpected error value, got %v, want: %v", err, expected)
}
@@ -135,7 +142,7 @@
// Access vtrace.
{
- vt := vtrace.StoreClient(naming.JoinAddressName(endpoint, "debug/vtrace"))
+ vt := vtracesvc.StoreClient(naming.JoinAddressName(endpoint, "debug/vtrace"))
call, err := vt.AllTraces(runtime.NewContext())
if err != nil {
t.Errorf("AllTraces failed: %v", err)
@@ -149,8 +156,8 @@
if err = stream.Err(); err != nil && err != io.EOF {
t.Fatalf("Unexpected error reading trace stream: %s", err)
}
- if ntraces < 1 {
- t.Errorf("We expected at least one trace, got: %d", ntraces)
+ if ntraces != 4 {
+ t.Errorf("We expected 4 traces, got: %d", ntraces)
}
}
diff --git a/services/mgmt/lib/binary/impl.go b/services/mgmt/lib/binary/impl.go
index 41af160..07bedd9 100644
--- a/services/mgmt/lib/binary/impl.go
+++ b/services/mgmt/lib/binary/impl.go
@@ -14,11 +14,12 @@
"time"
"veyron.io/veyron/veyron2/context"
- "veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/services/mgmt/binary"
"veyron.io/veyron/veyron2/services/mgmt/repository"
"veyron.io/veyron/veyron2/verror"
"veyron.io/veyron/veyron2/vlog"
+
+ "veyron.io/veyron/veyron/services/mgmt/lib/packages"
)
var (
@@ -32,8 +33,8 @@
subpartSize = 1 << 12
)
-func Delete(name string) error {
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+func Delete(ctx context.T, name string) error {
+ ctx, cancel := ctx.WithTimeout(time.Minute)
defer cancel()
if err := repository.BinaryClient(name).Delete(ctx); err != nil {
vlog.Errorf("Delete() failed: %v", err)
@@ -42,16 +43,16 @@
return nil
}
-func download(ctx context.T, w io.WriteSeeker, von string) error {
+func download(ctx context.T, w io.WriteSeeker, von string) (repository.MediaInfo, error) {
client := repository.BinaryClient(von)
- parts, err := client.Stat(ctx)
+ parts, mediaInfo, err := client.Stat(ctx)
if err != nil {
vlog.Errorf("Stat() failed: %v", err)
- return err
+ return repository.MediaInfo{}, err
}
for _, part := range parts {
if part.Checksum == binary.MissingChecksum {
- return errNotExist
+ return repository.MediaInfo{}, errNotExist
}
}
offset, whence := int64(0), 0
@@ -102,36 +103,37 @@
success = true
}
if !success {
- return errOperationFailed
+ return repository.MediaInfo{}, errOperationFailed
}
offset += part.Size
}
- return nil
+ return mediaInfo, nil
}
-func Download(von string) ([]byte, error) {
+func Download(ctx context.T, von string) ([]byte, repository.MediaInfo, error) {
dir, prefix := "", ""
file, err := ioutil.TempFile(dir, prefix)
if err != nil {
vlog.Errorf("TempFile(%v, %v) failed: %v", dir, prefix, err)
- return nil, errOperationFailed
+ return nil, repository.MediaInfo{}, errOperationFailed
}
defer os.Remove(file.Name())
defer file.Close()
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := ctx.WithTimeout(time.Minute)
defer cancel()
- if err := download(ctx, file, von); err != nil {
- return nil, errOperationFailed
+ mediaInfo, err := download(ctx, file, von)
+ if err != nil {
+ return nil, repository.MediaInfo{}, errOperationFailed
}
bytes, err := ioutil.ReadFile(file.Name())
if err != nil {
vlog.Errorf("ReadFile(%v) failed: %v", file.Name(), err)
- return nil, errOperationFailed
+ return nil, repository.MediaInfo{}, errOperationFailed
}
- return bytes, nil
+ return bytes, mediaInfo, nil
}
-func DownloadToFile(von, path string) error {
+func DownloadToFile(ctx context.T, von, path string) error {
dir, prefix := "", ""
file, err := ioutil.TempFile(dir, prefix)
if err != nil {
@@ -139,15 +141,16 @@
return errOperationFailed
}
defer file.Close()
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := ctx.WithTimeout(time.Minute)
defer cancel()
- if err := download(ctx, file, von); err != nil {
+ mediaInfo, err := download(ctx, file, von)
+ if err != nil {
if err := os.Remove(file.Name()); err != nil {
vlog.Errorf("Remove(%v) failed: %v", file.Name(), err)
}
return errOperationFailed
}
- perm := os.FileMode(0700)
+ perm := os.FileMode(0600)
if err := file.Chmod(perm); err != nil {
vlog.Errorf("Chmod(%v) failed: %v", perm, err)
if err := os.Remove(file.Name()); err != nil {
@@ -162,10 +165,17 @@
}
return errOperationFailed
}
+ if err := packages.SaveMediaInfo(path, mediaInfo); err != nil {
+ vlog.Errorf("packages.SaveMediaInfo(%v, %v) failed: %v", path, mediaInfo, err)
+ if err := os.Remove(path); err != nil {
+ vlog.Errorf("Remove(%v) failed: %v", path, err)
+ }
+ return errOperationFailed
+ }
return nil
}
-func upload(ctx context.T, r io.ReadSeeker, von string) error {
+func upload(ctx context.T, r io.ReadSeeker, mediaInfo repository.MediaInfo, von string) error {
client := repository.BinaryClient(von)
offset, whence := int64(0), 2
size, err := r.Seek(offset, whence)
@@ -174,7 +184,7 @@
return errOperationFailed
}
nparts := (size-1)/partSize + 1
- if err := client.Create(ctx, int32(nparts)); err != nil {
+ if err := client.Create(ctx, int32(nparts), mediaInfo); err != nil {
vlog.Errorf("Create() failed: %v", err)
return err
}
@@ -220,7 +230,7 @@
}
if err := sender.Close(); err != nil {
vlog.Errorf("Close() failed: %v", err)
- parts, statErr := client.Stat(ctx)
+ parts, _, statErr := client.Stat(ctx)
if statErr != nil {
vlog.Errorf("Stat() failed: %v", statErr)
if deleteErr := client.Delete(ctx); err != nil {
@@ -235,7 +245,7 @@
}
if err := stream.Finish(); err != nil {
vlog.Errorf("Finish() failed: %v", err)
- parts, statErr := client.Stat(ctx)
+ parts, _, statErr := client.Stat(ctx)
if statErr != nil {
vlog.Errorf("Stat() failed: %v", statErr)
if deleteErr := client.Delete(ctx); err != nil {
@@ -256,21 +266,22 @@
return nil
}
-func Upload(von string, data []byte) error {
+func Upload(ctx context.T, von string, data []byte, mediaInfo repository.MediaInfo) error {
buffer := bytes.NewReader(data)
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := ctx.WithTimeout(time.Minute)
defer cancel()
- return upload(ctx, buffer, von)
+ return upload(ctx, buffer, mediaInfo, von)
}
-func UploadFromFile(von, path string) error {
+func UploadFromFile(ctx context.T, von, path string) error {
file, err := os.Open(path)
defer file.Close()
if err != nil {
vlog.Errorf("Open(%v) failed: %v", err)
return errOperationFailed
}
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := ctx.WithTimeout(time.Minute)
defer cancel()
- return upload(ctx, file, von)
+ mediaInfo := packages.MediaInfoForFileName(path)
+ return upload(ctx, file, mediaInfo, von)
}
diff --git a/services/mgmt/lib/binary/impl_test.go b/services/mgmt/lib/binary/impl_test.go
index 3f79d89..ca0a76f 100644
--- a/services/mgmt/lib/binary/impl_test.go
+++ b/services/mgmt/lib/binary/impl_test.go
@@ -5,10 +5,13 @@
"io/ioutil"
"os"
"path/filepath"
+ "reflect"
"testing"
+ "veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/rt"
+ "veyron.io/veyron/veyron2/services/mgmt/repository"
"veyron.io/veyron/veyron2/vlog"
"veyron.io/veyron/veyron/lib/testutil"
@@ -20,9 +23,16 @@
veyronPrefix = "veyron_binary_repository"
)
+var runtime veyron2.Runtime
+
func init() {
testutil.Init()
- rt.Init()
+
+ var err error
+ runtime, err = rt.New()
+ if err != nil {
+ panic(err)
+ }
}
func setupRepository(t *testing.T) (string, func()) {
@@ -36,7 +46,7 @@
vlog.Fatalf("WriteFile(%v, %v, %v) failed: %v", path, impl.Version, perm, err)
}
// Setup and start the binary repository server.
- server, err := rt.R().NewServer()
+ server, err := runtime.NewServer()
if err != nil {
t.Fatalf("NewServer() failed: %v", err)
}
@@ -77,21 +87,25 @@
von, cleanup := setupRepository(t)
defer cleanup()
data := testutil.RandomBytes(testutil.Rand.Intn(10 << 20))
- if err := Upload(von, data); err != nil {
+ mediaInfo := repository.MediaInfo{Type: "application/octet-stream"}
+ if err := Upload(runtime.NewContext(), von, data, mediaInfo); err != nil {
t.Fatalf("Upload(%v) failed: %v", von, err)
}
- output, err := Download(von)
+ output, outInfo, err := Download(runtime.NewContext(), von)
if err != nil {
t.Fatalf("Download(%v) failed: %v", von, err)
}
if bytes.Compare(data, output) != 0 {
- t.Fatalf("Data mismatch:\nexpected %v %v\ngot %v %v", len(data), data[:100], len(output), output[:100])
+ t.Errorf("Data mismatch:\nexpected %v %v\ngot %v %v", len(data), data[:100], len(output), output[:100])
}
- if err := Delete(von); err != nil {
- t.Fatalf("Delete(%v) failed: %v", von, err)
+ if err := Delete(runtime.NewContext(), von); err != nil {
+ t.Errorf("Delete(%v) failed: %v", von, err)
}
- if _, err := Download(von); err == nil {
- t.Fatalf("Download(%v) did not fail", von)
+ if _, _, err := Download(runtime.NewContext(), von); err == nil {
+ t.Errorf("Download(%v) did not fail", von)
+ }
+ if !reflect.DeepEqual(mediaInfo, outInfo) {
+ t.Errorf("unexpected media info: expected %v, got %v", mediaInfo, outInfo)
}
}
@@ -109,29 +123,40 @@
}
defer os.Remove(src.Name())
defer src.Close()
- dst, err := ioutil.TempFile(dir, prefix)
+ dstdir, err := ioutil.TempDir(dir, prefix)
if err != nil {
- t.Fatalf("TempFile(%v, %v) failed: %v", dir, prefix, err)
+ t.Fatalf("TempDir(%v, %v) failed: %v", dir, prefix, err)
}
- defer os.Remove(dst.Name())
+ defer os.RemoveAll(dstdir)
+ dst, err := ioutil.TempFile(dstdir, prefix)
+ if err != nil {
+ t.Fatalf("TempFile(%v, %v) failed: %v", dstdir, prefix, err)
+ }
defer dst.Close()
if _, err := src.Write(data); err != nil {
t.Fatalf("Write() failed: %v", err)
}
- if err := UploadFromFile(von, src.Name()); err != nil {
+ if err := UploadFromFile(runtime.NewContext(), von, src.Name()); err != nil {
t.Fatalf("UploadFromFile(%v, %v) failed: %v", von, src.Name(), err)
}
- if err := DownloadToFile(von, dst.Name()); err != nil {
+ if err := DownloadToFile(runtime.NewContext(), von, dst.Name()); err != nil {
t.Fatalf("DownloadToFile(%v, %v) failed: %v", von, dst.Name(), err)
}
output, err := ioutil.ReadFile(dst.Name())
if err != nil {
- t.Fatalf("ReadFile(%v) failed: %v", dst.Name(), err)
+ t.Errorf("ReadFile(%v) failed: %v", dst.Name(), err)
}
if bytes.Compare(data, output) != 0 {
- t.Fatalf("Data mismatch:\nexpected %v %v\ngot %v %v", len(data), data[:100], len(output), output[:100])
+ t.Errorf("Data mismatch:\nexpected %v %v\ngot %v %v", len(data), data[:100], len(output), output[:100])
}
- if err := Delete(von); err != nil {
- t.Fatalf("Delete(%v) failed: %v", von, err)
+ jMediaInfo, err := ioutil.ReadFile(dst.Name() + ".__info")
+ if err != nil {
+ t.Errorf("ReadFile(%v) failed: %v", dst.Name()+".__info", err)
+ }
+ if expected := `{"Type":"application/octet-stream","Encoding":""}`; string(jMediaInfo) != expected {
+ t.Errorf("unexpected media info: expected %q, got %q", expected, string(jMediaInfo))
+ }
+ if err := Delete(runtime.NewContext(), von); err != nil {
+ t.Errorf("Delete(%v) failed: %v", von, err)
}
}
diff --git a/services/mgmt/lib/packages/packages.go b/services/mgmt/lib/packages/packages.go
new file mode 100644
index 0000000..57f9dde
--- /dev/null
+++ b/services/mgmt/lib/packages/packages.go
@@ -0,0 +1,192 @@
+// Package packages provides functionality to install ZIP and TAR packages.
+package packages
+
+import (
+ "archive/tar"
+ "archive/zip"
+ "compress/bzip2"
+ "compress/gzip"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "veyron.io/veyron/veyron2/services/mgmt/repository"
+)
+
+const defaultType = "application/octet-stream"
+
+var typemap = map[string]repository.MediaInfo{
+ ".zip": repository.MediaInfo{Type: "application/zip"},
+ ".tar": repository.MediaInfo{Type: "application/x-tar"},
+ ".tgz": repository.MediaInfo{Type: "application/x-tar", Encoding: "gzip"},
+ ".tar.gz": repository.MediaInfo{Type: "application/x-tar", Encoding: "gzip"},
+ ".tbz2": repository.MediaInfo{Type: "application/x-tar", Encoding: "bzip2"},
+ ".tb2": repository.MediaInfo{Type: "application/x-tar", Encoding: "bzip2"},
+ ".tbz": repository.MediaInfo{Type: "application/x-tar", Encoding: "bzip2"},
+ ".tar.bz2": repository.MediaInfo{Type: "application/x-tar", Encoding: "bzip2"},
+}
+
+// MediaInfoForFileName returns the MediaInfo based on the file's extension.
+func MediaInfoForFileName(fileName string) repository.MediaInfo {
+ fileName = strings.ToLower(fileName)
+ for k, v := range typemap {
+ if strings.HasSuffix(fileName, k) {
+ return v
+ }
+ }
+ return repository.MediaInfo{Type: defaultType}
+}
+
+// Install installs a package in the given directory. If the package is a TAR or
+// ZIP archive, its content is extracted in the destination directory.
+// Otherwise, the package file itself is copied to the destination directory.
+func Install(pkgFile, dir string) error {
+ mediaInfo, err := LoadMediaInfo(pkgFile)
+ if err != nil {
+ return err
+ }
+ switch mediaInfo.Type {
+ case "application/x-tar":
+ return extractTar(pkgFile, mediaInfo, dir)
+ case "application/zip":
+ return extractZip(pkgFile, dir)
+ default:
+ return fmt.Errorf("unsupported media type: %v", mediaInfo.Type)
+ }
+}
+
+// LoadMediaInfo returns the MediaInfo for the given package file.
+func LoadMediaInfo(pkgFile string) (repository.MediaInfo, error) {
+ jInfo, err := ioutil.ReadFile(pkgFile + ".__info")
+ if err != nil {
+ return repository.MediaInfo{}, err
+ }
+ var info repository.MediaInfo
+ if err := json.Unmarshal(jInfo, &info); err != nil {
+ return repository.MediaInfo{}, err
+ }
+ return info, nil
+}
+
+// SaveMediaInfo saves the media info for a package.
+func SaveMediaInfo(pkgFile string, mediaInfo repository.MediaInfo) error {
+ jInfo, err := json.Marshal(mediaInfo)
+ if err != nil {
+ return err
+ }
+ infoFile := pkgFile + ".__info"
+ if err := ioutil.WriteFile(infoFile, jInfo, os.FileMode(0600)); err != nil {
+ return err
+ }
+ return nil
+}
+
+func extractZip(zipFile, installDir string) error {
+ zr, err := zip.OpenReader(zipFile)
+ if err != nil {
+ return err
+ }
+ for _, file := range zr.File {
+ fi := file.FileInfo()
+ name := filepath.Join(installDir, file.Name)
+ if !strings.HasPrefix(name, installDir) {
+ return fmt.Errorf("failed to extract file %q outside of install directory", file.Name)
+ }
+ if fi.IsDir() {
+ if err := os.MkdirAll(name, os.FileMode(fi.Mode()&0700)); err != nil && !os.IsExist(err) {
+ return err
+ }
+ continue
+ }
+ in, err := file.Open()
+ if err != nil {
+ return err
+ }
+ parentName := filepath.Dir(name)
+ if err := os.MkdirAll(parentName, os.FileMode(0700)); err != nil {
+ return err
+ }
+ out, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY, os.FileMode(fi.Mode()&0700))
+ if err != nil {
+ in.Close()
+ return err
+ }
+ nbytes, err := io.Copy(out, in)
+ in.Close()
+ out.Close()
+ if err != nil {
+ return err
+ }
+ if nbytes != fi.Size() {
+ return fmt.Errorf("file size doesn't match for %q: %d != %d", fi.Name(), nbytes, fi.Size())
+ }
+ }
+ return nil
+}
+
+func extractTar(pkgFile string, mediaInfo repository.MediaInfo, installDir string) error {
+ f, err := os.Open(pkgFile)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ var reader io.Reader
+ switch enc := mediaInfo.Encoding; enc {
+ case "":
+ reader = f
+ case "gzip":
+ var err error
+ if reader, err = gzip.NewReader(f); err != nil {
+ return err
+ }
+ case "bzip2":
+ reader = bzip2.NewReader(f)
+ default:
+ return fmt.Errorf("unsupported encoding: %q", enc)
+ }
+
+ tr := tar.NewReader(reader)
+ for {
+ hdr, err := tr.Next()
+ if err == io.EOF {
+ return nil
+ }
+ if err != nil {
+ return err
+ }
+ name := filepath.Join(installDir, hdr.Name)
+ if !strings.HasPrefix(name, installDir) {
+ return fmt.Errorf("failed to extract file %q outside of install directory", hdr.Name)
+ }
+ // Regular file
+ if hdr.Typeflag == tar.TypeReg {
+ out, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY, os.FileMode(hdr.Mode&0700))
+ if err != nil {
+ return err
+ }
+ nbytes, err := io.Copy(out, tr)
+ out.Close()
+ if err != nil {
+ return err
+ }
+ if nbytes != hdr.Size {
+ return fmt.Errorf("file size doesn't match for %q: %d != %d", hdr.Name, nbytes, hdr.Size)
+ }
+ continue
+ }
+ // Directory
+ if hdr.Typeflag == tar.TypeDir {
+ if err := os.Mkdir(name, os.FileMode(hdr.Mode&0700)); err != nil && !os.IsExist(err) {
+ return err
+ }
+ continue
+ }
+ // Skip unsupported types
+ // TODO(rthellend): Consider adding support for Symlink.
+ }
+}
diff --git a/services/mgmt/lib/packages/packages_test.go b/services/mgmt/lib/packages/packages_test.go
new file mode 100644
index 0000000..8182b44
--- /dev/null
+++ b/services/mgmt/lib/packages/packages_test.go
@@ -0,0 +1,242 @@
+package packages_test
+
+import (
+ "archive/tar"
+ "archive/zip"
+ "compress/gzip"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "reflect"
+ "sort"
+ "testing"
+
+ "veyron.io/veyron/veyron2/services/mgmt/repository"
+
+ "veyron.io/veyron/veyron/services/mgmt/lib/packages"
+)
+
+func TestInstall(t *testing.T) {
+ workdir, err := ioutil.TempDir("", "packages-test-")
+ if err != nil {
+ t.Fatalf("ioutil.TempDir failed: %v", err)
+ }
+ defer os.RemoveAll(workdir)
+ srcdir := filepath.Join(workdir, "src")
+ dstdir := filepath.Join(workdir, "dst")
+ createFiles(t, srcdir)
+
+ zipfile := filepath.Join(workdir, "archivezip")
+ tarfile := filepath.Join(workdir, "archivetar")
+ tgzfile := filepath.Join(workdir, "archivetgz")
+
+ makeZip(t, zipfile, srcdir)
+ makeTar(t, tarfile, srcdir)
+ doGzip(t, tarfile, tgzfile)
+
+ binfile := filepath.Join(workdir, "binfile")
+ ioutil.WriteFile(binfile, []byte("This is a binary file"), os.FileMode(0644))
+ ioutil.WriteFile(binfile+".__info", []byte(`{"type":"application/octet-stream"}`), os.FileMode(0644))
+
+ expected := []string{
+ "a perm:700",
+ "a/b perm:700",
+ "a/b/xyzzy.txt perm:600",
+ "a/bar.txt perm:600",
+ "a/foo.txt perm:600",
+ }
+ for _, file := range []string{zipfile, tarfile, tgzfile} {
+ setupDstDir(t, dstdir)
+ if err := packages.Install(file, dstdir); err != nil {
+ t.Errorf("packages.Install failed for %q: %v", file, err)
+ }
+ files := scanDir(dstdir)
+ if !reflect.DeepEqual(files, expected) {
+ t.Errorf("unexpected result for %q: Got %q, want %q", file, files, expected)
+ }
+ }
+
+ setupDstDir(t, dstdir)
+ if err := packages.Install(binfile, dstdir); err == nil {
+ t.Errorf("expected packages.Install to fail %q", binfile)
+ }
+}
+
+func TestMediaInfo(t *testing.T) {
+ testcases := []struct {
+ filename string
+ expected repository.MediaInfo
+ }{
+ {"foo.zip", repository.MediaInfo{Type: "application/zip"}},
+ {"foo.ZIP", repository.MediaInfo{Type: "application/zip"}},
+ {"foo.tar", repository.MediaInfo{Type: "application/x-tar"}},
+ {"foo.TAR", repository.MediaInfo{Type: "application/x-tar"}},
+ {"foo.tgz", repository.MediaInfo{Type: "application/x-tar", Encoding: "gzip"}},
+ {"FOO.TAR.GZ", repository.MediaInfo{Type: "application/x-tar", Encoding: "gzip"}},
+ {"foo.tbz2", repository.MediaInfo{Type: "application/x-tar", Encoding: "bzip2"}},
+ {"foo.tar.bz2", repository.MediaInfo{Type: "application/x-tar", Encoding: "bzip2"}},
+ {"foo", repository.MediaInfo{Type: "application/octet-stream"}},
+ }
+ for _, tc := range testcases {
+ if got := packages.MediaInfoForFileName(tc.filename); !reflect.DeepEqual(got, tc.expected) {
+ t.Errorf("unexpected result for %q: Got %v, want %v", tc.filename, got, tc.expected)
+ }
+ }
+}
+
+func createFiles(t *testing.T, dir string) {
+ if err := os.Mkdir(dir, os.FileMode(0755)); err != nil {
+ t.Fatalf("os.Mkdir(%q) failed: %v", dir, err)
+ }
+ dirs := []string{"a", "a/b"}
+ for _, d := range dirs {
+ fullname := filepath.Join(dir, d)
+ if err := os.Mkdir(fullname, os.FileMode(0755)); err != nil {
+ t.Fatalf("os.Mkdir(%q) failed: %v", fullname, err)
+ }
+ }
+ files := []string{"a/foo.txt", "a/bar.txt", "a/b/xyzzy.txt"}
+ for _, f := range files {
+ fullname := filepath.Join(dir, f)
+ if err := ioutil.WriteFile(fullname, []byte(f), os.FileMode(0644)); err != nil {
+ t.Fatalf("ioutil.WriteFile(%q) failed: %v", fullname, err)
+ }
+ }
+}
+
+func makeZip(t *testing.T, zipfile, dir string) {
+ z, err := os.OpenFile(zipfile, os.O_CREATE|os.O_WRONLY, os.FileMode(0644))
+ if err != nil {
+ t.Fatalf("os.OpenFile(%q) failed: %v", zipfile, err)
+ }
+ defer z.Close()
+ w := zip.NewWriter(z)
+ filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ t.Fatalf("Walk(%q) error: %v", dir, err)
+ }
+ if dir == path {
+ return nil
+ }
+ fh, err := zip.FileInfoHeader(info)
+ if err != nil {
+ t.Fatalf("FileInfoHeader failed: %v", err)
+ }
+ fh.Name, _ = filepath.Rel(dir, path)
+ hdr, err := w.CreateHeader(fh)
+ if err != nil {
+ t.Fatalf("w.CreateHeader failed: %v", err)
+ }
+ if !info.IsDir() {
+ content, err := ioutil.ReadFile(path)
+ if err != nil {
+ t.Fatalf("ioutil.ReadFile(%q) failed: %v", path, err)
+ }
+ if _, err = hdr.Write(content); err != nil {
+ t.Fatalf("hdr.Write(%q) failed: %v", content, err)
+ }
+ }
+ return nil
+ })
+ if err := w.Close(); err != nil {
+ t.Fatalf("w.Close() failed: %v", err)
+ }
+ if err := ioutil.WriteFile(zipfile+".__info", []byte(`{"type":"application/zip"}`), os.FileMode(0644)); err != nil {
+ t.Fatalf("ioutil.WriteFile() failed: %v", err)
+ }
+}
+
+func makeTar(t *testing.T, tarfile, dir string) {
+ tf, err := os.OpenFile(tarfile, os.O_CREATE|os.O_WRONLY, os.FileMode(0644))
+ if err != nil {
+ t.Fatalf("os.OpenFile(%q) failed: %v", tarfile, err)
+ }
+ defer tf.Close()
+
+ tw := tar.NewWriter(tf)
+ filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ t.Fatalf("Walk(%q) error: %v", dir, err)
+ }
+ if dir == path {
+ return nil
+ }
+ hdr, err := tar.FileInfoHeader(info, "")
+ if err != nil {
+ t.Fatalf("tar.FileInfoHeader failed: %v", err)
+ }
+ hdr.Name, _ = filepath.Rel(dir, path)
+ if err := tw.WriteHeader(hdr); err != nil {
+ t.Fatalf("tw.WriteHeader failed: %v", err)
+ }
+ if !info.IsDir() {
+ content, err := ioutil.ReadFile(path)
+ if err != nil {
+ t.Fatalf("ioutil.ReadFile(%q) failed: %v", path, err)
+ }
+ if _, err := tw.Write(content); err != nil {
+ t.Fatalf("tw.Write failed: %v", err)
+ }
+ }
+ return nil
+ })
+ if err := tw.Close(); err != nil {
+ t.Fatalf("tw.Close failed: %v", err)
+ }
+ if err := ioutil.WriteFile(tarfile+".__info", []byte(`{"type":"application/x-tar"}`), os.FileMode(0644)); err != nil {
+ t.Fatalf("ioutil.WriteFile() failed: %v", err)
+ }
+}
+
+func doGzip(t *testing.T, infile, outfile string) {
+ in, err := os.Open(infile)
+ if err != nil {
+ t.Fatalf("os.Open(%q) failed: %v", infile, err)
+ }
+ defer in.Close()
+ out, err := os.OpenFile(outfile, os.O_CREATE|os.O_WRONLY, os.FileMode(0644))
+ if err != nil {
+ t.Fatalf("os.OpenFile(%q) failed: %v", outfile, err)
+ }
+ defer out.Close()
+ writer := gzip.NewWriter(out)
+ defer writer.Close()
+ if _, err := io.Copy(writer, in); err != nil {
+ t.Fatalf("io.Copy() failed: %v", err)
+ }
+
+ info, err := packages.LoadMediaInfo(infile)
+ if err != nil {
+ t.Fatalf("LoadMediaInfo(%q) failed: %v", infile, err)
+ }
+ info.Encoding = "gzip"
+ if err := packages.SaveMediaInfo(outfile, info); err != nil {
+ t.Fatalf("SaveMediaInfo(%v) failed: %v", outfile, err)
+ }
+}
+
+func scanDir(root string) []string {
+ files := []string{}
+ filepath.Walk(root, func(path string, info os.FileInfo, _ error) error {
+ if root == path {
+ return nil
+ }
+ rel, _ := filepath.Rel(root, path)
+ perm := info.Mode() & 0700
+ files = append(files, fmt.Sprintf("%s perm:%o", rel, perm))
+ return nil
+ })
+ sort.Strings(files)
+ return files
+}
+
+func setupDstDir(t *testing.T, dst string) {
+ if err := os.RemoveAll(dst); err != nil {
+ t.Fatalf("os.RemoveAll(%q) failed: %v", dst, err)
+ }
+ if err := os.Mkdir(dst, os.FileMode(0755)); err != nil {
+ t.Fatalf("os.Mkdir(%q) failed: %v", dst, err)
+ }
+}
diff --git a/services/mgmt/logreader/impl/logfile.go b/services/mgmt/logreader/impl/logfile.go
index fd6f354..0011fa8 100644
--- a/services/mgmt/logreader/impl/logfile.go
+++ b/services/mgmt/logreader/impl/logfile.go
@@ -110,10 +110,10 @@
return reader.tell(), nil
}
-// VGlobChildren returns the list of files in a directory, or an empty list if
-// the object is a file.
-func (i *logfileService) VGlobChildren() ([]string, error) {
- vlog.VI(1).Infof("%v.VGlobChildren()", i.suffix)
+// GlobChildren__ returns the list of files in a directory streamed on a
+// channel. The list is empty if the object is a file.
+func (i *logfileService) GlobChildren__() (<-chan string, error) {
+ vlog.VI(1).Infof("%v.GlobChildren__()", i.suffix)
dirName, err := translateNameToFilename(i.root, i.suffix)
if err != nil {
return nil, err
@@ -129,22 +129,24 @@
return nil, nil
}
- f, err := os.Open(dirName)
- if err != nil {
- return nil, errOperationFailed
- }
- fi, err := f.Readdir(0)
- if err != nil {
- return nil, errOperationFailed
- }
- f.Close()
- children := []string{}
- for _, file := range fi {
- fileName := file.Name()
- if fileName == "." || fileName == ".." {
- continue
+ const batchSize = 100
+ ch := make(chan string, batchSize)
+ go func() {
+ defer close(ch)
+ f, err := os.Open(dirName)
+ if err != nil {
+ return
}
- children = append(children, file.Name())
- }
- return children, nil
+ defer f.Close()
+ for {
+ fi, err := f.Readdir(batchSize)
+ if err != nil {
+ return
+ }
+ for _, file := range fi {
+ ch <- file.Name()
+ }
+ }
+ }()
+ return ch, nil
}
diff --git a/services/mgmt/node/impl/app_service.go b/services/mgmt/node/impl/app_service.go
index 3473a37..b5ab1ab 100644
--- a/services/mgmt/node/impl/app_service.go
+++ b/services/mgmt/node/impl/app_service.go
@@ -21,6 +21,10 @@
// bin - application binary
// previous - symbolic link to previous version directory
// envelope - application envelope (JSON-encoded)
+// pkg/ - the application packages
+// <pkg name>
+// <pkg name>.__info
+// ...
// <version 2 timestamp>
// ...
// current - symbolic link to the current version
@@ -29,6 +33,9 @@
// credentials/ - holds veyron credentials (unless running
// through security agent)
// root/ - workspace that the instance is run from
+// packages/ - the installed packages
+// <pkg name>/
+// ...
// logs/ - stderr/stdout and log files generated by instance
// info - metadata for the instance (such as app
// cycle manager name and process id)
@@ -128,7 +135,8 @@
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/services/mgmt/appcycle"
"veyron.io/veyron/veyron2/services/mgmt/application"
- "veyron.io/veyron/veyron2/verror"
+ "veyron.io/veyron/veyron2/services/security/access"
+ "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
vexec "veyron.io/veyron/veyron/lib/exec"
@@ -136,6 +144,8 @@
vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron/security/agent"
"veyron.io/veyron/veyron/security/agent/keymgr"
+ libbinary "veyron.io/veyron/veyron/services/mgmt/lib/binary"
+ libpackages "veyron.io/veyron/veyron/services/mgmt/lib/packages"
iconfig "veyron.io/veyron/veyron/services/mgmt/node/config"
)
@@ -151,12 +161,12 @@
jsonInfo, err := json.Marshal(info)
if err != nil {
vlog.Errorf("Marshal(%v) failed: %v", info, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
infoPath := filepath.Join(dir, "info")
if err := ioutil.WriteFile(infoPath, jsonInfo, 0600); err != nil {
vlog.Errorf("WriteFile(%v) failed: %v", infoPath, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
return nil
}
@@ -166,10 +176,10 @@
info := new(instanceInfo)
if infoBytes, err := ioutil.ReadFile(infoPath); err != nil {
vlog.Errorf("ReadFile(%v) failed: %v", infoPath, err)
- return nil, errOperationFailed
+ return nil, verror2.Make(ErrOperationFailed, nil)
} else if err := json.Unmarshal(infoBytes, info); err != nil {
vlog.Errorf("Unmarshal(%v) failed: %v", infoBytes, err)
- return nil, errOperationFailed
+ return nil, verror2.Make(ErrOperationFailed, nil)
}
return info, nil
}
@@ -196,7 +206,7 @@
uat BlessingSystemAssociationStore
locks aclLocks
// Reference to the nodemanager top-level ACL list.
- nodeACL security.ACL
+ nodeACL access.TaggedACLMap
// securityAgent holds state related to the security agent (nil if not
// using the agent).
securityAgent *securityAgentState
@@ -206,12 +216,12 @@
jsonEnvelope, err := json.Marshal(envelope)
if err != nil {
vlog.Errorf("Marshal(%v) failed: %v", envelope, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
path := filepath.Join(dir, "envelope")
if err := ioutil.WriteFile(path, jsonEnvelope, 0600); err != nil {
vlog.Errorf("WriteFile(%v) failed: %v", path, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
return nil
}
@@ -221,10 +231,10 @@
envelope := new(application.Envelope)
if envelopeBytes, err := ioutil.ReadFile(path); err != nil {
vlog.Errorf("ReadFile(%v) failed: %v", path, err)
- return nil, errOperationFailed
+ return nil, verror2.Make(ErrOperationFailed, nil)
} else if err := json.Unmarshal(envelopeBytes, envelope); err != nil {
vlog.Errorf("Unmarshal(%v) failed: %v", envelopeBytes, err)
- return nil, errOperationFailed
+ return nil, verror2.Make(ErrOperationFailed, nil)
}
return envelope, nil
}
@@ -233,7 +243,7 @@
path := filepath.Join(dir, "origin")
if err := ioutil.WriteFile(path, []byte(originVON), 0600); err != nil {
vlog.Errorf("WriteFile(%v) failed: %v", path, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
return nil
}
@@ -242,7 +252,7 @@
path := filepath.Join(dir, "origin")
if originBytes, err := ioutil.ReadFile(path); err != nil {
vlog.Errorf("ReadFile(%v) failed: %v", path, err)
- return "", errOperationFailed
+ return "", verror2.Make(ErrOperationFailed, nil)
} else {
return string(originBytes), nil
}
@@ -308,7 +318,7 @@
if envelope.Title == application.NodeManagerTitle {
// Disallow node manager apps from being installed like a
// regular app.
- return nil, errInvalidOperation
+ return nil, verror2.Make(ErrInvalidOperation, ctx)
}
return envelope, nil
}
@@ -317,12 +327,27 @@
func newVersion(installationDir string, envelope *application.Envelope, oldVersionDir string) (string, error) {
versionDir := filepath.Join(installationDir, generateVersionDirName())
if err := mkdir(versionDir); err != nil {
- return "", errOperationFailed
+ return "", verror2.Make(ErrOperationFailed, nil)
+ }
+ pkgDir := filepath.Join(versionDir, "pkg")
+ if err := mkdir(pkgDir); err != nil {
+ return "", verror2.Make(ErrOperationFailed, nil)
}
// TODO(caprita): Share binaries if already existing locally.
if err := downloadBinary(versionDir, "bin", envelope.Binary); err != nil {
return versionDir, err
}
+ for localPkg, pkgName := range envelope.Packages {
+ if localPkg == "" || localPkg[0] == '.' || strings.Contains(localPkg, string(filepath.Separator)) {
+ vlog.Infof("invalid local package name: %q", localPkg)
+ return versionDir, verror2.Make(ErrOperationFailed, nil)
+ }
+ path := filepath.Join(pkgDir, localPkg)
+ if err := libbinary.DownloadToFile(rt.R().NewContext(), pkgName, path); err != nil {
+ vlog.Infof("DownloadToFile(%q, %q) failed: %v", pkgName, path, err)
+ return versionDir, verror2.Make(ErrOperationFailed, nil)
+ }
+ }
if err := saveEnvelope(versionDir, envelope); err != nil {
return versionDir, err
}
@@ -330,7 +355,7 @@
previousLink := filepath.Join(versionDir, "previous")
if err := os.Symlink(oldVersionDir, previousLink); err != nil {
vlog.Errorf("Symlink(%v, %v) failed: %v", oldVersionDir, previousLink, err)
- return versionDir, errOperationFailed
+ return versionDir, verror2.Make(ErrOperationFailed, nil)
}
}
// updateLink should be the last thing we do, after we've ensured the
@@ -339,23 +364,14 @@
return versionDir, updateLink(versionDir, filepath.Join(installationDir, "current"))
}
-// TODO(rjkroege): Refactor this code with the intance creation code.
-func initializeInstallationACLs(dir string, blessings []string, acl security.ACL) error {
- // Start out with the claimant's ACLs and add the invoker's blessings.
-
- var labels security.LabelSet
- if acl.In == nil {
- // The acl.In will be empty for an unclaimed node manager. In this case,
- // create it.
- acl.In = make(map[security.BlessingPattern]security.LabelSet)
+// TODO(rjkroege): Refactor this code with the instance creation code.
+func initializeInstallationACLs(dir string, blessings []string, acl access.TaggedACLMap) error {
+ // Add the invoker's blessings.
+ for _, b := range blessings {
+ for _, tag := range access.AllTypicalTags() {
+ acl.Add(security.BlessingPattern(b), string(tag))
+ }
}
- labels = security.AllLabels
-
- for _, name := range blessings {
- // TODO(rjkroege): Use custom labels.
- acl.In[security.BlessingPattern(name)] = labels
- }
-
aclDir := path.Join(dir, "acls")
aclData := path.Join(aclDir, "data")
aclSig := path.Join(aclDir, "signature")
@@ -364,9 +380,9 @@
func (i *appService) Install(call ipc.ServerContext, applicationVON string) (string, error) {
if len(i.suffix) > 0 {
- return "", errInvalidSuffix
+ return "", verror2.Make(ErrInvalidSuffix, call)
}
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := rt.R().NewContext().WithTimeout(ipcContextTimeout)
defer cancel()
envelope, err := fetchAppEnvelope(ctx, applicationVON)
if err != nil {
@@ -392,7 +408,10 @@
return "", err
}
- if err := initializeInstallationACLs(installationDir, call.RemoteBlessings().ForContext(call), i.nodeACL); err != nil {
+ // TODO(caprita,rjkroege): Should the installation ACLs really be
+ // seeded with the node ACL? Instead, might want to hide the nodeACL
+ // from the app?
+ if err := initializeInstallationACLs(installationDir, call.RemoteBlessings().ForContext(call), i.nodeACL.Copy()); err != nil {
return "", err
}
deferrer = nil
@@ -414,23 +433,23 @@
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, perm)
if err != nil {
vlog.Errorf("OpenFile(%v) failed: %v", path, err)
- return nil, errOperationFailed
+ return nil, verror2.Make(ErrOperationFailed, nil)
}
return file, nil
}
func installationDirCore(components []string, root string) (string, error) {
if nComponents := len(components); nComponents != 2 {
- return "", errInvalidSuffix
+ return "", verror2.Make(ErrInvalidSuffix, nil)
}
app, installation := components[0], components[1]
installationDir := filepath.Join(root, applicationDirName(app), installationDirName(installation))
if _, err := os.Stat(installationDir); err != nil {
if os.IsNotExist(err) {
- return "", errNotExist
+ return "", verror2.Make(ErrObjectNoExist, nil)
}
vlog.Errorf("Stat(%v) failed: %v", installationDir, err)
- return "", errOperationFailed
+ return "", verror2.Make(ErrOperationFailed, nil)
}
return installationDir, nil
}
@@ -446,13 +465,13 @@
client, err := rt.R().NewClient(options.VCSecurityNone)
if err != nil {
vlog.Errorf("NewClient() failed: %v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
defer client.Close()
// TODO(caprita): release the socket created by NewAgentPrincipal.
if p, err = agent.NewAgentPrincipal(client, int(conn.Fd()), rt.R().NewContext()); err != nil {
vlog.Errorf("NewAgentPrincipal() failed: %v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
info.SecurityAgentHandle = handle
} else {
@@ -462,7 +481,7 @@
var err error
if p, err = vsecurity.CreatePersistentPrincipal(credentialsDir, nil); err != nil {
vlog.Errorf("CreatePersistentPrincipal(%v, nil) failed: %v", credentialsDir, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
}
// Read the app installation version's envelope to obtain the app title.
@@ -479,27 +498,27 @@
// with the app title.
grantedBlessings := call.Blessings()
if grantedBlessings == nil {
- return errInvalidBlessing
+ return verror2.Make(ErrInvalidBlessing, nil)
}
// TODO(caprita): Revisit UnconstrainedUse.
appBlessings, err := nmPrincipal.Bless(p.PublicKey(), grantedBlessings, envelope.Title, security.UnconstrainedUse())
if err != nil {
vlog.Errorf("Bless() failed: %v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
// The blessings we extended from the blessings that the Start-er
// granted are the default blessings for the app.
if err := p.BlessingStore().SetDefault(appBlessings); err != nil {
vlog.Errorf("BlessingStore.SetDefault() failed: %v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
if _, err := p.BlessingStore().Set(appBlessings, security.AllPrincipals); err != nil {
vlog.Errorf("BlessingStore.Set() failed: %v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
if err := p.AddToRoots(appBlessings); err != nil {
vlog.Errorf("AddToRoots() failed: %v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
// In addition, we give the app separate blessings for the purpose of
// communicating with the node manager.
@@ -522,7 +541,7 @@
for _, n := range names {
if _, err := p.BlessingStore().Set(nmBlessings, security.BlessingPattern(n)); err != nil {
vlog.Errorf("BlessingStore.Set() failed: %v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
}
// We also want to override the app cycle manager's server blessing in
@@ -532,16 +551,16 @@
randomPattern, err := generateRandomString()
if err != nil {
vlog.Errorf("generateRandomString() failed: %v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
if _, err := p.BlessingStore().Set(nmBlessings, security.BlessingPattern(randomPattern)); err != nil {
vlog.Errorf("BlessingStore.Set() failed: %v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
info.NodeManagerPeerPattern = randomPattern
if err := p.AddToRoots(nmBlessings); err != nil {
vlog.Errorf("AddToRoots() failed: %v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
return nil
}
@@ -554,23 +573,40 @@
return installationDirCore(i.suffix, i.config.Root)
}
-func initializeInstanceACLs(instanceDir string, blessings []string, acl security.ACL) error {
- if acl.In == nil {
- // The acl.In will be empty for an unclaimed node manager. In this case,
- // create it
- acl.In = make(map[security.BlessingPattern]security.LabelSet)
+// installPackages installs all the packages for a new instance.
+func installPackages(versionDir, instanceDir string) error {
+ envelope, err := loadEnvelope(versionDir)
+ if err != nil {
+ return err
}
-
- labels := security.AllLabels
- for _, name := range blessings {
- // TODO(rjkroege): Use custom labels.
- acl.In[security.BlessingPattern(name)] = labels
+ packagesDir := filepath.Join(instanceDir, "root", "packages")
+ if err := os.MkdirAll(packagesDir, os.FileMode(0700)); err != nil {
+ return err
}
+ // TODO(rthellend): Consider making the packages read-only and sharing
+ // them between apps or instances.
+ for pkg, _ := range envelope.Packages {
+ pkgFile := filepath.Join(versionDir, "pkg", pkg)
+ dstDir := filepath.Join(packagesDir, pkg)
+ if err := os.MkdirAll(dstDir, os.FileMode(0700)); err != nil {
+ return err
+ }
+ if err := libpackages.Install(pkgFile, dstDir); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+func initializeInstanceACLs(instanceDir string, blessings []string, acl access.TaggedACLMap) error {
+ for _, b := range blessings {
+ for _, tag := range access.AllTypicalTags() {
+ acl.Add(security.BlessingPattern(b), string(tag))
+ }
+ }
aclDir := path.Join(instanceDir, "acls")
aclData := path.Join(aclDir, "data")
aclSig := path.Join(aclDir, "signature")
-
return writeACLs(aclData, aclSig, aclDir, acl)
}
@@ -581,23 +617,27 @@
return "", "", err
}
if !installationStateIs(installationDir, active) {
- return "", "", errInvalidOperation
+ return "", "", verror2.Make(ErrInvalidOperation, call)
}
instanceID := generateID()
instanceDir := filepath.Join(installationDir, "instances", instanceDirName(instanceID))
if mkdir(instanceDir) != nil {
- return "", instanceID, errOperationFailed
+ return "", instanceID, verror2.Make(ErrOperationFailed, call)
}
currLink := filepath.Join(installationDir, "current")
versionDir, err := filepath.EvalSymlinks(currLink)
if err != nil {
vlog.Errorf("EvalSymlinks(%v) failed: %v", currLink, err)
- return instanceDir, instanceID, errOperationFailed
+ return instanceDir, instanceID, verror2.Make(ErrOperationFailed, call)
}
versionLink := filepath.Join(instanceDir, "version")
if err := os.Symlink(versionDir, versionLink); err != nil {
vlog.Errorf("Symlink(%v, %v) failed: %v", versionDir, versionLink, err)
- return instanceDir, instanceID, errOperationFailed
+ return instanceDir, instanceID, verror2.Make(ErrOperationFailed, call)
+ }
+ if err := installPackages(versionDir, instanceDir); err != nil {
+ vlog.Errorf("installPackages(%v, %v) failed: %v", versionDir, instanceDir, err)
+ return instanceDir, instanceID, verror2.Make(ErrOperationFailed, call)
}
instanceInfo := new(instanceInfo)
if err := setupPrincipal(instanceDir, versionDir, call, i.securityAgent, instanceInfo); err != nil {
@@ -610,7 +650,7 @@
return instanceDir, instanceID, err
}
- if err := initializeInstanceACLs(instanceDir, call.RemoteBlessings().ForContext(call), i.nodeACL); err != nil {
+ if err := initializeInstanceACLs(instanceDir, call.RemoteBlessings().ForContext(call), i.nodeACL.Copy()); err != nil {
return instanceDir, instanceID, err
}
return instanceDir, instanceID, nil
@@ -631,11 +671,12 @@
// and is probably not a good fit in other contexts. Revisit the design
// as appropriate. This function also internalizes a decision as to when
// it is possible to start an application that needs to be made explicit.
-func systemAccountForHelper(helperPath string, identityNames []string, uat BlessingSystemAssociationStore) (systemName string, err error) {
+func systemAccountForHelper(ctx ipc.ServerContext, helperPath string, uat BlessingSystemAssociationStore) (systemName string, err error) {
+ identityNames := ctx.RemoteBlessings().ForContext(ctx)
helperStat, err := os.Stat(helperPath)
if err != nil {
vlog.Errorf("Stat(%v) failed: %v. helper is required.", helperPath, err)
- return "", errOperationFailed
+ return "", verror2.Make(ErrOperationFailed, ctx)
}
haveHelper := isSetuid(helperStat)
systemName, present := uat.SystemAccountForBlessings(identityNames)
@@ -648,7 +689,7 @@
// Therefore, the node manager must never run an app as itself to
// prevent an app trivially granting itself root permissions.
// There must be an associated uname for the account in this case.
- return "", verror.NoAccessf("use of setuid helper requires an associated uname.")
+ return "", verror2.Make(verror2.NoAccess, ctx, "use of setuid helper requires an associated uname.")
case !haveHelper:
// When the helper is not setuid, the helper can't change the
// app's uid so just run the app as the node manager's uname
@@ -657,11 +698,11 @@
user, err := user.Current()
if err != nil {
vlog.Errorf("user.Current() failed: %v", err)
- return "", errOperationFailed
+ return "", verror2.Make(ErrOperationFailed, ctx)
}
return user.Username, nil
}
- return "", errOperationFailed
+ return "", verror2.Make(ErrOperationFailed, ctx)
}
func genCmd(instanceDir, helperPath, systemName string) (*exec.Cmd, error) {
@@ -669,7 +710,7 @@
versionDir, err := filepath.EvalSymlinks(versionLink)
if err != nil {
vlog.Errorf("EvalSymlinks(%v) failed: %v", versionLink, err)
- return nil, errOperationFailed
+ return nil, verror2.Make(ErrOperationFailed, nil)
}
envelope, err := loadEnvelope(versionDir)
if err != nil {
@@ -678,7 +719,7 @@
binPath := filepath.Join(versionDir, "bin")
if _, err := os.Stat(binPath); err != nil {
vlog.Errorf("Stat(%v) failed: %v", binPath, err)
- return nil, errOperationFailed
+ return nil, verror2.Make(ErrOperationFailed, nil)
}
cmd := exec.Command(helperPath)
@@ -778,21 +819,20 @@
agentCleaner()
}
vlog.Errorf("Start() failed: %v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
if agentCleaner != nil {
agentCleaner()
}
// Wait for the child process to start.
- timeout := 10 * time.Second
- if err := handle.WaitForReady(timeout); err != nil {
- vlog.Errorf("WaitForReady(%v) failed: %v", timeout, err)
- return errOperationFailed
+ if err := handle.WaitForReady(childReadyTimeout); err != nil {
+ vlog.Errorf("WaitForReady(%v) failed: %v", childReadyTimeout, err)
+ return verror2.Make(ErrOperationFailed, nil)
}
- childName, err := listener.waitForValue(timeout)
+ childName, err := listener.waitForValue(childReadyTimeout)
if err != nil {
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
info.AppCycleMgrName, info.Pid = childName, handle.Pid()
if err := saveInstanceInfo(instanceDir, info); err != nil {
@@ -829,7 +869,7 @@
return nil, err
}
- systemName, err := systemAccountForHelper(helper, call.RemoteBlessings().ForContext(call), i.uat)
+ systemName, err := systemAccountForHelper(call, helper, i.uat)
if err != nil {
cleanupDir(instanceDir, helper)
return nil, err
@@ -854,7 +894,7 @@
// referred to by the given suffix relative to the given root directory.
func instanceDir(root string, suffix []string) (string, error) {
if nComponents := len(suffix); nComponents != 3 {
- return "", errInvalidSuffix
+ return "", verror2.Make(ErrInvalidSuffix, nil)
}
app, installation, instance := suffix[0], suffix[1], suffix[2]
instancesDir := filepath.Join(root, applicationDirName(app), installationDirName(installation), "instances")
@@ -875,7 +915,7 @@
return err
}
- systemName, err := systemAccountForHelper(i.config.Helper, call.RemoteBlessings().ForContext(call), i.uat)
+ systemName, err := systemAccountForHelper(call, i.config.Helper, i.uat)
if err != nil {
return err
}
@@ -886,19 +926,19 @@
}
if startSystemName != systemName {
- return verror.NoAccessf("Not allowed to resume an application under a different system name.")
+ return verror2.Make(verror2.NoAccess, call, "Not allowed to resume an application under a different system name.")
}
return i.run(instanceDir, systemName)
}
func stopAppRemotely(appVON string) error {
appStub := appcycle.AppCycleClient(appVON)
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := rt.R().NewContext().WithTimeout(ipcContextTimeout)
defer cancel()
stream, err := appStub.Stop(ctx)
if err != nil {
vlog.Errorf("%v.Stop() failed: %v", appVON, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
rstream := stream.RecvStream()
for rstream.Advance() {
@@ -906,11 +946,11 @@
}
if err := rstream.Err(); err != nil {
vlog.Errorf("Advance() failed: %v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
if err := stream.Finish(); err != nil {
vlog.Errorf("Finish() failed: %v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
return nil
}
@@ -930,7 +970,7 @@
if err != nil {
return err
}
- if err := transitionInstance(instanceDir, suspended, stopped); err == errOperationFailed || err == nil {
+ if err := transitionInstance(instanceDir, suspended, stopped); verror2.Is(err, ErrOperationFailed.ID) || err == nil {
return err
}
if err := transitionInstance(instanceDir, started, stopping); err != nil {
@@ -966,19 +1006,19 @@
return transitionInstallation(installationDir, active, uninstalled)
}
-func (i *appService) Update(ipc.ServerContext) error {
+func (i *appService) Update(call ipc.ServerContext) error {
installationDir, err := i.installationDir()
if err != nil {
return err
}
if !installationStateIs(installationDir, active) {
- return errInvalidOperation
+ return verror2.Make(ErrInvalidOperation, call)
}
originVON, err := loadOrigin(installationDir)
if err != nil {
return err
}
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := rt.R().NewContext().WithTimeout(ipcContextTimeout)
defer cancel()
newEnvelope, err := fetchAppEnvelope(ctx, originVON)
if err != nil {
@@ -988,7 +1028,7 @@
oldVersionDir, err := filepath.EvalSymlinks(currLink)
if err != nil {
vlog.Errorf("EvalSymlinks(%v) failed: %v", currLink, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, call)
}
// NOTE(caprita): A race can occur between two competing updates, where
// both use the old version as their baseline. This can result in both
@@ -1003,10 +1043,10 @@
return err
}
if oldEnvelope.Title != newEnvelope.Title {
- return errIncompatibleUpdate
+ return verror2.Make(ErrAppTitleMismatch, call)
}
if reflect.DeepEqual(oldEnvelope, newEnvelope) {
- return errUpdateNoOp
+ return verror2.Make(ErrUpdateNoOp, call)
}
versionDir, err := newVersion(installationDir, newEnvelope, oldVersionDir)
if err != nil {
@@ -1021,13 +1061,13 @@
return nil
}
-func (i *appService) Revert(ipc.ServerContext) error {
+func (i *appService) Revert(ctx ipc.ServerContext) error {
installationDir, err := i.installationDir()
if err != nil {
return err
}
if !installationStateIs(installationDir, active) {
- return errInvalidOperation
+ return verror2.Make(ErrInvalidOperation, ctx)
}
// NOTE(caprita): A race can occur between an update and a revert, where
// both use the same current version as their starting point. This will
@@ -1038,21 +1078,21 @@
currVersionDir, err := filepath.EvalSymlinks(currLink)
if err != nil {
vlog.Errorf("EvalSymlinks(%v) failed: %v", currLink, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, ctx)
}
previousLink := filepath.Join(currVersionDir, "previous")
if _, err := os.Lstat(previousLink); err != nil {
if os.IsNotExist(err) {
// No 'previous' link -- must be the first version.
- return errUpdateNoOp
+ return verror2.Make(ErrUpdateNoOp, ctx)
}
vlog.Errorf("Lstat(%v) failed: %v", previousLink, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, ctx)
}
prevVersionDir, err := filepath.EvalSymlinks(previousLink)
if err != nil {
vlog.Errorf("EvalSymlinks(%v) failed: %v", previousLink, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, ctx)
}
return updateLink(prevVersionDir, currLink)
}
@@ -1154,7 +1194,7 @@
}
}
-func (i *appService) VGlobChildren() ([]string, error) {
+func (i *appService) GlobChildren__() (<-chan string, error) {
tree := newTreeNode()
switch len(i.suffix) {
case 0:
@@ -1171,19 +1211,20 @@
}
i.scanInstance(tree, i.suffix[0], dir)
default:
- return nil, errNotExist
+ return nil, verror2.Make(ErrObjectNoExist, nil)
}
n := tree.find(i.suffix, false)
if n == nil {
- return nil, errInvalidSuffix
+ return nil, verror2.Make(ErrInvalidSuffix, nil)
}
- children := make([]string, len(n.children))
- index := 0
- for child, _ := range n.children {
- children[index] = child
- index++
- }
- return children, nil
+ ch := make(chan string, 100)
+ go func() {
+ for child, _ := range n.children {
+ ch <- child
+ }
+ close(ch)
+ }()
+ return ch, nil
}
// TODO(rjkroege): Refactor to eliminate redundancy with newAppSpecificAuthorizer.
@@ -1203,11 +1244,11 @@
}
return p, nil
}
- return "", errInvalidSuffix
+ return "", verror2.Make(ErrInvalidSuffix, nil)
}
// TODO(rjkroege): Consider maintaining an in-memory ACL cache.
-func (i *appService) SetACL(_ ipc.ServerContext, acl security.ACL, etag string) error {
+func (i *appService) SetACL(_ ipc.ServerContext, acl access.TaggedACLMap, etag string) error {
dir, err := dirFromSuffix(i.suffix, i.config.Root)
if err != nil {
return err
@@ -1215,10 +1256,10 @@
return setAppACL(i.locks, dir, acl, etag)
}
-func (i *appService) GetACL(_ ipc.ServerContext) (acl security.ACL, etag string, err error) {
+func (i *appService) GetACL(_ ipc.ServerContext) (acl access.TaggedACLMap, etag string, err error) {
dir, err := dirFromSuffix(i.suffix, i.config.Root)
if err != nil {
- return security.ACL{}, "", err
+ return nil, "", err
}
return getAppACL(i.locks, dir)
}
diff --git a/services/mgmt/node/impl/app_state.go b/services/mgmt/node/impl/app_state.go
index 9fbc1c2..63071ca 100644
--- a/services/mgmt/node/impl/app_state.go
+++ b/services/mgmt/node/impl/app_state.go
@@ -6,6 +6,7 @@
"os"
"path/filepath"
+ "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
)
@@ -98,10 +99,10 @@
targetState := filepath.Join(dir, target.String())
if err := os.Rename(initialState, targetState); err != nil {
if os.IsNotExist(err) {
- return errInvalidOperation
+ return verror2.Make(ErrInvalidOperation, nil)
}
vlog.Errorf("Rename(%v, %v) failed: %v", initialState, targetState, err) // Something went really wrong.
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
return nil
}
@@ -110,7 +111,7 @@
initialStatus := filepath.Join(dir, initial.String())
if err := ioutil.WriteFile(initialStatus, []byte("status"), 0600); err != nil {
vlog.Errorf("WriteFile(%v) failed: %v", initialStatus, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
return nil
}
diff --git a/services/mgmt/node/impl/association_instance.go b/services/mgmt/node/impl/association_instance.go
index 02ae3a7..32dd993 100644
--- a/services/mgmt/node/impl/association_instance.go
+++ b/services/mgmt/node/impl/association_instance.go
@@ -7,6 +7,7 @@
"io/ioutil"
"path/filepath"
+ "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
)
@@ -14,7 +15,7 @@
snp := filepath.Join(dir, "systemname")
if err := ioutil.WriteFile(snp, []byte(systemName), 0600); err != nil {
vlog.Errorf("WriteFile(%v, %v) failed: %v", snp, systemName, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
return nil
}
@@ -24,7 +25,7 @@
name, err := ioutil.ReadFile(snp)
if err != nil {
vlog.Errorf("ReadFile(%v) failed: %v", snp, err)
- return "", errOperationFailed
+ return "", verror2.Make(ErrOperationFailed, nil)
}
return string(name), nil
}
diff --git a/services/mgmt/node/impl/association_state.go b/services/mgmt/node/impl/association_state.go
index 8fe738f..1e9a285 100644
--- a/services/mgmt/node/impl/association_state.go
+++ b/services/mgmt/node/impl/association_state.go
@@ -1,13 +1,13 @@
package impl
import (
+ "encoding/json"
"os"
"path/filepath"
- "veyron.io/veyron/veyron2/services/mgmt/node"
- "veyron.io/veyron/veyron2/verror"
-
- "encoding/json"
"sync"
+
+ "veyron.io/veyron/veyron2/services/mgmt/node"
+ "veyron.io/veyron/veyron2/verror2"
)
// BlessingSystemAssociationStore manages a persisted association between
@@ -65,7 +65,7 @@
func (u *association) serialize() (err error) {
f, err := os.OpenFile(u.filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
- return verror.NoExistf("Could not open association file for writing %s: %v", u.filename, err)
+ return verror2.Make(verror2.NoExist, nil, "Could not open association file for writing", u.filename, err)
}
defer func() {
if closerr := f.Close(); closerr != nil {
@@ -100,13 +100,13 @@
func NewBlessingSystemAssociationStore(root string) (BlessingSystemAssociationStore, error) {
nddir := filepath.Join(root, "node-manager", "node-data")
if err := os.MkdirAll(nddir, os.FileMode(0700)); err != nil {
- return nil, verror.NoExistf("Could not create node-data directory %s: %v\n", nddir, err)
+ return nil, verror2.Make(verror2.NoExist, nil, "Could not create node-data directory", nddir, err)
}
msf := filepath.Join(nddir, "associated.accounts")
f, err := os.Open(msf)
if err != nil && os.IsExist(err) {
- return nil, verror.NoExistf("Could not open association file %s: %v\n", msf, err)
+ return nil, verror2.Make(verror2.NoExist, nil, "Could not open association file", msf, err)
}
defer f.Close()
@@ -117,7 +117,7 @@
dec := json.NewDecoder(f)
err := dec.Decode(&a.data)
if err != nil {
- return nil, verror.NoExistf("Could not read association file %s: %v\n", msf, err)
+ return nil, verror2.Make(verror2.NoExist, nil, "Could not read association file", msf, err)
}
}
return BlessingSystemAssociationStore(a), nil
diff --git a/services/mgmt/node/impl/association_state_test.go b/services/mgmt/node/impl/association_state_test.go
index 4d313dc..39b8f28 100644
--- a/services/mgmt/node/impl/association_state_test.go
+++ b/services/mgmt/node/impl/association_state_test.go
@@ -128,13 +128,17 @@
}
// Create a NewBlessingSystemAssociationStore directory as a side-effect.
- nbsa1, err = impl.NewBlessingSystemAssociationStore(os.TempDir())
- defer os.RemoveAll(path.Join(os.TempDir(), "node-manager"))
+ dir, err = ioutil.TempDir("", "bad-starting-conditions")
+ if err != nil {
+ t.Fatalf("TempDir failed: %v", err)
+ }
+ nbsa1, err = impl.NewBlessingSystemAssociationStore(dir)
+ defer os.RemoveAll(dir)
if err != nil {
t.Fatalf("NewBlessingSystemAssociationStore failed: %v", err)
}
- tpath := path.Join(os.TempDir(), "node-manager", "node-data", "associated.accounts")
+ tpath := path.Join(dir, "node-manager", "node-data", "associated.accounts")
f, err := os.Create(tpath)
if err != nil {
t.Fatalf("could not open backing file for setup: %v", err)
diff --git a/services/mgmt/node/impl/callback.go b/services/mgmt/node/impl/callback.go
index 02238c6..94241d9 100644
--- a/services/mgmt/node/impl/callback.go
+++ b/services/mgmt/node/impl/callback.go
@@ -1,8 +1,6 @@
package impl
import (
- "time"
-
"veyron.io/veyron/veyron2/mgmt"
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/vlog"
@@ -24,7 +22,7 @@
return
}
nmClient := node.ConfigClient(callbackName)
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := rt.R().NewContext().WithTimeout(ipcContextTimeout)
defer cancel()
if err := nmClient.Set(ctx, mgmt.ChildNameConfigKey, name); err != nil {
vlog.Fatalf("Set(%v, %v) failed: %v", mgmt.ChildNameConfigKey, name, err)
diff --git a/services/mgmt/node/impl/config_service.go b/services/mgmt/node/impl/config_service.go
index 609e1c6..b7c504d 100644
--- a/services/mgmt/node/impl/config_service.go
+++ b/services/mgmt/node/impl/config_service.go
@@ -12,6 +12,7 @@
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
+ "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
)
@@ -64,7 +65,7 @@
return value, nil
case <-time.After(timeout):
vlog.Errorf("Waiting for callback timed out")
- return "", errOperationFailed
+ return "", verror2.Make(ErrOperationFailed, nil)
}
}
@@ -127,7 +128,7 @@
i.callback.Lock()
if _, ok := i.callback.channels[id]; !ok {
i.callback.Unlock()
- return errInvalidSuffix
+ return verror2.Make(ErrInvalidSuffix, nil)
}
channel, ok := i.callback.channels[id][key]
i.callback.Unlock()
diff --git a/services/mgmt/node/impl/dispatcher.go b/services/mgmt/node/impl/dispatcher.go
index dd4b647..45b2bd2 100644
--- a/services/mgmt/node/impl/dispatcher.go
+++ b/services/mgmt/node/impl/dispatcher.go
@@ -12,7 +12,6 @@
"strings"
"sync"
- vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron/security/agent"
"veyron.io/veyron/veyron/security/agent/keymgr"
vflag "veyron.io/veyron/veyron/security/flag"
@@ -30,6 +29,7 @@
"veyron.io/veyron/veyron2/services/mgmt/stats"
"veyron.io/veyron/veyron2/services/security/access"
"veyron.io/veyron/veyron2/verror"
+ "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
)
@@ -48,7 +48,7 @@
type dispatcher struct {
// acl/auth hold the acl and authorizer used to authorize access to the
// node manager methods.
- acl security.ACL
+ acl access.TaggedACLMap
auth security.Authorizer
// etag holds the version string for the ACL. We use this for optimistic
// concurrency control when clients update the ACLs for the node manager.
@@ -72,18 +72,19 @@
appsSuffix = "apps"
nodeSuffix = "nm"
configSuffix = "cfg"
+
+ pkgPath = "veyron.io/veyron/veyron/services/mgmt/node/impl"
)
var (
- errInvalidSuffix = verror.BadArgf("invalid suffix")
- errOperationFailed = verror.Internalf("operation failed")
- errInProgress = verror.Existsf("operation in progress")
- errIncompatibleUpdate = verror.BadArgf("update failed: mismatching app title")
- // TODO(bprosnitz) Remove the TODO blocks in util_test when these are upgraded to verror2
- errUpdateNoOp = verror.NoExistf("no different version available")
- errNotExist = verror.NoExistf("object does not exist")
- errInvalidOperation = verror.BadArgf("invalid operation")
- errInvalidBlessing = verror.BadArgf("invalid claim blessing")
+ ErrInvalidSuffix = verror2.Register(pkgPath+".InvalidSuffix", verror2.NoRetry, "")
+ ErrOperationFailed = verror2.Register(pkgPath+".OperationFailed", verror2.NoRetry, "")
+ ErrOperationInProgress = verror2.Register(pkgPath+".OperationInProgress", verror2.NoRetry, "")
+ ErrAppTitleMismatch = verror2.Register(pkgPath+".AppTitleMismatch", verror2.NoRetry, "")
+ ErrUpdateNoOp = verror2.Register(pkgPath+".UpdateNoOp", verror2.NoRetry, "")
+ ErrObjectNoExist = verror2.Register(pkgPath+".ObjectNoExist", verror2.NoRetry, "")
+ ErrInvalidOperation = verror2.Register(pkgPath+".InvalidOperation", verror2.NoRetry, "")
+ ErrInvalidBlessing = verror2.Register(pkgPath+".InvalidBlessing", verror2.NoRetry, "")
)
// NewDispatcher is the node manager dispatcher factory.
@@ -124,7 +125,7 @@
if err != nil {
return nil, fmt.Errorf("failed to read nodemanager ACL file:%v", err)
}
- acl, err := vsecurity.LoadACL(reader)
+ acl, err := access.ReadTaggedACLMap(reader)
if err != nil {
return nil, fmt.Errorf("failed to load nodemanager ACL:%v", err)
}
@@ -135,7 +136,7 @@
if d.auth = vflag.NewAuthorizerOrDie(); d.auth == nil {
// If there are no specified ACLs we grant nodemanager access to all
// principals until it is claimed.
- d.auth = vsecurity.NewACLAuthorizer(vsecurity.OpenACL())
+ d.auth = allowEveryone{}
}
}
// If we're in 'security agent mode', set up the key manager agent.
@@ -162,29 +163,31 @@
// TODO(rjkroege): Scrub the state tree of installation and instance ACL files.
if len(names) == 0 {
vlog.Errorf("No names for claimer(%v) are trusted", proof)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
rt.R().Principal().BlessingStore().Set(proof, security.AllPrincipals)
rt.R().Principal().BlessingStore().SetDefault(proof)
// Create ACLs to transfer nodemanager permissions to the provided identity.
- acl := security.ACL{In: make(map[security.BlessingPattern]security.LabelSet)}
- for _, name := range names {
- acl.In[security.BlessingPattern(name)] = security.AllLabels
+ acl := make(access.TaggedACLMap)
+ for _, n := range names {
+ for _, tag := range access.AllTypicalTags() {
+ acl.Add(security.BlessingPattern(n), string(tag))
+ }
}
_, etag, err := d.getACL()
if err != nil {
vlog.Errorf("Failed to getACL:%v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
if err := d.setACL(acl, etag, true /* store ACL on disk */); err != nil {
vlog.Errorf("Failed to setACL:%v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
return nil
}
// TODO(rjkroege): Further refactor ACL-setting code.
-func setAppACL(locks aclLocks, dir string, acl security.ACL, etag string) error {
+func setAppACL(locks aclLocks, dir string, acl access.TaggedACLMap, etag string) error {
aclpath := path.Join(dir, "acls", "data")
sigpath := path.Join(dir, "acls", "signature")
@@ -204,9 +207,9 @@
}
defer f.Close()
- curACL, err := vsecurity.LoadACL(f)
+ curACL, err := access.ReadTaggedACLMap(f)
if err != nil {
- vlog.Errorf("LoadACL(%s) failed: %v", aclpath, err)
+ vlog.Errorf("ReadTaggedACLMap(%s) failed: %v", aclpath, err)
return err
}
curEtag, err := computeEtag(curACL)
@@ -222,7 +225,7 @@
return writeACLs(aclpath, sigpath, dir, acl)
}
-func getAppACL(locks aclLocks, dir string) (security.ACL, string, error) {
+func getAppACL(locks aclLocks, dir string) (access.TaggedACLMap, string, error) {
aclpath := path.Join(dir, "acls", "data")
// Acquire lock. Locks are per path to an acls file.
@@ -236,30 +239,26 @@
f, err := os.Open(aclpath)
if err != nil {
- vlog.Errorf("LoadACL(%s) failed: %v", aclpath, err)
- return security.ACL{}, "", err
+ vlog.Errorf("Open(%s) failed: %v", aclpath, err)
+ return nil, "", err
}
defer f.Close()
- acl, err := vsecurity.LoadACL(f)
+ acl, err := access.ReadTaggedACLMap(f)
if err != nil {
- vlog.Errorf("LoadACL(%s) failed: %v", aclpath, err)
- return security.ACL{}, "", err
+ vlog.Errorf("ReadTaggedACLMap(%s) failed: %v", aclpath, err)
+ return nil, "", err
}
curEtag, err := computeEtag(acl)
if err != nil {
- return security.ACL{}, "", err
- }
-
- if err != nil {
- return security.ACL{}, "", err
+ return nil, "", err
}
return acl, curEtag, nil
}
-func computeEtag(acl security.ACL) (string, error) {
+func computeEtag(acl access.TaggedACLMap) (string, error) {
b := new(bytes.Buffer)
- if err := vsecurity.SaveACL(b, acl); err != nil {
+ if err := acl.WriteTo(b); err != nil {
vlog.Errorf("Failed to save ACL:%v", err)
return "", err
}
@@ -269,7 +268,7 @@
return etag, nil
}
-func writeACLs(aclFile, sigFile, dir string, acl security.ACL) error {
+func writeACLs(aclFile, sigFile, dir string, acl access.TaggedACLMap) error {
// Create dir directory if it does not exist
os.MkdirAll(dir, os.FileMode(0700))
// Save the object to temporary data and signature files, and then move
@@ -277,27 +276,27 @@
data, err := ioutil.TempFile(dir, "data")
if err != nil {
vlog.Errorf("Failed to open tmpfile data:%v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
defer os.Remove(data.Name())
sig, err := ioutil.TempFile(dir, "sig")
if err != nil {
vlog.Errorf("Failed to open tmpfile sig:%v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
defer os.Remove(sig.Name())
writer, err := serialization.NewSigningWriteCloser(data, sig, rt.R().Principal(), nil)
if err != nil {
vlog.Errorf("Failed to create NewSigningWriteCloser:%v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
- if err = vsecurity.SaveACL(writer, acl); err != nil {
+ if err = acl.WriteTo(writer); err != nil {
vlog.Errorf("Failed to SaveACL:%v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
if err = writer.Close(); err != nil {
vlog.Errorf("Failed to Close() SigningWriteCloser:%v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
if err := os.Rename(data.Name(), aclFile); err != nil {
return err
@@ -308,7 +307,7 @@
return nil
}
-func (d *dispatcher) setACL(acl security.ACL, etag string, writeToFile bool) error {
+func (d *dispatcher) setACL(acl access.TaggedACLMap, etag string, writeToFile bool) error {
d.mu.Lock()
defer d.mu.Unlock()
aclFile, sigFile, nodedata := d.getACLFilePaths()
@@ -326,11 +325,15 @@
if err != nil {
return err
}
- d.acl, d.etag, d.auth = acl, etag, vsecurity.NewACLAuthorizer(acl)
+ auth, err := access.TaggedACLAuthorizer(acl, access.TypicalTagType())
+ if err != nil {
+ return err
+ }
+ d.acl, d.etag, d.auth = acl, etag, auth
return nil
}
-func (d *dispatcher) getACL() (acl security.ACL, etag string, err error) {
+func (d *dispatcher) getACL() (acl access.TaggedACLMap, etag string, err error) {
d.mu.RLock()
defer d.mu.RUnlock()
return d.acl, d.etag, nil
@@ -346,7 +349,7 @@
}
}
if len(components) == 0 {
- return ipc.VChildrenGlobberInvoker(nodeSuffix, appsSuffix), d.auth, nil
+ return ipc.ChildrenGlobberInvoker(nodeSuffix, appsSuffix), d.auth, nil
}
// The implementation of the node manager is split up into several
// invokers, which are instantiated depending on the receiver name
@@ -382,20 +385,17 @@
return nil, nil, err
}
if !instanceStateIs(appInstanceDir, started) {
- return nil, nil, errInvalidSuffix
+ return nil, nil, verror2.Make(ErrInvalidSuffix, nil)
}
- var label security.Label
var sigStub signatureStub
if kind == "pprof" {
- label = security.DebugLabel
sigStub = pprof.PProfServer(nil)
} else {
- label = security.DebugLabel | security.MonitoringLabel
sigStub = stats.StatsServer(nil)
}
suffix := naming.Join("__debug", naming.Join(components[4:]...))
remote := naming.JoinAddressName(info.AppCycleMgrName, suffix)
- return &proxyInvoker{remote, label, sigStub}, d.auth, nil
+ return &proxyInvoker{remote, access.Debug, sigStub}, d.auth, nil
}
}
nodeACLs, _, err := d.getACL()
@@ -418,7 +418,7 @@
return receiver, appSpecificAuthorizer, nil
case configSuffix:
if len(components) != 2 {
- return nil, nil, errInvalidSuffix
+ return nil, nil, verror2.Make(ErrInvalidSuffix, nil)
}
receiver := inode.ConfigServer(&configService{
callback: d.internal.callback,
@@ -433,7 +433,7 @@
// (and not other apps).
return receiver, nil, nil
default:
- return nil, nil, errInvalidSuffix
+ return nil, nil, verror2.Make(ErrInvalidSuffix, nil)
}
}
@@ -445,23 +445,27 @@
return sec, nil
}
// Otherwise, we require a per-installation and per-instance ACL file.
-
if len(suffix) == 2 {
p, err := installationDirCore(suffix, config.Root)
if err != nil {
vlog.Errorf("newAppSpecificAuthorizer failed: %v", err)
return nil, err
}
- p = path.Join(p, "acls", "data")
- return vsecurity.NewFileACLAuthorizer(p), nil
- } else if len(suffix) > 2 {
+ return access.TaggedACLAuthorizerFromFile(path.Join(p, "acls", "data"), access.TypicalTagType())
+ }
+ if len(suffix) > 2 {
p, err := instanceDir(config.Root, suffix[0:3])
if err != nil {
vlog.Errorf("newAppSpecificAuthorizer failed: %v", err)
return nil, err
}
- p = path.Join(p, "acls", "data")
- return vsecurity.NewFileACLAuthorizer(p), nil
+ return access.TaggedACLAuthorizerFromFile(path.Join(p, "acls", "data"), access.TypicalTagType())
}
- return nil, errInvalidSuffix
+ return nil, verror2.Make(ErrInvalidSuffix, nil)
}
+
+// allowEveryone implements the authorization policy that allows all principals
+// access.
+type allowEveryone struct{}
+
+func (allowEveryone) Authorize(security.Context) error { return nil }
diff --git a/services/mgmt/node/impl/impl_test.go b/services/mgmt/node/impl/impl_test.go
index 0baca5b..103b2d4 100644
--- a/services/mgmt/node/impl/impl_test.go
+++ b/services/mgmt/node/impl/impl_test.go
@@ -10,7 +10,6 @@
"encoding/base64"
"encoding/hex"
"encoding/json"
- "flag"
"fmt"
"io"
"io/ioutil"
@@ -38,16 +37,16 @@
"veyron.io/veyron/veyron2/services/mgmt/node"
"veyron.io/veyron/veyron2/services/mgmt/pprof"
"veyron.io/veyron/veyron2/services/mgmt/stats"
+ "veyron.io/veyron/veyron2/services/security/access"
+ "veyron.io/veyron/veyron2/vdl/vdlutil"
"veyron.io/veyron/veyron2/verror"
"veyron.io/veyron/veyron2/vlog"
- "veyron.io/veyron/veyron2/vom"
"veyron.io/veyron/veyron/lib/expect"
"veyron.io/veyron/veyron/lib/modules"
"veyron.io/veyron/veyron/lib/signals"
"veyron.io/veyron/veyron/lib/testutil"
tsecurity "veyron.io/veyron/veyron/lib/testutil/security"
- vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron/services/mgmt/node/config"
"veyron.io/veyron/veyron/services/mgmt/node/impl"
suidhelper "veyron.io/veyron/veyron/services/mgmt/suidhelper/impl"
@@ -62,7 +61,7 @@
func init() {
// TODO(rthellend): Remove when vom2 is ready.
- vom.Register(&naming.VDLMountedServer{})
+ vdlutil.Register(&naming.VDLMountedServer{})
modules.RegisterChild(execScriptCmd, "", execScript)
modules.RegisterChild(nodeManagerCmd, "", nodeManager)
@@ -74,17 +73,6 @@
return
}
initRT()
-
- // NOTE(caprita): The default 1m timeout that the modules package gives
- // to subprocesses is insufficient, especially with --race.
- // If not explicitly set to a non-default value by the user, we set the
- // test-wide timeout to 2 minutes (which will get propagated by the
- // modules framework to the subprocesses).
- timeoutFlag := flag.Lookup("test.timeout")
- if timeoutFlag.Value.String() == timeoutFlag.DefValue {
- timeoutFlag.Value.Set("2m")
- vlog.Infof("Setting test.timeout to 2m.")
- }
}
func initRT() {
@@ -185,7 +173,7 @@
fmt.Fprintf(stdout, "ready:%d\n", os.Getpid())
- <-signals.ShutdownOnSignals()
+ <-signals.ShutdownOnSignals(rt.R())
if val, present := env["PAUSE_BEFORE_STOP"]; present && val == "1" {
modules.WaitForEOF(stdin)
@@ -247,9 +235,12 @@
if err := server.Serve(publishName, new(appService), nil); err != nil {
vlog.Fatalf("Serve(%v) failed: %v", publishName, err)
}
+ // Some of our tests look for log files, so make sure they are flushed
+ // to ensure that at least the files exist.
+ vlog.FlushLog()
ping()
- <-signals.ShutdownOnSignals()
+ <-signals.ShutdownOnSignals(rt.R())
if err := ioutil.WriteFile("testfile", []byte("goodbye world"), 0600); err != nil {
vlog.Fatalf("Failed to write testfile: %v", err)
}
@@ -350,8 +341,8 @@
// Simulate an invalid envelope in the application repository.
*envelope = envelopeFromShell(sh, nmPauseBeforeStopEnv, nodeManagerCmd, "bogus", nmArgs...)
- updateNodeExpectError(t, "factoryNM", verror.BadArg) // Incorrect title.
- revertNodeExpectError(t, "factoryNM", verror.NoExist) // No previous version available.
+ updateNodeExpectError(t, "factoryNM", impl.ErrAppTitleMismatch.ID)
+ revertNodeExpectError(t, "factoryNM", impl.ErrUpdateNoOp.ID)
// Set up a second version of the node manager. The information in the
// envelope will be used by the node manager to stage the next version.
@@ -373,7 +364,7 @@
t.Fatalf("current link didn't change")
}
- updateNodeExpectError(t, "factoryNM", verror.Exists) // Update already in progress.
+ updateNodeExpectError(t, "factoryNM", impl.ErrOperationInProgress.ID)
nmh.CloseStdin()
@@ -391,7 +382,7 @@
// Try issuing an update without changing the envelope in the application
// repository: this should fail, and current link should be unchanged.
- updateNodeExpectError(t, "v2NM", naming.ErrNoSuchName.ID)
+ updateNodeExpectError(t, "v2NM", impl.ErrUpdateNoOp.ID)
if evalLink() != scriptPathV2 {
t.Fatalf("script changed")
}
@@ -424,7 +415,7 @@
// Revert the node manager to its previous version (v2).
revertNode(t, "v3NM")
- revertNodeExpectError(t, "v3NM", verror.Exists) // Revert already in progress.
+ revertNodeExpectError(t, "v3NM", impl.ErrOperationInProgress.ID) // Revert already in progress.
nmh.CloseStdin()
nms.Expect("v3NM terminating")
if evalLink() != scriptPathV2 {
@@ -457,6 +448,9 @@
type pingServer chan<- string
+// TODO(caprita): Set the timeout in a more principled manner.
+const pingTimeout = 20 * time.Second
+
func (p pingServer) Ping(_ ipc.ServerContext, arg string) {
p <- arg
}
@@ -508,7 +502,7 @@
var env string
select {
case env = <-pingCh:
- case <-time.After(time.Minute):
+ case <-time.After(pingTimeout):
t.Fatalf("%s: failed to get ping", loc(1))
}
d := json.NewDecoder(strings.NewReader(env))
@@ -560,8 +554,8 @@
appID := installApp(t)
// Start requires the caller to grant a blessing for the app instance.
- if _, err := startAppImpl(t, appID, ""); err == nil || !verror.Is(err, verror.BadArg) {
- t.Fatalf("Start(%v) expected to fail with %v, got %v instead", appID, verror.BadArg, err)
+ if _, err := startAppImpl(t, appID, ""); err == nil || !verror.Is(err, impl.ErrInvalidBlessing.ID) {
+ t.Fatalf("Start(%v) expected to fail with %v, got %v instead", appID, impl.ErrInvalidBlessing.ID, err)
}
// Start an instance of the app.
@@ -612,12 +606,12 @@
}
// Updating the installation to itself is a no-op.
- updateAppExpectError(t, appID, naming.ErrNoSuchName.ID)
+ updateAppExpectError(t, appID, impl.ErrUpdateNoOp.ID)
// Updating the installation should not work with a mismatched title.
*envelope = envelopeFromShell(sh, nil, appCmd, "bogus")
- updateAppExpectError(t, appID, verror.BadArg)
+ updateAppExpectError(t, appID, impl.ErrAppTitleMismatch.ID)
// Create a second version of the app and update the app to it.
*envelope = envelopeFromShell(sh, nil, appCmd, "google naps", "appV2")
@@ -679,19 +673,19 @@
resolveExpectNotFound(t, "appV1")
// We are already on the first version, no further revert possible.
- revertAppExpectError(t, appID, naming.ErrNoSuchName.ID)
+ revertAppExpectError(t, appID, impl.ErrUpdateNoOp.ID)
// Uninstall the app.
uninstallApp(t, appID)
// Updating the installation should no longer be allowed.
- updateAppExpectError(t, appID, verror.BadArg)
+ updateAppExpectError(t, appID, impl.ErrInvalidOperation.ID)
// Reverting the installation should no longer be allowed.
- revertAppExpectError(t, appID, verror.BadArg)
+ revertAppExpectError(t, appID, impl.ErrInvalidOperation.ID)
// Starting new instances should no longer be allowed.
- startAppExpectError(t, appID, verror.BadArg)
+ startAppExpectError(t, appID, impl.ErrInvalidOperation.ID)
// Cleanly shut down the node manager.
syscall.Kill(nmh.Pid(), syscall.SIGINT)
@@ -776,7 +770,7 @@
// Wait until the app pings us that it's ready.
select {
case <-pingCh:
- case <-time.After(5 * time.Second):
+ case <-time.After(pingTimeout):
t.Fatalf("failed to get ping")
}
resolve(t, "trapp", 1)
@@ -839,10 +833,13 @@
if err := nodeStub.Claim(selfRT.NewContext(), &granter{p: selfRT.Principal(), extension: "mydevice"}); err != nil {
t.Fatal(err)
}
- expectedACL := security.ACL{In: map[security.BlessingPattern]security.LabelSet{"root/self/mydevice": security.AllLabels}}
+ expectedACL := make(access.TaggedACLMap)
+ for _, tag := range access.AllTypicalTags() {
+ expectedACL[string(tag)] = access.ACL{In: []security.BlessingPattern{"root/self/mydevice"}}
+ }
var b bytes.Buffer
- if err := vsecurity.SaveACL(&b, expectedACL); err != nil {
- t.Fatalf("Failed to saveACL:%v", err)
+ if err := expectedACL.WriteTo(&b); err != nil {
+ t.Fatalf("Failed to save ACL:%v", err)
}
md5hash := md5.Sum(b.Bytes())
expectedETAG := hex.EncodeToString(md5hash[:])
@@ -856,7 +853,10 @@
if err := tryInstall(otherRT); err == nil {
t.Fatalf("Install should have failed with random identity")
}
- newACL := security.ACL{In: map[security.BlessingPattern]security.LabelSet{"root/other": security.AllLabels}}
+ newACL := make(access.TaggedACLMap)
+ for _, tag := range access.AllTypicalTags() {
+ newACL.Add("root/other", string(tag))
+ }
if err := nodeStub.SetACL(selfRT.NewContext(), newACL, "invalid"); err == nil {
t.Fatalf("SetACL should have failed with invalid etag")
}
@@ -921,7 +921,7 @@
// sent to their children and so on.
pid := readPID(t, nms)
resolve(t, "nm", 1)
- revertNodeExpectError(t, "nm", naming.ErrNoSuchName.ID) // No previous version available.
+ revertNodeExpectError(t, "nm", impl.ErrUpdateNoOp.ID) // No previous version available.
syscall.Kill(pid, syscall.SIGINT)
nms.Expect("nm terminating")
@@ -969,7 +969,7 @@
// Wait until the app pings us that it's ready.
select {
case <-pingCh:
- case <-time.After(5 * time.Second):
+ case <-time.After(pingTimeout):
t.Fatalf("failed to get ping")
}
@@ -1013,7 +1013,7 @@
logFileRemoveErrorFatalWarningRE := regexp.MustCompile("(ERROR|FATAL|WARNING)")
statsTrimRE := regexp.MustCompile("/stats/(ipc|system(/start-time.*)?)$")
for _, tc := range testcases {
- results, err := testutil.GlobName(tc.name, tc.pattern)
+ results, err := testutil.GlobName(rt.R().NewContext(), tc.name, tc.pattern)
if err != nil {
t.Errorf("unexpected glob error for (%q, %q): %v", tc.name, tc.pattern, err)
continue
@@ -1041,7 +1041,7 @@
}
// Call Size() on the log file objects.
- files, err := testutil.GlobName("nm", "apps/google naps/"+installID+"/"+instance1ID+"/logs/*")
+ files, err := testutil.GlobName(rt.R().NewContext(), "nm", "apps/google naps/"+installID+"/"+instance1ID+"/logs/*")
if err != nil {
t.Errorf("unexpected glob error: %v", err)
}
@@ -1057,7 +1057,7 @@
}
// Call Value() on some of the stats objects.
- objects, err := testutil.GlobName("nm", "apps/google naps/"+installID+"/"+instance1ID+"/stats/system/start-time*")
+ objects, err := testutil.GlobName(rt.R().NewContext(), "nm", "apps/google naps/"+installID+"/"+instance1ID+"/stats/system/start-time*")
if err != nil {
t.Errorf("unexpected glob error: %v", err)
}
@@ -1288,7 +1288,7 @@
if err != nil {
t.Fatalf("GetACL failed %v", err)
}
- newACL.In["root/other/..."] = security.LabelSet(security.WriteLabel)
+ newACL.Add("root/other", string(access.Write))
if err := nodeStub.SetACL(selfRT.NewContext(), newACL, ""); err != nil {
t.Fatalf("SetACL failed %v", err)
}
@@ -1305,7 +1305,7 @@
if err != nil {
t.Fatalf("GetACL on appID: %v failed %v", appID, err)
}
- newACL.In["root/other/..."] = security.LabelSet(security.ReadLabel)
+ newACL.Add("root/other", string(access.Read))
if err = appStub(appID).SetACL(selfRT.NewContext(), newACL, ""); err != nil {
t.Fatalf("SetACL on appID: %v failed: %v", appID, err)
}
diff --git a/services/mgmt/node/impl/mock_repo_test.go b/services/mgmt/node/impl/mock_repo_test.go
index 3901275..d55e2c9 100644
--- a/services/mgmt/node/impl/mock_repo_test.go
+++ b/services/mgmt/node/impl/mock_repo_test.go
@@ -3,7 +3,6 @@
import (
"crypto/md5"
"encoding/hex"
- "errors"
"io"
"io/ioutil"
"os"
@@ -13,6 +12,7 @@
"veyron.io/veyron/veyron2/services/mgmt/application"
"veyron.io/veyron/veyron2/services/mgmt/binary"
"veyron.io/veyron/veyron2/services/mgmt/repository"
+ "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
)
@@ -80,7 +80,12 @@
// BINARY REPOSITORY INTERFACE IMPLEMENTATION
-func (*brInvoker) Create(ipc.ServerContext, int32) error {
+// TODO(toddw): Move the errors from dispatcher.go into a common location.
+const pkgPath = "veyron.io/veyron/veyron/services/mgmt/node/impl"
+
+var ErrOperationFailed = verror2.Register(pkgPath+".OperationFailed", verror2.NoRetry, "")
+
+func (*brInvoker) Create(ipc.ServerContext, int32, repository.MediaInfo) error {
vlog.VI(1).Infof("Create()")
return nil
}
@@ -90,14 +95,12 @@
return nil
}
-var errOperationFailed = errors.New("operation failed")
-
func (i *brInvoker) Download(ctx repository.BinaryDownloadContext, _ int32) error {
vlog.VI(1).Infof("Download()")
file, err := os.Open(os.Args[0])
if err != nil {
vlog.Errorf("Open() failed: %v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, ctx)
}
defer file.Close()
bufferLength := 4096
@@ -111,11 +114,11 @@
case nil:
if err := sender.Send(buffer[:n]); err != nil {
vlog.Errorf("Send() failed: %v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, ctx)
}
default:
vlog.Errorf("Read() failed: %v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, ctx)
}
}
}
@@ -125,16 +128,16 @@
return "", 0, nil
}
-func (*brInvoker) Stat(ipc.ServerContext) ([]binary.PartInfo, error) {
+func (*brInvoker) Stat(ctx ipc.ServerContext) ([]binary.PartInfo, repository.MediaInfo, error) {
vlog.VI(1).Infof("Stat()")
h := md5.New()
bytes, err := ioutil.ReadFile(os.Args[0])
if err != nil {
- return []binary.PartInfo{}, errOperationFailed
+ return []binary.PartInfo{}, repository.MediaInfo{}, verror2.Make(ErrOperationFailed, ctx)
}
h.Write(bytes)
part := binary.PartInfo{Checksum: hex.EncodeToString(h.Sum(nil)), Size: int64(len(bytes))}
- return []binary.PartInfo{part}, nil
+ return []binary.PartInfo{part}, repository.MediaInfo{Type: "application/octet-stream"}, nil
}
func (i *brInvoker) Upload(repository.BinaryUploadContext, int32) error {
diff --git a/services/mgmt/node/impl/node_service.go b/services/mgmt/node/impl/node_service.go
index 773d4d9..e99b5b3 100644
--- a/services/mgmt/node/impl/node_service.go
+++ b/services/mgmt/node/impl/node_service.go
@@ -43,10 +43,11 @@
"veyron.io/veyron/veyron2/mgmt"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/rt"
- "veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/services/mgmt/application"
"veyron.io/veyron/veyron2/services/mgmt/binary"
"veyron.io/veyron/veyron2/services/mgmt/node"
+ "veyron.io/veyron/veyron2/services/security/access"
+ "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
vexec "veyron.io/veyron/veyron/lib/exec"
@@ -97,7 +98,7 @@
// Get the blessing to be used by the claimant.
blessings := call.Blessings()
if blessings == nil {
- return errInvalidBlessing
+ return verror2.Make(ErrInvalidBlessing, call)
}
return i.disp.claimNodeManager(blessings.ForContext(call), blessings)
}
@@ -220,55 +221,50 @@
// Start the child process.
if err := handle.Start(); err != nil {
vlog.Errorf("Start() failed: %v", err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, ctx)
}
defer func() {
- // Ensure that any stdout output gets flushed.
- if err := handle.Wait(10 * time.Second); err != nil {
- vlog.Errorf("Wait() failed: %v", err)
- if err := handle.Clean(); err != nil {
- vlog.Errorf("Clean() failed: %v", err)
- }
+ if err := handle.Clean(); err != nil {
+ vlog.Errorf("Clean() failed: %v", err)
}
}()
// Wait for the child process to start.
- testTimeout := 10 * time.Second
- if err := handle.WaitForReady(testTimeout); err != nil {
- vlog.Errorf("WaitForReady(%v) failed: %v", testTimeout, err)
- return errOperationFailed
+ if err := handle.WaitForReady(childReadyTimeout); err != nil {
+ vlog.Errorf("WaitForReady(%v) failed: %v", childReadyTimeout, err)
+ return verror2.Make(ErrOperationFailed, ctx)
}
- childName, err := listener.waitForValue(testTimeout)
+ childName, err := listener.waitForValue(childReadyTimeout)
if err != nil {
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, ctx)
}
// Check that invoking Update() succeeds.
childName = naming.Join(childName, "nm")
nmClient := node.NodeClient(childName)
linkOld, pathOld, err := i.getCurrentFileInfo()
if err != nil {
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, ctx)
}
// Since the resolution of mtime for files is seconds, the test sleeps
// for a second to make sure it can check whether the current symlink is
// updated.
time.Sleep(time.Second)
if err := nmClient.Revert(ctx); err != nil {
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, ctx)
}
linkNew, pathNew, err := i.getCurrentFileInfo()
if err != nil {
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, ctx)
}
// Check that the new node manager updated the current symbolic link.
if !linkOld.ModTime().Before(linkNew.ModTime()) {
vlog.Errorf("new node manager test failed")
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, ctx)
}
// Ensure that the current symbolic link points to the same script.
if pathNew != pathOld {
updateLink(pathOld, i.config.CurrentLink)
vlog.Errorf("new node manager test failed")
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, ctx)
}
return nil
}
@@ -281,7 +277,7 @@
path, err := filepath.EvalSymlinks(os.Args[0])
if err != nil {
vlog.Errorf("EvalSymlinks(%v) failed: %v", os.Args[0], err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
output := "#!/bin/bash\n"
@@ -297,31 +293,31 @@
path = filepath.Join(workspace, "noded.sh")
if err := ioutil.WriteFile(path, []byte(output), 0700); err != nil {
vlog.Errorf("WriteFile(%v) failed: %v", path, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
return nil
}
func (i *nodeService) updateNodeManager(ctx context.T) error {
if len(i.config.Origin) == 0 {
- return errUpdateNoOp
+ return verror2.Make(ErrUpdateNoOp, ctx)
}
envelope, err := fetchEnvelope(ctx, i.config.Origin)
if err != nil {
return err
}
if envelope.Title != application.NodeManagerTitle {
- return errIncompatibleUpdate
+ return verror2.Make(ErrAppTitleMismatch, ctx)
}
if i.config.Envelope != nil && reflect.DeepEqual(envelope, i.config.Envelope) {
- return errUpdateNoOp
+ return verror2.Make(ErrUpdateNoOp, ctx)
}
// Create new workspace.
workspace := filepath.Join(i.config.Root, "node-manager", generateVersionDirName())
perm := os.FileMode(0700)
if err := os.MkdirAll(workspace, perm); err != nil {
vlog.Errorf("MkdirAll(%v, %v) failed: %v", workspace, perm, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, ctx)
}
deferrer := func() {
@@ -350,7 +346,7 @@
// Populate the new workspace with a node manager script.
configSettings, err := i.config.Save(envelope)
if err != nil {
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, ctx)
}
if err := generateScript(workspace, configSettings, envelope); err != nil {
@@ -370,8 +366,8 @@
return nil
}
-func (*nodeService) Install(ipc.ServerContext, string) (string, error) {
- return "", errInvalidSuffix
+func (*nodeService) Install(ctx ipc.ServerContext, _ string) (string, error) {
+ return "", verror2.Make(ErrInvalidSuffix, ctx)
}
func (*nodeService) Refresh(ipc.ServerContext) error {
@@ -384,17 +380,17 @@
return nil
}
-func (*nodeService) Resume(ipc.ServerContext) error {
- return errInvalidSuffix
+func (*nodeService) Resume(ctx ipc.ServerContext) error {
+ return verror2.Make(ErrInvalidSuffix, ctx)
}
func (i *nodeService) Revert(call ipc.ServerContext) error {
if i.config.Previous == "" {
- return errUpdateNoOp
+ return verror2.Make(ErrUpdateNoOp, call)
}
updatingState := i.updating
if updatingState.testAndSetUpdating() {
- return errInProgress
+ return verror2.Make(ErrOperationInProgress, call)
}
err := i.revertNodeManager()
if err != nil {
@@ -403,12 +399,12 @@
return err
}
-func (*nodeService) Start(ipc.ServerContext) ([]string, error) {
- return nil, errInvalidSuffix
+func (*nodeService) Start(ctx ipc.ServerContext) ([]string, error) {
+ return nil, verror2.Make(ErrInvalidSuffix, ctx)
}
-func (*nodeService) Stop(ipc.ServerContext, uint32) error {
- return errInvalidSuffix
+func (*nodeService) Stop(ctx ipc.ServerContext, _ uint32) error {
+ return verror2.Make(ErrInvalidSuffix, ctx)
}
func (*nodeService) Suspend(ipc.ServerContext) error {
@@ -416,17 +412,17 @@
return nil
}
-func (*nodeService) Uninstall(ipc.ServerContext) error {
- return errInvalidSuffix
+func (*nodeService) Uninstall(ctx ipc.ServerContext) error {
+ return verror2.Make(ErrInvalidSuffix, ctx)
}
-func (i *nodeService) Update(ipc.ServerContext) error {
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+func (i *nodeService) Update(call ipc.ServerContext) error {
+ ctx, cancel := rt.R().NewContext().WithTimeout(ipcContextTimeout)
defer cancel()
updatingState := i.updating
if updatingState.testAndSetUpdating() {
- return errInProgress
+ return verror2.Make(ErrOperationInProgress, call)
}
err := i.updateNodeManager(ctx)
@@ -441,21 +437,21 @@
return nil
}
-func (i *nodeService) SetACL(_ ipc.ServerContext, acl security.ACL, etag string) error {
+func (i *nodeService) SetACL(_ ipc.ServerContext, acl access.TaggedACLMap, etag string) error {
return i.disp.setACL(acl, etag, true /* store ACL on disk */)
}
-func (i *nodeService) GetACL(_ ipc.ServerContext) (acl security.ACL, etag string, err error) {
+func (i *nodeService) GetACL(_ ipc.ServerContext) (acl access.TaggedACLMap, etag string, err error) {
return i.disp.getACL()
}
-func sameMachineCheck(call ipc.ServerContext) error {
- switch local, err := netstate.SameMachine(call.RemoteEndpoint().Addr()); {
+func sameMachineCheck(ctx ipc.ServerContext) error {
+ switch local, err := netstate.SameMachine(ctx.RemoteEndpoint().Addr()); {
case err != nil:
return err
case local == false:
vlog.Errorf("SameMachine() indicates that endpoint is not on the same node")
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, ctx)
}
return nil
}
diff --git a/services/mgmt/node/impl/proxy_invoker.go b/services/mgmt/node/impl/proxy_invoker.go
index 1617dac..1f8cf98 100644
--- a/services/mgmt/node/impl/proxy_invoker.go
+++ b/services/mgmt/node/impl/proxy_invoker.go
@@ -6,7 +6,7 @@
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/rt"
- "veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/services/security/access"
)
// proxyInvoker is an ipc.Invoker implementation that proxies all requests
@@ -14,11 +14,11 @@
// <remote> transparently.
//
// remote is the name of the remote object.
-// label is the security label required to access this object.
+// access is the access tag require to access the object.
// sigStub is used to get the signature of the remote object.
type proxyInvoker struct {
remote string
- label security.Label
+ access access.Tag
sigStub signatureStub
}
@@ -34,7 +34,7 @@
var x interface{}
argptrs[i] = &x
}
- tags = []interface{}{p.label}
+ tags = []interface{}{p.access}
return
}
@@ -185,7 +185,7 @@
}
func (p *proxyInvoker) VGlob() *ipc.GlobState {
- return &ipc.GlobState{VAllGlobber: p}
+ return &ipc.GlobState{AllGlobber: p}
}
func (p *proxyInvoker) Glob(ctx *ipc.GlobContextStub, pattern string) error {
diff --git a/services/mgmt/node/impl/proxy_invoker_test.go b/services/mgmt/node/impl/proxy_invoker_test.go
index 0fcb836..b729d10 100644
--- a/services/mgmt/node/impl/proxy_invoker_test.go
+++ b/services/mgmt/node/impl/proxy_invoker_test.go
@@ -11,6 +11,7 @@
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/services/mgmt/stats"
"veyron.io/veyron/veyron2/services/mounttable"
+ "veyron.io/veyron/veyron2/services/security/access"
)
// TODO(toddw): Add tests of Signature and MethodSignature.
@@ -45,7 +46,6 @@
}
disp := &proxyDispatcher{
naming.JoinAddressName(ep1.String(), "__debug/stats"),
- security.Label(security.AllLabels),
stats.StatsServer(nil),
}
if err := server2.ServeDispatcher("", disp); err != nil {
@@ -97,10 +97,9 @@
type proxyDispatcher struct {
remote string
- label security.Label
sigStub signatureStub
}
func (d *proxyDispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
- return &proxyInvoker{naming.Join(d.remote, suffix), d.label, d.sigStub}, nil, nil
+ return &proxyInvoker{naming.Join(d.remote, suffix), access.Debug, d.sigStub}, nil, nil
}
diff --git a/services/mgmt/node/impl/util.go b/services/mgmt/node/impl/util.go
index 29ca3da..f700552 100644
--- a/services/mgmt/node/impl/util.go
+++ b/services/mgmt/node/impl/util.go
@@ -10,21 +10,29 @@
"veyron.io/veyron/veyron/services/mgmt/lib/binary"
"veyron.io/veyron/veyron2/context"
+ "veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/services/mgmt/application"
"veyron.io/veyron/veyron2/services/mgmt/repository"
+ "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
)
+// TODO(caprita): Set these timeout in a more principled manner.
+const (
+ childReadyTimeout = 20 * time.Second
+ ipcContextTimeout = time.Minute
+)
+
func downloadBinary(workspace, fileName, name string) error {
- data, err := binary.Download(name)
+ data, _, err := binary.Download(rt.R().NewContext(), name)
if err != nil {
vlog.Errorf("Download(%v) failed: %v", name, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
path, perm := filepath.Join(workspace, fileName), os.FileMode(755)
if err := ioutil.WriteFile(path, data, perm); err != nil {
vlog.Errorf("WriteFile(%v, %v) failed: %v", path, perm, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
return nil
}
@@ -37,7 +45,7 @@
envelope, err := stub.Match(ctx, profiles)
if err != nil {
vlog.Errorf("Match(%v) failed: %v", profiles, err)
- return nil, errOperationFailed
+ return nil, verror2.Make(ErrOperationFailed, ctx)
}
return &envelope, nil
}
@@ -48,7 +56,7 @@
self := os.Args[0]
if err := os.Link(self, path); err != nil {
vlog.Errorf("Link(%v, %v) failed: %v", self, path, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
return nil
}
@@ -63,16 +71,16 @@
if err == nil {
if err := os.Remove(fi.Name()); err != nil {
vlog.Errorf("Remove(%v) failed: %v", fi.Name(), err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
}
if err := os.Symlink(target, newLink); err != nil {
vlog.Errorf("Symlink(%v, %v) failed: %v", target, newLink, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
if err := os.Rename(newLink, link); err != nil {
vlog.Errorf("Rename(%v, %v) failed: %v", newLink, link, err)
- return errOperationFailed
+ return verror2.Make(ErrOperationFailed, nil)
}
return nil
}
diff --git a/services/mgmt/node/impl/util_test.go b/services/mgmt/node/impl/util_test.go
index 0db0633..0d5acf2 100644
--- a/services/mgmt/node/impl/util_test.go
+++ b/services/mgmt/node/impl/util_test.go
@@ -19,6 +19,7 @@
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/services/mgmt/node"
"veyron.io/veyron/veyron2/verror"
+ "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
"veyron.io/veyron/veyron/lib/expect"
@@ -31,27 +32,33 @@
"veyron.io/veyron/veyron2/services/mgmt/application"
)
-// Setting this environment variable to any non-empty value avoids removing the
-// node manager's workspace for successful test runs (for failed test runs, this
-// is already the case). This is useful when developing test cases.
-const preserveNMWorkspaceEnv = "VEYRON_TEST_PRESERVE_NM_WORKSPACE"
+const (
+ // Setting this environment variable to any non-empty value avoids
+ // removing the node manager's workspace for successful test runs (for
+ // failed test runs, this is already the case). This is useful when
+ // developing test cases.
+ preserveNMWorkspaceEnv = "VEYRON_TEST_PRESERVE_NM_WORKSPACE"
+
+ // TODO(caprita): Set the timeout in a more principled manner.
+ expectTimeout = 20 * time.Second
+)
func loc(d int) string {
_, file, line, _ := runtime.Caller(d + 1)
return fmt.Sprintf("%s:%d", filepath.Base(file), line)
}
-func startRootMT(t *testing.T, sh *modules.Shell) (string, modules.Handle, *expect.Session) {
+func startRootMT(t *testing.T, sh *modules.Shell) (string, modules.Handle) {
h, err := sh.Start(core.RootMTCommand, nil, "--", "--veyron.tcp.address=127.0.0.1:0")
if err != nil {
t.Fatalf("failed to start root mount table: %s", err)
}
- s := expect.NewSession(t, h.Stdout(), time.Minute)
+ s := expect.NewSession(t, h.Stdout(), expectTimeout)
rootName := s.ExpectVar("MT_NAME")
if t.Failed() {
t.Fatalf("failed to read mt name: %s", s.Error())
}
- return rootName, h, s
+ return rootName, h
}
func credentialsForChild(blessing string) (string, []string) {
@@ -59,28 +66,56 @@
return creds, []string{consts.VeyronCredentials + "=" + creds}
}
+// setNSRoots sets the roots for the local runtime's namespace using the modules
+// function SetNamesaceRootCommand.
+func setNSRoots(t *testing.T, sh *modules.Shell, roots ...string) {
+ setRootsHandle, err := sh.Start(core.SetNamespaceRootsCommand, nil, roots...)
+ if err != nil {
+ t.Fatalf("%s: unexpected error: %s", loc(2), err)
+ }
+ if err := setRootsHandle.Shutdown(nil, os.Stderr); err != nil {
+ t.Fatalf("%s: SetNamespaceRootsCommand failed with %v", loc(2), err)
+ }
+}
+
func createShellAndMountTable(t *testing.T) (*modules.Shell, func()) {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
// The shell, will, by default share credentials with its children.
sh.ClearVar(consts.VeyronCredentials)
- mtName, mtHandle, _ := startRootMT(t, sh)
+ mtName, mtHandle := startRootMT(t, sh)
+ vlog.VI(1).Infof("Started shell mounttable with name %v", mtName)
// Make sure the root mount table is the last process to be shutdown
// since the others will likely want to communicate with it during
// their shutdown process
sh.Forget(mtHandle)
+ // TODO(caprita): Define a GetNamespaceRootsCommand in modules/core and
+ // use that?
+ oldNamespaceRoots := rt.R().Namespace().Roots()
fn := func() {
vlog.VI(1).Info("------------ CLEANUP ------------")
vlog.VI(1).Info("---------------------------------")
- sh.Cleanup(nil, os.Stderr)
- mtHandle.Shutdown(nil, os.Stderr)
- sh.Start(core.SetNamespaceRootsCommand, nil, "")
+ vlog.VI(1).Info("--(cleaning up shell)------------")
+ if err := sh.Cleanup(os.Stdout, os.Stderr); err != nil {
+ t.Fatalf("sh.Cleanup failed with %v", err)
+ }
+ vlog.VI(1).Info("--(done cleaning up shell)-------")
+ vlog.VI(1).Info("--(shutting down root mt)--------")
+ if err := mtHandle.Shutdown(os.Stdout, os.Stderr); err != nil {
+ t.Fatalf("mtHandle.Shutdown failed with %v", err)
+ }
+ vlog.VI(1).Info("--(done shutting down root mt)---")
+ vlog.VI(1).Info("--------- DONE CLEANUP ----------")
+ // Calling sh.Start after sh.Cleanup is not a good idea.
+ // TODO(caprita): set the roots by hand and avoid running the
+ // corresponding modules command.
+ setNSRoots(t, sh, oldNamespaceRoots...)
}
-
- if _, err := sh.Start(core.SetNamespaceRootsCommand, nil, mtName); err != nil {
- t.Fatalf("%s: unexpected error: %s", loc(1), err)
- }
+ setNSRoots(t, sh, mtName)
sh.SetVar(consts.NamespaceRootPrefix, mtName)
return sh, fn
}
@@ -91,7 +126,7 @@
t.Fatalf("%s: failed to start %q: %s", loc(1), cmd, err)
return nil, nil
}
- s := expect.NewSession(t, h.Stdout(), 10*time.Second)
+ s := expect.NewSession(t, h.Stdout(), expectTimeout)
s.SetVerbosity(testing.Verbose())
return h, s
}
@@ -137,7 +172,7 @@
vlog.Fatalf("NewServer() failed: %v", err)
}
spec := static.ListenSpec
- spec.Address = "127.0.0.1:0"
+ spec.Address = "127.0.0.1:0" // Isn't this the default?
endpoint, err := server.Listen(spec)
if err != nil {
vlog.Fatalf("Listen(%s) failed: %v", static.ListenSpec, err)
@@ -149,7 +184,7 @@
func resolveExpectNotFound(t *testing.T, name string) {
if results, err := rt.R().Namespace().Resolve(rt.R().NewContext(), name); err == nil {
t.Fatalf("Resolve(%v) succeeded with results %v when it was expected to fail", name, results)
- } else if expectErr := naming.ErrNoSuchName.ID; !verror.Is(err, expectErr) {
+ } else if expectErr := naming.ErrNoSuchName.ID; !verror2.Is(err, expectErr) {
t.Fatalf("Resolve(%v) failed with error %v, expected error ID %v", name, err, expectErr)
}
}
@@ -183,11 +218,7 @@
}
func updateNodeExpectError(t *testing.T, name string, errID verror.ID) {
- if err := nodeStub(name).Update(rt.R().NewContext()); !verror.Is(err, errID) {
- if errID == naming.ErrNoSuchName.ID && err.Error() == "no different version available" {
- // TODO(bprosnitz) Remove this check when errUpdateNoOp is updated to verror2
- return
- }
+ if err := nodeStub(name).Update(rt.R().NewContext()); !verror2.Is(err, errID) {
t.Fatalf("%s: Update(%v) expected to fail with %v, got %v instead", loc(1), name, errID, err)
}
}
@@ -199,11 +230,7 @@
}
func revertNodeExpectError(t *testing.T, name string, errID verror.ID) {
- if err := nodeStub(name).Revert(rt.R().NewContext()); !verror.Is(err, errID) {
- if errID == naming.ErrNoSuchName.ID && err.Error() == "no different version available" {
- // TODO(bprosnitz) Remove this check when errUpdateNoOp is updated to verror2
- return
- }
+ if err := nodeStub(name).Revert(rt.R().NewContext()); !verror2.Is(err, errID) {
t.Fatalf("%s: Revert(%v) expected to fail with %v, got %v instead", loc(1), name, errID, err)
}
}
@@ -273,7 +300,7 @@
}
func startAppExpectError(t *testing.T, appID string, expectedError verror.ID, opt ...veyron2.Runtime) {
- if _, err := startAppImpl(t, appID, "forapp", opt...); err == nil || !verror.Is(err, expectedError) {
+ if _, err := startAppImpl(t, appID, "forapp", opt...); err == nil || !verror2.Is(err, expectedError) {
t.Fatalf("%s: Start(%v) expected to fail with %v, got %v instead", loc(1), appID, expectedError, err)
}
}
@@ -297,7 +324,7 @@
}
func resumeAppExpectError(t *testing.T, appID, instanceID string, expectedError verror.ID, opt ...veyron2.Runtime) {
- if err := appStub(appID, instanceID).Resume(ort(opt).NewContext()); err == nil || !verror.Is(err, expectedError) {
+ if err := appStub(appID, instanceID).Resume(ort(opt).NewContext()); err == nil || !verror2.Is(err, expectedError) {
t.Fatalf("%s: Resume(%v/%v) expected to fail with %v, got %v instead", loc(1), appID, instanceID, expectedError, err)
}
}
@@ -309,11 +336,7 @@
}
func updateAppExpectError(t *testing.T, appID string, expectedError verror.ID) {
- if err := appStub(appID).Update(rt.R().NewContext()); err == nil || !verror.Is(err, expectedError) {
- if expectedError == naming.ErrNoSuchName.ID && err.Error() == "no different version available" {
- // TODO(bprosnitz) Remove this check when errUpdateNoOp is updated to verror2
- return
- }
+ if err := appStub(appID).Update(rt.R().NewContext()); err == nil || !verror2.Is(err, expectedError) {
t.Fatalf("%s: Update(%v) expected to fail with %v, got %v instead", loc(1), appID, expectedError, err)
}
}
@@ -325,11 +348,7 @@
}
func revertAppExpectError(t *testing.T, appID string, expectedError verror.ID) {
- if err := appStub(appID).Revert(rt.R().NewContext()); err == nil || !verror.Is(err, expectedError) {
- if expectedError == naming.ErrNoSuchName.ID && err.Error() == "no different version available" {
- // TODO(bprosnitz) Remove this check when errUpdateNoOp is updated to verror2
- return
- }
+ if err := appStub(appID).Revert(rt.R().NewContext()); err == nil || !verror2.Is(err, expectedError) {
t.Fatalf("%s: Revert(%v) expected to fail with %v, got %v instead", loc(1), appID, expectedError, err)
}
}
diff --git a/services/mgmt/node/noded/main.go b/services/mgmt/node/noded/main.go
index eea0702..c40f2a9 100644
--- a/services/mgmt/node/noded/main.go
+++ b/services/mgmt/node/noded/main.go
@@ -76,5 +76,5 @@
impl.InvokeCallback(name)
// Wait until shutdown.
- <-signals.ShutdownOnSignals()
+ <-signals.ShutdownOnSignals(runtime)
}
diff --git a/services/mgmt/profile/profile.vdl.go b/services/mgmt/profile/profile.vdl.go
index 97c0689..d3c766c 100644
--- a/services/mgmt/profile/profile.vdl.go
+++ b/services/mgmt/profile/profile.vdl.go
@@ -7,6 +7,9 @@
import (
"veyron.io/veyron/veyron2/services/mgmt/build"
+
+ // The non-user imports are prefixed with "__" to prevent collisions.
+ __vdl "veyron.io/veyron/veyron2/vdl"
)
// Library describes a shared library that applications may use.
@@ -19,6 +22,11 @@
MinorVersion string
}
+func (Library) __VDLReflect(struct {
+ Name string "veyron.io/veyron/veyron/services/mgmt/profile.Library"
+}) {
+}
+
// Specification is how we represent a profile internally. It should
// provide enough information to allow matching of binaries to nodes.
type Specification struct {
@@ -36,3 +44,13 @@
// OS is the target operating system of the profile.
OS build.OperatingSystem
}
+
+func (Specification) __VDLReflect(struct {
+ Name string "veyron.io/veyron/veyron/services/mgmt/profile.Specification"
+}) {
+}
+
+func init() {
+ __vdl.Register(Library{})
+ __vdl.Register(Specification{})
+}
diff --git a/services/mgmt/profile/profiled/main.go b/services/mgmt/profile/profiled/main.go
index d01b803..a811f12 100644
--- a/services/mgmt/profile/profiled/main.go
+++ b/services/mgmt/profile/profiled/main.go
@@ -45,5 +45,5 @@
vlog.Infof("Profile repository running at endpoint=%q", endpoint)
// Wait until shutdown.
- <-signals.ShutdownOnSignals()
+ <-signals.ShutdownOnSignals(runtime)
}
diff --git a/services/mgmt/repository/repository.vdl b/services/mgmt/repository/repository.vdl
index 3b553b8..ab9acb4 100644
--- a/services/mgmt/repository/repository.vdl
+++ b/services/mgmt/repository/repository.vdl
@@ -4,8 +4,8 @@
import (
"veyron.io/veyron/veyron/services/mgmt/profile"
- "veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/services/mgmt/application"
+ "veyron.io/veyron/veyron2/services/security/access"
public "veyron.io/veyron/veyron2/services/mgmt/repository"
)
@@ -17,7 +17,7 @@
// Put adds the given tuple of application version (specified
// through the object name suffix) and application envelope to all
// of the given application profiles.
- Put(Profiles []string, Envelope application.Envelope) error {security.WriteLabel}
+ Put(Profiles []string, Envelope application.Envelope) error {access.Write}
// Remove removes the application envelope for the given profile
// name and application version (specified through the object name
// suffix). If no version is specified as part of the suffix, the
@@ -25,7 +25,7 @@
//
// TODO(jsimsa): Add support for using "*" to specify all profiles
// when Matt implements Globing (or Ken implements querying).
- Remove(Profile string) error {security.WriteLabel}
+ Remove(Profile string) error {access.Write}
}
// Profile describes a profile internally. Besides the public Profile
@@ -34,11 +34,11 @@
public.Profile
// Specification returns the profile specification for the profile
// identified through the object name suffix.
- Specification() (profile.Specification, error) {security.ReadLabel}
+ Specification() (profile.Specification, error) {access.Read}
// Put sets the profile specification for the profile identified
// through the object name suffix.
- Put(Specification profile.Specification) error {security.WriteLabel}
+ Put(Specification profile.Specification) error {access.Write}
// Remove removes the profile specification for the profile
// identified through the object name suffix.
- Remove() error {security.WriteLabel}
+ Remove() error {access.Write}
}
diff --git a/services/mgmt/repository/repository.vdl.go b/services/mgmt/repository/repository.vdl.go
index 0b017b3..48541ae 100644
--- a/services/mgmt/repository/repository.vdl.go
+++ b/services/mgmt/repository/repository.vdl.go
@@ -8,12 +8,12 @@
import (
"veyron.io/veyron/veyron/services/mgmt/profile"
- "veyron.io/veyron/veyron2/security"
-
"veyron.io/veyron/veyron2/services/mgmt/application"
"veyron.io/veyron/veyron2/services/mgmt/repository"
+ "veyron.io/veyron/veyron2/services/security/access"
+
// The non-user imports are prefixed with "__" to prevent collisions.
__veyron2 "veyron.io/veyron/veyron2"
__context "veyron.io/veyron/veyron2/context"
@@ -232,7 +232,7 @@
OutArgs: []__ipc.ArgDesc{
{"", ``}, // error
},
- Tags: []__vdlutil.Any{security.Label(4)},
+ Tags: []__vdlutil.Any{access.Tag("Write")},
},
{
Name: "Remove",
@@ -243,7 +243,7 @@
OutArgs: []__ipc.ArgDesc{
{"", ``}, // error
},
- Tags: []__vdlutil.Any{security.Label(4)},
+ Tags: []__vdlutil.Any{access.Tag("Write")},
},
},
}
@@ -254,10 +254,10 @@
result.Methods["Put"] = __ipc.MethodSignature{
InArgs: []__ipc.MethodArgument{
{Name: "Profiles", Type: 61},
- {Name: "Envelope", Type: 65},
+ {Name: "Envelope", Type: 66},
},
OutArgs: []__ipc.MethodArgument{
- {Name: "", Type: 66},
+ {Name: "", Type: 67},
},
}
result.Methods["Remove"] = __ipc.MethodSignature{
@@ -265,17 +265,18 @@
{Name: "Profile", Type: 3},
},
OutArgs: []__ipc.MethodArgument{
- {Name: "", Type: 66},
+ {Name: "", Type: 67},
},
}
result.TypeDefs = []__vdlutil.Any{
- __wiretype.StructType{
+ __wiretype.MapType{Key: 0x3, Elem: 0x3, Name: "", Tags: []string(nil)}, __wiretype.StructType{
[]__wiretype.FieldType{
__wiretype.FieldType{Type: 0x3, Name: "Title"},
__wiretype.FieldType{Type: 0x3d, Name: "Args"},
__wiretype.FieldType{Type: 0x3, Name: "Binary"},
__wiretype.FieldType{Type: 0x3d, Name: "Env"},
+ __wiretype.FieldType{Type: 0x41, Name: "Packages"},
},
"veyron.io/veyron/veyron2/services/mgmt/application.Envelope", []string(nil)},
__wiretype.NamedPrimitiveType{Type: 0x1, Name: "error", Tags: []string(nil)}}
@@ -535,7 +536,7 @@
{"", ``}, // profile.Specification
{"", ``}, // error
},
- Tags: []__vdlutil.Any{security.Label(2)},
+ Tags: []__vdlutil.Any{access.Tag("Read")},
},
{
Name: "Put",
@@ -546,7 +547,7 @@
OutArgs: []__ipc.ArgDesc{
{"", ``}, // error
},
- Tags: []__vdlutil.Any{security.Label(4)},
+ Tags: []__vdlutil.Any{access.Tag("Write")},
},
{
Name: "Remove",
@@ -554,7 +555,7 @@
OutArgs: []__ipc.ArgDesc{
{"", ``}, // error
},
- Tags: []__vdlutil.Any{security.Label(4)},
+ Tags: []__vdlutil.Any{access.Tag("Write")},
},
},
}
diff --git a/services/mgmt/root/rootd/main.go b/services/mgmt/root/rootd/main.go
index ff23fdc..45f9205 100644
--- a/services/mgmt/root/rootd/main.go
+++ b/services/mgmt/root/rootd/main.go
@@ -32,5 +32,5 @@
}
// Wait until shutdown.
- <-signals.ShutdownOnSignals()
+ <-signals.ShutdownOnSignals(r)
}
diff --git a/services/mgmt/stats/impl/stats_test.go b/services/mgmt/stats/impl/stats_test.go
index 6b526f6..fe502c4 100644
--- a/services/mgmt/stats/impl/stats_test.go
+++ b/services/mgmt/stats/impl/stats_test.go
@@ -107,6 +107,8 @@
// Test WatchGlob()
{
+ noRM := types.ResumeMarker{}
+ _ = noRM
stream, err := c.WatchGlob(rt.R().NewContext(), types.GlobRequest{Pattern: "testing/foo/bar"})
if err != nil {
t.Fatalf("c.WatchGlob failed: %v", err)
@@ -116,6 +118,8 @@
t.Fatalf("expected more stream values")
}
got := iterator.Value()
+ // TODO(toddw): This change is necessary for vom2:
+ //expected := types.Change{Name: "testing/foo/bar", Value: int64(10), ResumeMarker: noRM}
expected := types.Change{Name: "testing/foo/bar", Value: int64(10)}
if !reflect.DeepEqual(got, expected) {
t.Errorf("unexpected result. Got %#v, want %#v", got, expected)
@@ -127,6 +131,8 @@
t.Fatalf("expected more stream values")
}
got = iterator.Value()
+ // TODO(toddw): This change is necessary for vom2:
+ //expected := types.Change{Name: "testing/foo/bar", Value: int64(15), ResumeMarker: noRM}
expected = types.Change{Name: "testing/foo/bar", Value: int64(15)}
if !reflect.DeepEqual(got, expected) {
t.Errorf("unexpected result. Got %#v, want %#v", got, expected)
@@ -138,6 +144,8 @@
t.Fatalf("expected more stream values")
}
got = iterator.Value()
+ // TODO(toddw): This change is necessary for vom2:
+ //expected := types.Change{Name: "testing/foo/bar", Value: int64(17), ResumeMarker: noRM}
expected = types.Change{Name: "testing/foo/bar", Value: int64(17)}
if !reflect.DeepEqual(got, expected) {
t.Errorf("unexpected result. Got %#v, want %#v", got, expected)
@@ -171,6 +179,8 @@
want := istats.HistogramValue{
Count: 10,
Sum: 45,
+ Min: 0,
+ Max: 9,
Buckets: []istats.HistogramBucket{
istats.HistogramBucket{LowBound: 0, Count: 1},
istats.HistogramBucket{LowBound: 1, Count: 2},
diff --git a/services/mgmt/stats/types.go b/services/mgmt/stats/types.go
new file mode 100644
index 0000000..1791465
--- /dev/null
+++ b/services/mgmt/stats/types.go
@@ -0,0 +1,51 @@
+package stats
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+)
+
+// Print writes textual output of the histogram values.
+func (v HistogramValue) Print(w io.Writer) {
+ avg := float64(v.Sum) / float64(v.Count)
+ fmt.Fprintf(w, "Count: %d Min: %d Max: %d Avg: %.2f\n", v.Count, v.Min, v.Max, avg)
+ fmt.Fprintf(w, "%s\n", strings.Repeat("-", 60))
+ if v.Count <= 0 {
+ return
+ }
+
+ maxBucketDigitLen := len(strconv.FormatInt(v.Buckets[len(v.Buckets)-1].LowBound, 10))
+ if maxBucketDigitLen < 3 {
+ // For "inf".
+ maxBucketDigitLen = 3
+ }
+ maxCountDigitLen := len(strconv.FormatInt(v.Count, 10))
+ percentMulti := 100 / float64(v.Count)
+
+ accCount := int64(0)
+ for i, b := range v.Buckets {
+ fmt.Fprintf(w, "[%*d, ", maxBucketDigitLen, b.LowBound)
+ if i+1 < len(v.Buckets) {
+ fmt.Fprintf(w, "%*d)", maxBucketDigitLen, v.Buckets[i+1].LowBound)
+ } else {
+ fmt.Fprintf(w, "%*s)", maxBucketDigitLen, "inf")
+ }
+
+ accCount += b.Count
+ fmt.Fprintf(w, " %*d %5.1f%% %5.1f%%", maxCountDigitLen, b.Count, float64(b.Count)*percentMulti, float64(accCount)*percentMulti)
+
+ const barScale = 0.1
+ barLength := int(float64(b.Count)*percentMulti*barScale + 0.5)
+ fmt.Fprintf(w, " %s\n", strings.Repeat("#", barLength))
+ }
+}
+
+// String returns the textual output of the histogram values as string.
+func (v HistogramValue) String() string {
+ var b bytes.Buffer
+ v.Print(&b)
+ return b.String()
+}
diff --git a/services/mgmt/stats/types.vdl b/services/mgmt/stats/types.vdl
index 8aa3ec9..d11aa13 100644
--- a/services/mgmt/stats/types.vdl
+++ b/services/mgmt/stats/types.vdl
@@ -7,6 +7,10 @@
Count int64
// Sum is the sum of all the values added to the histogram.
Sum int64
+ // Min is the minimum of all the values added to the histogram.
+ Min int64
+ // Max is the maximum of all the values added to the histogram.
+ Max int64
// Buckets contains all the buckets of the histogram.
Buckets []HistogramBucket
}
diff --git a/services/mgmt/stats/types.vdl.go b/services/mgmt/stats/types.vdl.go
index 7ad330c..4884f69 100644
--- a/services/mgmt/stats/types.vdl.go
+++ b/services/mgmt/stats/types.vdl.go
@@ -4,16 +4,30 @@
// Packages stats defines the non-native types exported by the stats service.
package stats
+import (
+ // The non-user imports are prefixed with "__" to prevent collisions.
+ __vdl "veyron.io/veyron/veyron2/vdl"
+)
+
// HistogramValue is the value of Histogram objects.
type HistogramValue struct {
// Count is the total number of values added to the histogram.
Count int64
// Sum is the sum of all the values added to the histogram.
Sum int64
+ // Min is the minimum of all the values added to the histogram.
+ Min int64
+ // Max is the maximum of all the values added to the histogram.
+ Max int64
// Buckets contains all the buckets of the histogram.
Buckets []HistogramBucket
}
+func (HistogramValue) __VDLReflect(struct {
+ Name string "veyron.io/veyron/veyron/services/mgmt/stats.HistogramValue"
+}) {
+}
+
// HistogramBucket is one histogram bucket.
type HistogramBucket struct {
// LowBound is the lower bound of the bucket.
@@ -21,3 +35,13 @@
// Count is the number of values in the bucket.
Count int64
}
+
+func (HistogramBucket) __VDLReflect(struct {
+ Name string "veyron.io/veyron/veyron/services/mgmt/stats.HistogramBucket"
+}) {
+}
+
+func init() {
+ __vdl.Register(HistogramValue{})
+ __vdl.Register(HistogramBucket{})
+}
diff --git a/services/mgmt/vtrace/impl/vtrace_test.go b/services/mgmt/vtrace/impl/vtrace_test.go
index 4e1d0c2..3600fd9 100644
--- a/services/mgmt/vtrace/impl/vtrace_test.go
+++ b/services/mgmt/vtrace/impl/vtrace_test.go
@@ -75,8 +75,8 @@
if err = stream.Err(); err != nil && err != io.EOF {
t.Fatalf("Unexpected error reading trace stream: %s", err)
}
- if ntraces < 2 {
- t.Fatalf("Expected at least 2 traces, got %#v", ntraces)
+ if ntraces != 1 {
+ t.Fatalf("Expected 1 trace, got %#v", ntraces)
}
if tr == nil {
t.Fatalf("Desired trace %x not found.", id)
diff --git a/services/mounttable/lib/mounttable.go b/services/mounttable/lib/mounttable.go
index 5ca2af2..719a321 100644
--- a/services/mounttable/lib/mounttable.go
+++ b/services/mounttable/lib/mounttable.go
@@ -10,13 +10,13 @@
"time"
"veyron.io/veyron/veyron/lib/glob"
- vsecurity "veyron.io/veyron/veyron/security"
"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/mounttable"
+ "veyron.io/veyron/veyron2/services/security/access"
verror "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
)
@@ -58,7 +58,12 @@
children map[string]*node
}
-// NewMountTable creates a new server that uses the default authorization policy.
+// NewMountTable creates a new server that uses the ACLs specified in
+// aclfile for authorization.
+//
+// aclfile is a JSON-encoded mapping from paths in the mounttable to the
+// access.TaggedACLMap for that path. The tags used in the map are the typical
+// access tags (the Tag type defined in veyron2/services/security/access).
func NewMountTable(aclfile string) (*mountTable, error) {
acls, err := parseACLs(aclfile)
if err != nil {
@@ -74,7 +79,7 @@
if path == "" {
return nil, nil
}
- var acls map[string]security.ACL
+ var acls map[string]access.TaggedACLMap
f, err := os.Open(path)
if err != nil {
return nil, err
@@ -85,7 +90,10 @@
}
result := make(map[string]security.Authorizer)
for name, acl := range acls {
- result[name] = vsecurity.NewACLAuthorizer(acl)
+ result[name], err = access.TaggedACLAuthorizer(acl, access.TypicalTagType())
+ if err != nil {
+ return nil, fmt.Errorf("Unable to create ACL for %q: %v", name, err)
+ }
}
if result["/"] == nil {
return nil, fmt.Errorf("No acl for / in %s", path)
@@ -173,7 +181,7 @@
mt.Lock()
acl := mt.acls[name]
mt.Unlock()
- vlog.VI(2).Infof("authorizeStep(%s) %s %s %s", name, c.RemoteBlessings(), c.Label(), acl)
+ vlog.VI(2).Infof("authorizeStep(%v) %v %v %v", name, c.RemoteBlessings(), c.MethodTags(), acl)
if acl != nil {
return acl.Authorize(c)
}
diff --git a/services/mounttable/lib/testdata/noRoot.acl b/services/mounttable/lib/testdata/noRoot.acl
index 0ff2383..b663a07 100644
--- a/services/mounttable/lib/testdata/noRoot.acl
+++ b/services/mounttable/lib/testdata/noRoot.acl
@@ -1,3 +1,6 @@
{
-"/foo/bar": {"fake/root": "RW"},
-}
\ No newline at end of file
+"/foo/bar": {
+ "Read": { "In": ["fake/root"] },
+ "Write": { "In": ["fake/root"] }
+}
+}
diff --git a/services/mounttable/lib/testdata/test.acl b/services/mounttable/lib/testdata/test.acl
index 597dda9..7990532 100644
--- a/services/mounttable/lib/testdata/test.acl
+++ b/services/mounttable/lib/testdata/test.acl
@@ -1,5 +1,14 @@
{
-"/": {"In": {"root": "RW", "...": "R"}},
-"/stuff": {"In": {"root": "RW", "bob": "R"}},
-"/a": {"In": {"root": "RW", "alice": "R"}}
-}
\ No newline at end of file
+"/": {
+ "Read": { "In": ["..."] },
+ "Write": { "In": ["root"] }
+},
+"/stuff": {
+ "Read": { "In": ["bob", "root"] },
+ "Write": { "In": ["root"] }
+},
+"/a": {
+ "Read": { "In": ["alice", "root"] },
+ "Write": { "In": ["root"] }
+}
+}
diff --git a/services/mounttable/mounttabled/mounttable.go b/services/mounttable/mounttabled/mounttable.go
index 7234a09..3286652 100644
--- a/services/mounttable/mounttabled/mounttable.go
+++ b/services/mounttable/mounttabled/mounttable.go
@@ -84,5 +84,5 @@
}
// Wait until signal is received.
- vlog.Info("Received signal ", <-signals.ShutdownOnSignals())
+ vlog.Info("Received signal ", <-signals.ShutdownOnSignals(r))
}
diff --git a/services/proxy/proxyd/main.go b/services/proxy/proxyd/main.go
index be03702..b31b919 100644
--- a/services/proxy/proxyd/main.go
+++ b/services/proxy/proxyd/main.go
@@ -6,6 +6,7 @@
"flag"
"net/http"
_ "net/http/pprof"
+ "strings"
"time"
"veyron.io/veyron/veyron2/naming"
@@ -47,7 +48,15 @@
publisher := publisher.New(r.NewContext(), r.Namespace(), time.Minute)
defer publisher.WaitForStop()
defer publisher.Stop()
- publisher.AddServer(naming.JoinAddressName(proxy.Endpoint().String(), ""), false)
+ ep := naming.JoinAddressName(proxy.Endpoint().String(), "")
+ publisher.AddServer(ep, false)
+ // If the protocol is tcp we need to also publish the websocket endpoint.
+ // TODO(bjornick): Remove this hack before we launch.
+ if strings.HasPrefix(proxy.Endpoint().Addr().Network(), "tcp") {
+ wsEP := strings.Replace(ep, "@"+proxy.Endpoint().Addr().Network()+"@", "@ws@", 1)
+ publisher.AddServer(wsEP, false)
+ }
+
publisher.AddName(*name)
}
diff --git a/services/security/discharger.vdl b/services/security/discharger.vdl
index 037c6b4..a7dfbd3 100644
--- a/services/security/discharger.vdl
+++ b/services/security/discharger.vdl
@@ -12,5 +12,5 @@
// respectively. (not enforced here because vdl does not know these types)
// TODO(ataly,ashankar): Figure out a VDL representation for ThirdPartyCaveat
// and Discharge and use those here?
- Discharge(Caveat any, Impetus security.DischargeImpetus) (Discharge any, err error) {security.ReadLabel}
+ Discharge(Caveat any, Impetus security.DischargeImpetus) (Discharge any, err error)
}
diff --git a/services/security/discharger.vdl.go b/services/security/discharger.vdl.go
index 420c1c6..8fbf3c6 100644
--- a/services/security/discharger.vdl.go
+++ b/services/security/discharger.vdl.go
@@ -171,7 +171,6 @@
{"Discharge", ``}, // __vdlutil.Any
{"err", ``}, // error
},
- Tags: []__vdlutil.Any{security.Label(2)},
},
},
}
diff --git a/tools/application/doc.go b/tools/application/doc.go
index 5c8f11f..ba58393 100644
--- a/tools/application/doc.go
+++ b/tools/application/doc.go
@@ -9,7 +9,8 @@
application <command>
The application commands are:
- match Shows the first matching envelope that matches the given profiles.
+ match Shows the first matching envelope that matches the given
+ profiles.
put Add the given envelope to the application for the given profiles.
remove removes the application envelope for the given profile.
edit edits the application envelope for the given profile.
@@ -17,15 +18,32 @@
Run "application help [command]" for command usage.
The global flags are:
- -alsologtostderr=true: log to standard error as well as files
- -log_backtrace_at=:0: when logging hits line file:N, emit a stack trace
- -log_dir=: if non-empty, write log files to this directory
- -logtostderr=false: log to standard error instead of files
- -max_stack_buf_size=4292608: max size in bytes of the buffer to use for logging stack traces
- -stderrthreshold=2: logs at or above this threshold go to stderr
- -v=0: log level for V logs
- -vmodule=: comma-separated list of pattern=N settings for file-filtered logging
- -vv=0: log level for V logs
+ -alsologtostderr=true
+ log to standard error as well as files
+ -log_backtrace_at=:0
+ when logging hits line file:N, emit a stack trace
+ -log_dir=
+ if non-empty, write log files to this directory
+ -logtostderr=false
+ log to standard error instead of files
+ -max_stack_buf_size=4292608
+ max size in bytes of the buffer to use for logging stack traces
+ -stderrthreshold=2
+ logs at or above this threshold go to stderr
+ -v=0
+ log level for V logs
+ -veyron.credentials=
+ directory to use for storing security credentials
+ -veyron.namespace.root=[/proxy.envyor.com:8101]
+ local namespace root; can be repeated to provided multiple roots
+ -veyron.vtrace.cache_size=1024
+ The number of vtrace traces to store in memory.
+ -veyron.vtrace.dump_on_shutdown=false
+ If true, dump all stored traces on runtime shutdown.
+ -veyron.vtrace.sample_rate=0
+ Rate (from 0.0 to 1.0) to sample vtrace traces.
+ -vmodule=
+ comma-separated list of pattern=N settings for file-filtered logging
Application Match
@@ -34,8 +52,8 @@
Usage:
application match <application> <profiles>
-<application> is the full name of the application.
-<profiles> is a comma-separated list of profiles.
+<application> is the full name of the application. <profiles> is a
+comma-separated list of profiles.
Application Put
@@ -44,9 +62,9 @@
Usage:
application put <application> <profiles> <envelope>
-<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.
+<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.
Application Remove
@@ -55,8 +73,7 @@
Usage:
application remove <application> <profile>
-<application> is the full name of the application.
-<profile> is a profile.
+<application> is the full name of the application. <profile> is a profile.
Application Edit
@@ -65,21 +82,29 @@
Usage:
application edit <application> <profile>
-<application> is the full name of the application.
-<profile> is a profile.
+<application> is the full name of the application. <profile> is a profile.
Application Help
Help with no args displays the usage of the parent command.
+
Help with args displays the usage of the specified sub-command or help topic.
+
"help ..." recursively displays help for all commands and topics.
+The output is formatted to a target width in runes. The target width is
+determined by checking the environment variable CMDLINE_WIDTH, falling back on
+the terminal width from the OS, falling back on 80 chars. By setting
+CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0 the width is unlimited, and
+if x == 0 or is unset one of the fallbacks is used.
+
Usage:
application help [flags] [command/topic ...]
[command/topic ...] optionally identifies a specific sub-command or help topic.
-The help flags are:
- -style=text: The formatting style for help output, either "text" or "godoc".
+The application help flags are:
+ -style=text
+ The formatting style for help output, either "text" or "godoc".
*/
package main
diff --git a/tools/application/impl.go b/tools/application/impl.go
index e37608f..033775b 100644
--- a/tools/application/impl.go
+++ b/tools/application/impl.go
@@ -14,7 +14,6 @@
"veyron.io/lib/cmdline"
"veyron.io/veyron/veyron/services/mgmt/repository"
"veyron.io/veyron/veyron2/context"
- "veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/services/mgmt/application"
)
@@ -67,7 +66,7 @@
}
name, profiles := args[0], args[1]
app := repository.ApplicationClient(name)
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
j, err := getEnvelopeJSON(ctx, app, profiles)
if err != nil {
@@ -99,7 +98,7 @@
if err != nil {
return fmt.Errorf("ReadFile(%v): %v", envelope, err)
}
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
if err = putEnvelopeJSON(ctx, app, profiles, j); err != nil {
return err
@@ -125,7 +124,7 @@
}
name, profile := args[0], args[1]
app := repository.ApplicationClient(name)
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
if err := app.Remove(ctx, profile); err != nil {
return err
@@ -159,7 +158,7 @@
f.Close()
defer os.Remove(fileName)
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
envData, err := getEnvelopeJSON(ctx, app, profile)
if err != nil {
diff --git a/tools/application/impl_test.go b/tools/application/impl_test.go
index 010ace1..d7a6421 100644
--- a/tools/application/impl_test.go
+++ b/tools/application/impl_test.go
@@ -25,6 +25,9 @@
Args: []string{"arg1", "arg2", "arg3"},
Binary: "/path/to/binary",
Env: []string{"env1", "env2", "env3"},
+ Packages: map[string]string{
+ "pkg1": "/path/to/package1",
+ },
}
jsonEnv = `{
"Title": "fifa world cup",
@@ -38,7 +41,10 @@
"env1",
"env2",
"env3"
- ]
+ ],
+ "Packages": {
+ "pkg1": "/path/to/package1"
+ }
}`
)
@@ -98,7 +104,13 @@
}
func TestApplicationClient(t *testing.T) {
- runtime := rt.Init()
+ var err error
+ runtime, err = rt.New()
+ if err != nil {
+ t.Fatalf("Unexpected error initializing runtime: %s", err)
+ }
+ defer runtime.Cleanup()
+
server, endpoint, err := startServer(t, runtime)
if err != nil {
return
diff --git a/tools/application/main.go b/tools/application/main.go
index 9e5d556..3723981 100644
--- a/tools/application/main.go
+++ b/tools/application/main.go
@@ -1,15 +1,23 @@
// The following enables go generate to generate the doc.go file.
-//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
+//go:generate go run $VEYRON_ROOT/veyron/go/src/veyron.io/lib/cmdline/testdata/gendoc.go .
package main
import (
+ "veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/rt"
_ "veyron.io/veyron/veyron/profiles"
)
+var runtime veyron2.Runtime
+
func main() {
- defer rt.Init().Cleanup()
+ var err error
+ runtime, err = rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
root().Main()
}
diff --git a/tools/binary/doc.go b/tools/binary/doc.go
index 04afd49..daae844 100644
--- a/tools/binary/doc.go
+++ b/tools/binary/doc.go
@@ -15,15 +15,32 @@
Run "binary help [command]" for command usage.
The global flags are:
- -alsologtostderr=true: log to standard error as well as files
- -log_backtrace_at=:0: when logging hits line file:N, emit a stack trace
- -log_dir=: if non-empty, write log files to this directory
- -logtostderr=false: log to standard error instead of files
- -max_stack_buf_size=4292608: max size in bytes of the buffer to use for logging stack traces
- -stderrthreshold=2: logs at or above this threshold go to stderr
- -v=0: log level for V logs
- -vmodule=: comma-separated list of pattern=N settings for file-filtered logging
- -vv=0: log level for V logs
+ -alsologtostderr=true
+ log to standard error as well as files
+ -log_backtrace_at=:0
+ when logging hits line file:N, emit a stack trace
+ -log_dir=
+ if non-empty, write log files to this directory
+ -logtostderr=false
+ log to standard error instead of files
+ -max_stack_buf_size=4292608
+ max size in bytes of the buffer to use for logging stack traces
+ -stderrthreshold=2
+ logs at or above this threshold go to stderr
+ -v=0
+ log level for V logs
+ -veyron.credentials=
+ directory to use for storing security credentials
+ -veyron.namespace.root=[/proxy.envyor.com:8101]
+ local namespace root; can be repeated to provided multiple roots
+ -veyron.vtrace.cache_size=1024
+ The number of vtrace traces to store in memory.
+ -veyron.vtrace.dump_on_shutdown=false
+ If true, dump all stored traces on runtime shutdown.
+ -veyron.vtrace.sample_rate=0
+ Rate (from 0.0 to 1.0) to sample vtrace traces.
+ -vmodule=
+ comma-separated list of pattern=N settings for file-filtered logging
Binary Delete
@@ -42,8 +59,8 @@
Usage:
binary download <von> <filename>
-<von> is the veyron object name of the binary to download
-<filename> is the name of the file where the binary will be written
+<von> is the veyron object name of the binary to download <filename> is the name
+of the file where the binary will be written
Binary Upload
@@ -53,21 +70,30 @@
Usage:
binary upload <von> <filename>
-<von> is the veyron object name of the binary to upload
-<filename> is the name of the file to upload
+<von> is the veyron object name of the binary to upload <filename> is the name
+of the file to upload
Binary Help
Help with no args displays the usage of the parent command.
+
Help with args displays the usage of the specified sub-command or help topic.
+
"help ..." recursively displays help for all commands and topics.
+The output is formatted to a target width in runes. The target width is
+determined by checking the environment variable CMDLINE_WIDTH, falling back on
+the terminal width from the OS, falling back on 80 chars. By setting
+CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0 the width is unlimited, and
+if x == 0 or is unset one of the fallbacks is used.
+
Usage:
binary help [flags] [command/topic ...]
[command/topic ...] optionally identifies a specific sub-command or help topic.
-The help flags are:
- -style=text: The formatting style for help output, either "text" or "godoc".
+The binary help flags are:
+ -style=text
+ The formatting style for help output, either "text" or "godoc".
*/
package main
diff --git a/tools/binary/impl.go b/tools/binary/impl.go
index 69f0f51..df34d6c 100644
--- a/tools/binary/impl.go
+++ b/tools/binary/impl.go
@@ -21,7 +21,7 @@
return cmd.UsageErrorf("delete: incorrect number of arguments, expected %d, got %d", expected, got)
}
von := args[0]
- if err := binary.Delete(von); err != nil {
+ if err := binary.Delete(runtime.NewContext(), von); err != nil {
return err
}
fmt.Fprintf(cmd.Stdout(), "Binary deleted successfully\n")
@@ -48,7 +48,7 @@
return cmd.UsageErrorf("download: incorrect number of arguments, expected %d, got %d", expected, got)
}
von, filename := args[0], args[1]
- if err := binary.DownloadToFile(von, filename); err != nil {
+ if err := binary.DownloadToFile(runtime.NewContext(), von, filename); err != nil {
return err
}
fmt.Fprintf(cmd.Stdout(), "Binary downloaded to file %s\n", filename)
@@ -71,11 +71,12 @@
}
func runUpload(cmd *cmdline.Command, args []string) error {
+ // TODO(rthellend): Add support for creating packages on the fly.
if expected, got := 2, len(args); expected != got {
return cmd.UsageErrorf("upload: incorrect number of arguments, expected %d, got %d", expected, got)
}
von, filename := args[0], args[1]
- if err := binary.UploadFromFile(von, filename); err != nil {
+ if err := binary.UploadFromFile(runtime.NewContext(), von, filename); err != nil {
return err
}
fmt.Fprintf(cmd.Stdout(), "Binary uploaded from file %s\n", filename)
diff --git a/tools/binary/impl_test.go b/tools/binary/impl_test.go
index 0f32f85..865c35d 100644
--- a/tools/binary/impl_test.go
+++ b/tools/binary/impl_test.go
@@ -27,7 +27,7 @@
suffix string
}
-func (s *server) Create(ipc.ServerContext, int32) error {
+func (s *server) Create(ipc.ServerContext, int32, repository.MediaInfo) error {
vlog.Infof("Create() was called. suffix=%v", s.suffix)
return nil
}
@@ -53,13 +53,13 @@
return "", 0, nil
}
-func (s *server) Stat(ipc.ServerContext) ([]binary.PartInfo, error) {
+func (s *server) Stat(ipc.ServerContext) ([]binary.PartInfo, repository.MediaInfo, error) {
vlog.Infof("Stat() was called. suffix=%v", s.suffix)
h := md5.New()
text := "HelloWorld"
h.Write([]byte(text))
part := binary.PartInfo{Checksum: hex.EncodeToString(h.Sum(nil)), Size: int64(len(text))}
- return []binary.PartInfo{part}, nil
+ return []binary.PartInfo{part}, repository.MediaInfo{Type: "text/plain"}, nil
}
func (s *server) Upload(ctx repository.BinaryUploadContext, _ int32) error {
@@ -107,7 +107,13 @@
}
func TestBinaryClient(t *testing.T) {
- runtime := rt.Init()
+ var err error
+ runtime, err = rt.New()
+ if err != nil {
+ t.Fatalf("Unexpected error initializing runtime: %s", err)
+ }
+ defer runtime.Cleanup()
+
server, endpoint, err := startServer(t, runtime)
if err != nil {
return
diff --git a/tools/binary/main.go b/tools/binary/main.go
index 9e5d556..3723981 100644
--- a/tools/binary/main.go
+++ b/tools/binary/main.go
@@ -1,15 +1,23 @@
// The following enables go generate to generate the doc.go file.
-//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
+//go:generate go run $VEYRON_ROOT/veyron/go/src/veyron.io/lib/cmdline/testdata/gendoc.go .
package main
import (
+ "veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/rt"
_ "veyron.io/veyron/veyron/profiles"
)
+var runtime veyron2.Runtime
+
func main() {
- defer rt.Init().Cleanup()
+ var err error
+ runtime, err = rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
root().Main()
}
diff --git a/tools/build/doc.go b/tools/build/doc.go
index 9d7fc7e..347c55a 100644
--- a/tools/build/doc.go
+++ b/tools/build/doc.go
@@ -13,49 +13,76 @@
Run "build help [command]" for command usage.
The global flags are:
- -alsologtostderr=true: log to standard error as well as files
- -log_backtrace_at=:0: when logging hits line file:N, emit a stack trace
- -log_dir=: if non-empty, write log files to this directory
- -logtostderr=false: log to standard error instead of files
- -max_stack_buf_size=4292608: max size in bytes of the buffer to use for logging stack traces
- -stderrthreshold=2: logs at or above this threshold go to stderr
- -v=0: log level for V logs
- -vmodule=: comma-separated list of pattern=N settings for file-filtered logging
- -vv=0: log level for V logs
+ -alsologtostderr=true
+ log to standard error as well as files
+ -log_backtrace_at=:0
+ when logging hits line file:N, emit a stack trace
+ -log_dir=
+ if non-empty, write log files to this directory
+ -logtostderr=false
+ log to standard error instead of files
+ -max_stack_buf_size=4292608
+ max size in bytes of the buffer to use for logging stack traces
+ -stderrthreshold=2
+ logs at or above this threshold go to stderr
+ -v=0
+ log level for V logs
+ -veyron.credentials=
+ directory to use for storing security credentials
+ -veyron.namespace.root=[/proxy.envyor.com:8101]
+ local namespace root; can be repeated to provided multiple roots
+ -veyron.vtrace.cache_size=1024
+ The number of vtrace traces to store in memory.
+ -veyron.vtrace.dump_on_shutdown=false
+ If true, dump all stored traces on runtime shutdown.
+ -veyron.vtrace.sample_rate=0
+ Rate (from 0.0 to 1.0) to sample vtrace traces.
+ -vmodule=
+ comma-separated list of pattern=N settings for file-filtered logging
Build Build
-Build veyron Go packages using a remote build server. The command
-collects all source code files that are not part of the Go standard
-library that the target packages depend on, sends them to a build
-server, and receives the built binaries.
+Build veyron Go packages using a remote build server. The command collects all
+source code files that are not part of the Go standard library that the target
+packages depend on, sends them to a build server, and receives the built
+binaries.
Usage:
build build [flags] <name> <packages>
-<name> is a veyron object name of a build server
-<packages> is a list of packages to build, specified as arguments for
-each command. The format is similar to the go tool. In its simplest
-form each package is an import path; e.g. "veyron/tools/build". A
-package that ends with "..." does a wildcard match against all
-packages with that prefix.
+<name> is a veyron object name of a build server <packages> is a list of
+packages to build, specified as arguments for each command. The format is
+similar to the go tool. In its simplest form each package is an import path;
+e.g. "veyron/tools/build". A package that ends with "..." does a wildcard match
+against all packages with that prefix.
-The build flags are:
- -arch=amd64: Target architecture.
- -os=linux: Target operating system.
+The build build flags are:
+ -arch=amd64
+ Target architecture.
+ -os=darwin
+ Target operating system.
Build Help
Help with no args displays the usage of the parent command.
+
Help with args displays the usage of the specified sub-command or help topic.
+
"help ..." recursively displays help for all commands and topics.
+The output is formatted to a target width in runes. The target width is
+determined by checking the environment variable CMDLINE_WIDTH, falling back on
+the terminal width from the OS, falling back on 80 chars. By setting
+CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0 the width is unlimited, and
+if x == 0 or is unset one of the fallbacks is used.
+
Usage:
build help [flags] [command/topic ...]
[command/topic ...] optionally identifies a specific sub-command or help topic.
-The help flags are:
- -style=text: The formatting style for help output, either "text" or "godoc".
+The build help flags are:
+ -style=text
+ The formatting style for help output, either "text" or "godoc".
*/
package main
diff --git a/tools/build/impl.go b/tools/build/impl.go
index a4b9b87..e03779f 100644
--- a/tools/build/impl.go
+++ b/tools/build/impl.go
@@ -6,13 +6,12 @@
"io/ioutil"
"os"
"path/filepath"
- "runtime"
+ goruntime "runtime"
"strings"
"time"
"veyron.io/lib/cmdline"
"veyron.io/veyron/veyron2/context"
- "veyron.io/veyron/veyron2/rt"
vbuild "veyron.io/veyron/veyron2/services/mgmt/build"
)
@@ -22,8 +21,8 @@
)
func init() {
- cmdBuild.Flags.StringVar(&flagArch, "arch", runtime.GOARCH, "Target architecture.")
- cmdBuild.Flags.StringVar(&flagOS, "os", runtime.GOOS, "Target operating system.")
+ cmdBuild.Flags.StringVar(&flagArch, "arch", goruntime.GOARCH, "Target architecture.")
+ cmdBuild.Flags.StringVar(&flagOS, "os", goruntime.GOOS, "Target operating system.")
}
var cmdRoot = &cmdline.Command{
@@ -140,7 +139,6 @@
binaries := make(chan vbuild.File)
go func() {
defer close(binaries)
- rt.Init()
client := vbuild.BuilderClient(name)
stream, err := client.Build(ctx, vbuild.Architecture(flagArch), vbuild.OperatingSystem(flagOS))
if err != nil {
@@ -211,7 +209,7 @@
cancel, errchan := make(chan struct{}), make(chan error)
defer close(errchan)
- ctx, ctxCancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, ctxCancel := runtime.NewContext().WithTimeout(time.Minute)
defer ctxCancel()
// Start all stages of the pipeline.
diff --git a/tools/build/impl_test.go b/tools/build/impl_test.go
index 2778896..4f4d3f7 100644
--- a/tools/build/impl_test.go
+++ b/tools/build/impl_test.go
@@ -5,6 +5,7 @@
"strings"
"testing"
+ "veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/rt"
@@ -39,8 +40,8 @@
type dispatcher struct{}
-func startServer(t *testing.T) (ipc.Server, naming.Endpoint) {
- server, err := rt.R().NewServer()
+func startServer(runtime veyron2.Runtime, t *testing.T) (ipc.Server, naming.Endpoint) {
+ server, err := runtime.NewServer()
if err != nil {
t.Fatalf("NewServer failed: %v", err)
}
@@ -62,8 +63,12 @@
}
func TestBuildClient(t *testing.T) {
- rt.Init()
- server, endpoint := startServer(t)
+ var err error
+ runtime, err = rt.New()
+ if err != nil {
+ t.Fatalf("Unexpected error initializing runtime: %s", err)
+ }
+ server, endpoint := startServer(runtime, t)
defer stopServer(t, server)
cmd := root()
diff --git a/tools/build/main.go b/tools/build/main.go
index 9e5d556..3723981 100644
--- a/tools/build/main.go
+++ b/tools/build/main.go
@@ -1,15 +1,23 @@
// The following enables go generate to generate the doc.go file.
-//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
+//go:generate go run $VEYRON_ROOT/veyron/go/src/veyron.io/lib/cmdline/testdata/gendoc.go .
package main
import (
+ "veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/rt"
_ "veyron.io/veyron/veyron/profiles"
)
+var runtime veyron2.Runtime
+
func main() {
- defer rt.Init().Cleanup()
+ var err error
+ runtime, err = rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
root().Main()
}
diff --git a/tools/debug/doc.go b/tools/debug/doc.go
index e611553..ded0837 100644
--- a/tools/debug/doc.go
+++ b/tools/debug/doc.go
@@ -8,7 +8,8 @@
debug <command>
The debug commands are:
- glob Returns all matching entries from the namespace
+ glob Returns all matching entries from the namespace.
+ vtrace Returns vtrace traces.
logs Accesses log files
stats Accesses stats
pprof Accesses profiling data
@@ -16,15 +17,32 @@
Run "debug help [command]" for command usage.
The global flags are:
- -alsologtostderr=true: log to standard error as well as files
- -log_backtrace_at=:0: when logging hits line file:N, emit a stack trace
- -log_dir=: if non-empty, write log files to this directory
- -logtostderr=false: log to standard error instead of files
- -max_stack_buf_size=4292608: max size in bytes of the buffer to use for logging stack traces
- -stderrthreshold=2: logs at or above this threshold go to stderr
- -v=0: log level for V logs
- -vmodule=: comma-separated list of pattern=N settings for file-filtered logging
- -vv=0: log level for V logs
+ -alsologtostderr=true
+ log to standard error as well as files
+ -log_backtrace_at=:0
+ when logging hits line file:N, emit a stack trace
+ -log_dir=
+ if non-empty, write log files to this directory
+ -logtostderr=false
+ log to standard error instead of files
+ -max_stack_buf_size=4292608
+ max size in bytes of the buffer to use for logging stack traces
+ -stderrthreshold=2
+ logs at or above this threshold go to stderr
+ -v=0
+ log level for V logs
+ -veyron.credentials=
+ directory to use for storing security credentials
+ -veyron.namespace.root=[/proxy.envyor.com:8101]
+ local namespace root; can be repeated to provided multiple roots
+ -veyron.vtrace.cache_size=1024
+ The number of vtrace traces to store in memory.
+ -veyron.vtrace.dump_on_shutdown=false
+ If true, dump all stored traces on runtime shutdown.
+ -veyron.vtrace.sample_rate=0
+ Rate (from 0.0 to 1.0) to sample vtrace traces.
+ -vmodule=
+ comma-separated list of pattern=N settings for file-filtered logging
Debug Glob
@@ -35,6 +53,15 @@
<pattern> is a glob pattern to match.
+Debug Vtrace
+
+Returns matching vtrace traces (or all stored traces if no ids are given).
+
+Usage:
+ debug vtrace <name> [id ...]
+
+<name> is the name of a vtrace object. [id] is a vtrace trace id.
+
Debug Logs
Accesses log files
@@ -42,9 +69,9 @@
Usage:
debug logs <command>
-The logs commands are:
+The debug logs commands are:
read Reads the content of a log file object.
- size Returns the size of the a log file object
+ size Returns the size of a log file object.
Debug Logs Read
@@ -55,15 +82,20 @@
<name> is the name of the log file object.
-The read flags are:
- -f=false: When true, read will wait for new log entries when it reaches the end of the file.
- -n=-1: The number of log entries to read.
- -o=0: The position, in bytes, from which to start reading the log file.
- -v=false: When true, read will be more verbose.
+The debug logs read flags are:
+ -f=false
+ When true, read will wait for new log entries when it reaches the end of the
+ file.
+ -n=-1
+ The number of log entries to read.
+ -o=0
+ The position, in bytes, from which to start reading the log file.
+ -v=false
+ When true, read will be more verbose.
Debug Logs Size
-Returns the size of the a log file object.
+Returns the size of a log file object.
Usage:
debug logs size <name>
@@ -77,35 +109,41 @@
Usage:
debug stats <command>
-The stats commands are:
- value Returns the value of the a stats object
- watchglob Returns a stream of all matching entries and their values
+The debug stats commands are:
+ read Returns the value of stats objects.
+ watch Returns a stream of all matching entries and their values as they
+ change.
-Debug Stats Value
+Debug Stats Read
-Returns the value of the a stats object.
+Returns the value of stats objects.
Usage:
- debug stats value [flags] <name>
+ debug stats read [flags] <name> ...
-<name> is the name of the stats object.
+<name> is the name of a stats object, or a glob pattern to match against stats
+object names.
-The value flags are:
- -raw=false: When true, the command will display the raw value of the object.
- -type=false: When true, the type of the values will be displayed.
+The debug stats read flags are:
+ -raw=false
+ When true, the command will display the raw value of the object.
+ -type=false
+ When true, the type of the values will be displayed.
-Debug Stats Watchglob
+Debug Stats Watch
-Returns a stream of all matching entries and their values
+Returns a stream of all matching entries and their values as they change.
Usage:
- debug stats watchglob [flags] <pattern> ...
+ debug stats watch [flags] <pattern> ...
<pattern> is a glob pattern to match.
-The watchglob flags are:
- -raw=false: When true, the command will display the raw value of the object.
- -type=false: When true, the type of the values will be displayed.
+The debug stats watch flags are:
+ -raw=false
+ When true, the command will display the raw value of the object.
+ -type=false
+ When true, the type of the values will be displayed.
Debug Pprof
@@ -114,31 +152,31 @@
Usage:
debug pprof <command>
-The pprof commands are:
- run Runs the pprof tool
- proxy Runs an http proxy to a pprof object
+The debug pprof commands are:
+ run Runs the pprof tool.
+ proxy Runs an http proxy to a pprof object.
Debug Pprof Run
-Runs the pprof tool
+Runs the pprof tool.
Usage:
debug pprof run [flags] <name> <profile> [passthru args] ...
-<name> is the name of the pprof object.
-<profile> the name of the profile to use.
+<name> is the name of the pprof object. <profile> the name of the profile to
+use.
All the [passthru args] are passed to the pprof tool directly, e.g.
-$ debug pprof run a/b/c heap --text --lines
-$ debug pprof run a/b/c profile -gv
+$ debug pprof run a/b/c heap --text $ debug pprof run a/b/c profile -gv
-The run flags are:
- -pprofcmd=veyron go tool pprof: The pprof command to use.
+The debug pprof run flags are:
+ -pprofcmd=veyron go tool pprof
+ The pprof command to use.
Debug Pprof Proxy
-Runs an http proxy to a pprof object
+Runs an http proxy to a pprof object.
Usage:
debug pprof proxy <name>
@@ -148,15 +186,24 @@
Debug Help
Help with no args displays the usage of the parent command.
+
Help with args displays the usage of the specified sub-command or help topic.
+
"help ..." recursively displays help for all commands and topics.
+The output is formatted to a target width in runes. The target width is
+determined by checking the environment variable CMDLINE_WIDTH, falling back on
+the terminal width from the OS, falling back on 80 chars. By setting
+CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0 the width is unlimited, and
+if x == 0 or is unset one of the fallbacks is used.
+
Usage:
debug help [flags] [command/topic ...]
[command/topic ...] optionally identifies a specific sub-command or help topic.
-The help flags are:
- -style=text: The formatting style for help output, either "text" or "godoc".
+The debug help flags are:
+ -style=text
+ The formatting style for help output, either "text" or "godoc".
*/
package main
diff --git a/tools/debug/impl.go b/tools/debug/impl.go
index f3dc121..6a5a1fa 100644
--- a/tools/debug/impl.go
+++ b/tools/debug/impl.go
@@ -3,7 +3,6 @@
import (
"bytes"
"fmt"
- "io"
"os"
"os/exec"
"regexp"
@@ -19,14 +18,16 @@
istats "veyron.io/veyron/veyron/services/mgmt/stats"
"veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/naming"
- "veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/services/mgmt/logreader"
logtypes "veyron.io/veyron/veyron2/services/mgmt/logreader/types"
"veyron.io/veyron/veyron2/services/mgmt/pprof"
"veyron.io/veyron/veyron2/services/mgmt/stats"
+ vtracesvc "veyron.io/veyron/veyron2/services/mgmt/vtrace"
"veyron.io/veyron/veyron2/services/watch"
watchtypes "veyron.io/veyron/veyron2/services/watch/types"
- "veyron.io/veyron/veyron2/vom"
+ "veyron.io/veyron/veyron2/uniqueid"
+ "veyron.io/veyron/veyron2/vdl/vdlutil"
+ "veyron.io/veyron/veyron2/vtrace"
)
var (
@@ -40,7 +41,7 @@
)
func init() {
- vom.Register(istats.HistogramValue{})
+ vdlutil.Register(istats.HistogramValue{})
// logs read flags
cmdLogsRead.Flags.BoolVar(&follow, "f", false, "When true, read will wait for new log entries when it reaches the end of the file.")
@@ -60,6 +61,81 @@
cmdPProfRun.Flags.StringVar(&pprofCmd, "pprofcmd", "veyron go tool pprof", "The pprof command to use.")
}
+var cmdVtrace = &cmdline.Command{
+ Run: runVtrace,
+ Name: "vtrace",
+ Short: "Returns vtrace traces.",
+ Long: "Returns matching vtrace traces (or all stored traces if no ids are given).",
+ ArgsName: "<name> [id ...]",
+ ArgsLong: `
+<name> is the name of a vtrace object.
+[id] is a vtrace trace id.
+`,
+}
+
+func doFetchTrace(ctx context.T, wg *sync.WaitGroup, client vtracesvc.StoreClientStub,
+ id uniqueid.ID, traces chan *vtrace.TraceRecord, errors chan error) {
+ defer wg.Done()
+
+ trace, err := client.Trace(ctx, id)
+ if err != nil {
+ errors <- err
+ } else {
+ traces <- &trace
+ }
+}
+
+func runVtrace(cmd *cmdline.Command, args []string) error {
+ ctx := runtime.NewContext()
+ arglen := len(args)
+ if arglen == 0 {
+ return cmd.UsageErrorf("vtrace: incorrect number of arguments, got %d want >= 1", arglen)
+ }
+
+ name := args[0]
+ client := vtracesvc.StoreClient(name)
+ if arglen == 1 {
+ call, err := client.AllTraces(ctx)
+ if err != nil {
+ return err
+ }
+ stream := call.RecvStream()
+ for stream.Advance() {
+ trace := stream.Value()
+ vtrace.FormatTrace(os.Stdout, &trace, nil)
+ }
+ if err := stream.Err(); err != nil {
+ return err
+ }
+ return call.Finish()
+ }
+
+ ntraces := len(args) - 1
+ traces := make(chan *vtrace.TraceRecord, ntraces)
+ errors := make(chan error, ntraces)
+ var wg sync.WaitGroup
+ wg.Add(ntraces)
+ for _, arg := range args[1:] {
+ id, err := uniqueid.FromHexString(arg)
+ if err != nil {
+ return err
+ }
+ go doFetchTrace(ctx, &wg, client, id, traces, errors)
+ }
+ go func() {
+ wg.Wait()
+ close(traces)
+ close(errors)
+ }()
+
+ for trace := range traces {
+ vtrace.FormatTrace(os.Stdout, trace, nil)
+ }
+
+ // Just return one of the errors.
+ return <-errors
+}
+
var cmdGlob = &cmdline.Command{
Run: runGlob,
Name: "glob",
@@ -77,7 +153,7 @@
}
results := make(chan naming.MountEntry)
errors := make(chan error)
- doGlobs(rt.R().NewContext(), args, results, errors)
+ doGlobs(runtime.NewContext(), args, results, errors)
var lastErr error
for {
select {
@@ -116,7 +192,7 @@
defer wg.Done()
ctx, cancel := ctx.WithTimeout(time.Minute)
defer cancel()
- c, err := rt.R().Namespace().Glob(ctx, pattern)
+ c, err := runtime.Namespace().Glob(ctx, pattern)
if err != nil {
errors <- fmt.Errorf("%s: %v", pattern, err)
return
@@ -143,7 +219,7 @@
}
name := args[0]
lf := logreader.LogFileClient(name)
- stream, err := lf.ReadLog(rt.R().NewContext(), startPos, int32(numEntries), follow)
+ stream, err := lf.ReadLog(runtime.NewContext(), startPos, int32(numEntries), follow)
if err != nil {
return err
}
@@ -186,7 +262,7 @@
}
name := args[0]
lf := logreader.LogFileClient(name)
- size, err := lf.Size(rt.R().NewContext())
+ size, err := lf.Size(runtime.NewContext())
if err != nil {
return err
}
@@ -210,7 +286,7 @@
if min, got := 1, len(args); got < min {
return cmd.UsageErrorf("read: incorrect number of arguments, got %d, want >=%d", got, min)
}
- ctx := rt.R().NewContext()
+ ctx := runtime.NewContext()
globResults := make(chan naming.MountEntry)
errors := make(chan error)
doGlobs(ctx, args, globResults, errors)
@@ -271,7 +347,7 @@
results := make(chan string)
errors := make(chan error)
- ctx := rt.R().NewContext()
+ ctx := runtime.NewContext()
var wg sync.WaitGroup
wg.Add(len(args))
for _, arg := range args {
@@ -346,40 +422,13 @@
}
switch v := value.(type) {
case istats.HistogramValue:
- writeASCIIHistogram(&buf, v)
+ v.Print(&buf)
default:
fmt.Fprintf(&buf, "%v", v)
}
return buf.String()
}
-func writeASCIIHistogram(w io.Writer, h istats.HistogramValue) {
- scale := h.Count
- if scale > 100 {
- scale = 100
- }
- var avg float64
- if h.Count > 0 {
- avg = float64(h.Sum) / float64(h.Count)
- }
- fmt.Fprintf(w, "Count: %d Sum: %d Avg: %f\n", h.Count, h.Sum, avg)
- for i, b := range h.Buckets {
- var r string
- if i+1 < len(h.Buckets) {
- r = fmt.Sprintf("[%d,%d[", b.LowBound, h.Buckets[i+1].LowBound)
- } else {
- r = fmt.Sprintf("[%d,Inf", b.LowBound)
- }
- fmt.Fprintf(w, "%-18s: ", r)
- if b.Count > 0 && h.Count > 0 {
- fmt.Fprintf(w, "%s %d (%.1f%%)", strings.Repeat("*", int(b.Count*scale/h.Count)), b.Count, 100*float64(b.Count)/float64(h.Count))
- }
- if i+1 < len(h.Buckets) {
- fmt.Fprintln(w)
- }
- }
-}
-
var cmdPProfRun = &cmdline.Command{
Run: runPProf,
Name: "run",
@@ -406,7 +455,7 @@
return showPProfProfiles(cmd, name)
}
profile := args[1]
- listener, err := client.StartProxy(rt.R(), name)
+ listener, err := client.StartProxy(runtime, name)
if err != nil {
return err
}
@@ -429,7 +478,7 @@
}
func showPProfProfiles(cmd *cmdline.Command, name string) error {
- v, err := pprof.PProfClient(name).Profiles(rt.R().NewContext())
+ v, err := pprof.PProfClient(name).Profiles(runtime.NewContext())
if err != nil {
return err
}
@@ -466,7 +515,7 @@
return cmd.UsageErrorf("proxy: incorrect number of arguments, got %d, want %d", got, want)
}
name := args[0]
- listener, err := client.StartProxy(rt.R(), name)
+ listener, err := client.StartProxy(runtime, name)
if err != nil {
return err
}
@@ -477,7 +526,7 @@
fmt.Fprintln(cmd.Stdout())
fmt.Fprintln(cmd.Stdout(), "Hit CTRL-C to exit")
- <-signals.ShutdownOnSignals()
+ <-signals.ShutdownOnSignals(runtime)
return nil
}
@@ -487,6 +536,7 @@
Long: "Command-line tool for interacting with the debug server.",
Children: []*cmdline.Command{
cmdGlob,
+ cmdVtrace,
&cmdline.Command{
Name: "logs",
Short: "Accesses log files",
diff --git a/tools/debug/main.go b/tools/debug/main.go
index 9e5d556..3723981 100644
--- a/tools/debug/main.go
+++ b/tools/debug/main.go
@@ -1,15 +1,23 @@
// The following enables go generate to generate the doc.go file.
-//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
+//go:generate go run $VEYRON_ROOT/veyron/go/src/veyron.io/lib/cmdline/testdata/gendoc.go .
package main
import (
+ "veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/rt"
_ "veyron.io/veyron/veyron/profiles"
)
+var runtime veyron2.Runtime
+
func main() {
- defer rt.Init().Cleanup()
+ var err error
+ runtime, err = rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
root().Main()
}
diff --git a/tools/debug/test.sh b/tools/debug/test.sh
index 8c38207..17a016b 100755
--- a/tools/debug/test.sh
+++ b/tools/debug/test.sh
@@ -8,6 +8,7 @@
source "${VEYRON_ROOT}/scripts/lib/shell_test.sh"
readonly WORKDIR="${shell_test_WORK_DIR}"
+readonly DEBUG_FLAGS="--veyron.vtrace.sample_rate=1"
build() {
DEBUG_BIN="$(shell_test::build_go_binary 'veyron.io/veyron/veyron/tools/debug')"
@@ -35,7 +36,7 @@
# Test top level glob.
local -r DBGLOG="${WORKDIR}/debug.log"
- GOT=$("${DEBUG_BIN}" glob "${EP}/__debug/*" 2> "${DBGLOG}") \
+ GOT=$("${DEBUG_BIN}" "${DEBUG_FLAGS}" glob "${EP}/__debug/*" 2> "${DBGLOG}") \
|| (dumplogs "${DBGLOG}"; shell_test::fail "line ${LINENO}: failed to run debug")
WANT="${EP}/__debug/logs
${EP}/__debug/pprof
@@ -44,28 +45,39 @@
shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
# Test logs glob.
- GOT=$("${DEBUG_BIN}" glob "${EP}/__debug/logs/*" 2> "${DBGLOG}" | wc -l) \
+ GOT=$("${DEBUG_BIN}" "${DEBUG_FLAGS}" glob "${EP}/__debug/logs/*" 2> "${DBGLOG}" | wc -l) \
|| (dumplogs "${DBGLOG}"; shell_test::fail "line ${LINENO}: failed to run debug")
shell_test::assert_gt "${GOT}" "0" "${LINENO}"
# Test logs size.
echo "This is a log file" > "${TMPDIR}/my-test-log-file"
- GOT=$("${DEBUG_BIN}" logs size "${EP}/__debug/logs/my-test-log-file" 2> "${DBGLOG}") \
+ GOT=$("${DEBUG_BIN}" "${DEBUG_FLAGS}" logs size "${EP}/__debug/logs/my-test-log-file" 2> "${DBGLOG}") \
|| (dumplogs "${DBGLOG}"; shell_test::fail "line ${LINENO}: failed to run debug")
WANT=$(echo "This is a log file" | wc -c | tr -d ' ')
shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
# Test logs read.
- GOT=$("${DEBUG_BIN}" logs read "${EP}/__debug/logs/my-test-log-file" 2> "${DBGLOG}") \
+ GOT=$("${DEBUG_BIN}" "${DEBUG_FLAGS}" logs read "${EP}/__debug/logs/my-test-log-file" 2> "${DBGLOG}") \
|| (dumplogs "${DBGLOG}"; shell_test::fail "line ${LINENO}: failed to run debug")
WANT="This is a log file"
shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
# Test stats read.
- GOT=$("${DEBUG_BIN}" stats read "${EP}/__debug/stats/ipc/server/routing-id/*/methods/ReadLog/latency-ms" 2> "${DBGLOG}" | wc -l) \
+ GOT=$("${DEBUG_BIN}" "${DEBUG_FLAGS}" stats read "${EP}/__debug/stats/ipc/server/routing-id/*/methods/ReadLog/latency-ms" 2> "${DBGLOG}" | wc -l) \
|| (dumplogs "${DBGLOG}"; shell_test::fail "line ${LINENO}: failed to run debug")
shell_test::assert_gt "${GOT}" "0" "${LINENO}"
+ # Test fetching all vtrace traces.
+ GOT=$("${DEBUG_BIN}" "${DEBUG_FLAGS}" vtrace "${EP}/__debug/vtrace" 2> "${DBGLOG}" | egrep -o "^Trace - ([^ ]+)" | cut -b 9- | sort) \
+ || (dumplogs "${DBGLOG}"; shell_test::fail "line ${LINENO}: failed to run debug")
+ shell_test::assert_eq $(echo "${GOT}" | wc -l | tr -d ' ') "6" "${LINENO}"
+
+ # Test fetching individual traces.
+ IDS=$(echo "$GOT" | tr '\n' ' ')
+ GOT2=$("${DEBUG_BIN}" "${DEBUG_FLAGS}" vtrace "${EP}/__debug/vtrace" ${IDS} 2> "${DBGLOG}" | egrep -o "^Trace - ([^ ]+)" | cut -b 9- | sort) \
+ || (dumplogs "${DBGLOG}"; shell_test::fail "line ${LINENO}: failed to run debug")
+ shell_test::assert_eq "${GOT2}" "${GOT}" "${LINENO}"
+
# Test stats watch.
local TMP=$(shell::tmp_file)
touch "${TMP}"
@@ -73,7 +85,7 @@
"${DEBUG_BIN}" stats watch -raw "${EP}/__debug/stats/ipc/server/routing-id/*/methods/ReadLog/latency-ms")
shell::timed_wait_for "${shell_test_DEFAULT_MESSAGE_TIMEOUT}" "${TMP}" "ReadLog/latency-ms"
kill "${DEBUG_PID}"
- grep -q "Count:1 " "${TMP}" || (dumplogs "${TMP}"; shell_test::fail "line ${LINENO}: failed to find expected output")
+ grep -q "Count: 1 " "${TMP}" || (dumplogs "${TMP}"; shell_test::fail "line ${LINENO}: failed to find expected output")
# Test pprof.
if ! "${DEBUG_BIN}" pprof run "${EP}/__debug/pprof" heap --text &> "${DBGLOG}"; then
diff --git a/tools/mgmt/nodex/acl_fmt.go b/tools/mgmt/nodex/acl_fmt.go
new file mode 100644
index 0000000..1690edd
--- /dev/null
+++ b/tools/mgmt/nodex/acl_fmt.go
@@ -0,0 +1,84 @@
+package main
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+
+ "veyron.io/veyron/veyron2/security"
+)
+
+// aclEntries maps blessing patterns to the kind of access they should have.
+type aclEntries map[string]accessTags
+
+// accessTags maps access tags to whether they should be blacklisted
+// (i.e., part of the NotIn list) or not (part of the In list).
+//
+// TODO(ashankar,caprita): This structure is not friendly to a blessing
+// appearing in both the "In" and "NotIn" lists of an ACL. Arguably, such an
+// ACL is silly (In: ["foo"], NotIn: ["foo"]), but is legal. This structure can
+// end up hiding that.
+type accessTags map[string]bool
+
+// String representation of access tags. Between String and parseAccessTags,
+// the "get" and "set" commands are able to speak the same language: the output
+// of "get" and be copied/pasted into "set".
+func (tags accessTags) String() string {
+ // Sort tags and then apply "!".
+ var list []string
+ for tag, _ := range tags {
+ list = append(list, tag)
+ }
+ sort.Strings(list)
+ for ix, tag := range list {
+ if tags[tag] {
+ list[ix] = "!" + list[ix]
+ }
+ }
+ return strings.Join(list, ",")
+}
+
+func parseAccessTags(input string) (accessTags, error) {
+ ret := make(accessTags)
+ if input == "^" {
+ return ret, nil
+ }
+ for _, tag := range strings.Split(input, ",") {
+ blacklist := strings.HasPrefix(tag, "!")
+ if blacklist {
+ tag = tag[1:]
+ }
+ if len(tag) == 0 {
+ return nil, fmt.Errorf("empty access tag in %q", input)
+ }
+ ret[tag] = blacklist
+ }
+ return ret, nil
+}
+
+func (entries aclEntries) String() string {
+ var list []string
+ for pattern, _ := range entries {
+ list = append(list, pattern)
+ }
+ sort.Strings(list)
+ for ix, pattern := range list {
+ list[ix] = fmt.Sprintf("%s %v", pattern, entries[pattern])
+ }
+ return strings.Join(list, "\n")
+}
+
+func (entries aclEntries) Tags(pattern string) accessTags {
+ tags, exists := entries[pattern]
+ if !exists {
+ tags = make(accessTags)
+ entries[pattern] = tags
+ }
+ return tags
+}
+
+type byPattern []security.BlessingPattern
+
+func (a byPattern) Len() int { return len(a) }
+func (a byPattern) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a byPattern) Less(i, j int) bool { return a[i] < a[j] }
diff --git a/tools/mgmt/nodex/acl_impl.go b/tools/mgmt/nodex/acl_impl.go
index ca2b0a9..e9d2f7c 100644
--- a/tools/mgmt/nodex/acl_impl.go
+++ b/tools/mgmt/nodex/acl_impl.go
@@ -4,7 +4,6 @@
import (
"fmt"
- "sort"
"veyron.io/lib/cmdline"
"veyron.io/veyron/veyron2/rt"
@@ -25,19 +24,6 @@
application installation or instance.`,
}
-type formattedACLEntry struct {
- blessing string
- inout string
- label string
-}
-
-// Code to make formattedACLEntry sorted.
-type byBlessing []formattedACLEntry
-
-func (a byBlessing) Len() int { return len(a) }
-func (a byBlessing) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-func (a byBlessing) Less(i, j int) bool { return a[i].blessing < a[j].blessing }
-
func runGet(cmd *cmdline.Command, args []string) error {
if expected, got := 1, len(args); expected != got {
return cmd.UsageErrorf("get: incorrect number of arguments, expected %d, got %d", expected, got)
@@ -48,21 +34,17 @@
if err != nil {
return fmt.Errorf("GetACL on %s failed: %v", vanaName, err)
}
-
- // TODO(rjkroege): Update for custom labels.
- output := make([]formattedACLEntry, 0)
- for k, _ := range objACL.In {
- output = append(output, formattedACLEntry{string(k), "", objACL.In[k].String()})
+ // Convert objACL (TaggedACLMap) into aclEntries for pretty printing.
+ entries := make(aclEntries)
+ for tag, acl := range objACL {
+ for _, p := range acl.In {
+ entries.Tags(string(p))[tag] = false
+ }
+ for _, b := range acl.NotIn {
+ entries.Tags(b)[tag] = true
+ }
}
- for k, _ := range objACL.NotIn {
- output = append(output, formattedACLEntry{string(k), "!", objACL.NotIn[k].String()})
- }
-
- sort.Sort(byBlessing(output))
-
- for _, e := range output {
- fmt.Fprintf(cmd.Stdout(), "%s %s%s\n", e.blessing, e.inout, e.label)
- }
+ fmt.Fprintf(cmd.Stdout(), "%v", entries)
return nil
}
@@ -71,32 +53,28 @@
Name: "set",
Short: "Set ACLs for the given target.",
Long: "Set ACLs for the given target",
- ArgsName: "<node manager name> (<blessing> [!]<label>)...",
+ ArgsName: "<node manager name> (<blessing> [!]<tag>(,[!]<tag>)*",
ArgsLong: `
<node manager name> can be a Vanadium name for a node manager,
application installation or instance.
<blessing> is a blessing pattern.
+If the same pattern is repeated multiple times in the command, then
+the only the last occurrence will be honored.
-<label> is a character sequence defining a set of rights: some subset
-of the defined standard Vanadium labels of XRWADM where X is resolve,
-R is read, W for write, A for admin, D for debug and M is for
-monitoring. By default, the combination of <blessing>, <label>
-replaces whatever entry is present in the ACL's In field for the
-<blessing> but it can instead be added to the NotIn field by prefacing
-<label> with a '!' character. Use the <label> of 0 to clear the label.
+<tag> is a subset of defined access types ("Admin", "Read", "Write" etc.).
+If the access right is prefixed with a '!' then <blessing> is added to the
+NotIn list for that right. Using "^" as a "tag" causes all occurrences of
+<blessing> in the current ACL to be cleared.
-For example: root/self !0 will clear the NotIn field for blessingroot/self.`,
-}
+Examples:
+set root/self ^
+will remove "root/self" from the In and NotIn lists for all access rights.
-type inAdditionTuple struct {
- blessing security.BlessingPattern
- ls *security.LabelSet
-}
-
-type notInAdditionTuple struct {
- blessing string
- ls *security.LabelSet
+set root/self Read,!Write
+will add "root/self" to the In list for Read access and the NotIn list
+for Write access (and remove "root/self" from both the In and NotIn
+lists of all other access rights)`,
}
func runSet(cmd *cmdline.Command, args []string) error {
@@ -107,70 +85,32 @@
vanaName := args[0]
pairs := args[1:]
- // Parse each pair and aggregate what should happen to all of them
- notInDeletions := make([]string, 0)
- inDeletions := make([]security.BlessingPattern, 0)
- inAdditions := make([]inAdditionTuple, 0)
- notInAdditions := make([]notInAdditionTuple, 0)
-
+ entries := make(aclEntries)
for i := 0; i < len(pairs); i += 2 {
- blessing, label := pairs[i], pairs[i+1]
- if label == "" || label == "!" {
- return cmd.UsageErrorf("failed to parse LabelSet pair %s, %s", blessing, label)
+ blessing := pairs[i]
+ tags, err := parseAccessTags(pairs[i+1])
+ if err != nil {
+ return cmd.UsageErrorf("failed to parse access tags for %q: %v", blessing, err)
}
-
- switch {
- case label == "!0":
- notInDeletions = append(notInDeletions, blessing)
- case label == "0":
- inDeletions = append(inDeletions, security.BlessingPattern(blessing))
- case label[0] == '!':
- // e.g. !RW
- ls := new(security.LabelSet)
- if err := ls.FromString(label[1:]); err != nil {
- return cmd.UsageErrorf("failed to parse LabelSet %s: %v", label, err)
- }
- notInAdditions = append(notInAdditions, notInAdditionTuple{blessing, ls})
- default:
- // e.g. X
- ls := new(security.LabelSet)
- if err := ls.FromString(label); err != nil {
- return fmt.Errorf("failed to parse LabelSet %s: %v", label, err)
- }
- inAdditions = append(inAdditions, inAdditionTuple{security.BlessingPattern(blessing), ls})
- }
+ entries[blessing] = tags
}
- // Set the ACLs on the specified name.
+ // Set the ACLs on the specified names.
for {
objACL, etag, err := node.ApplicationClient(vanaName).GetACL(rt.R().NewContext())
if err != nil {
return cmd.UsageErrorf("GetACL(%s) failed: %v", vanaName, err)
}
-
- // Insert into objACL
- for _, b := range notInDeletions {
- if _, contains := objACL.NotIn[b]; !contains {
- fmt.Fprintf(cmd.Stderr(), "WARNING: ignoring attempt to remove non-existing NotIn ACL for %s\n", b)
+ for blessingOrPattern, tags := range entries {
+ objACL.Clear(blessingOrPattern) // Clear out any existing references
+ for tag, blacklist := range tags {
+ if blacklist {
+ objACL.Blacklist(blessingOrPattern, tag)
+ } else {
+ objACL.Add(security.BlessingPattern(blessingOrPattern), tag)
+ }
}
- delete(objACL.NotIn, b)
}
-
- for _, b := range inDeletions {
- if _, contains := objACL.In[b]; !contains {
- fmt.Fprintf(cmd.Stderr(), "WARNING: ignoring attempt to remove non-existing In ACL for %s\n", b)
- }
- delete(objACL.In, b)
- }
-
- for _, b := range inAdditions {
- objACL.In[b.blessing] = *b.ls
- }
-
- for _, b := range notInAdditions {
- objACL.NotIn[b.blessing] = *b.ls
- }
-
switch err := node.ApplicationClient(vanaName).SetACL(rt.R().NewContext(), objACL, etag); {
case err != nil && !verror.Is(err, access.ErrBadEtag):
return cmd.UsageErrorf("SetACL(%s) failed: %v", vanaName, err)
diff --git a/tools/mgmt/nodex/acl_test.go b/tools/mgmt/nodex/acl_test.go
index 9f4d581..25aec7e 100644
--- a/tools/mgmt/nodex/acl_test.go
+++ b/tools/mgmt/nodex/acl_test.go
@@ -4,6 +4,7 @@
"bytes"
"fmt"
"reflect"
+ "regexp"
"strings"
"testing"
@@ -29,30 +30,33 @@
cmd.Init(nil, &stdout, &stderr)
nodeName := naming.JoinAddressName(endpoint.String(), "")
- // Test the 'list' command.
+ // Test the 'get' command.
tape.SetResponses([]interface{}{GetACLResponse{
- acl: security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{
- "root/self/...": security.AllLabels,
- "root/other": security.LabelSet(security.ReadLabel),
+ acl: access.TaggedACLMap{
+ "Admin": access.ACL{
+ In: []security.BlessingPattern{"self/..."},
+ NotIn: []string{"self/bad"},
},
- NotIn: map[string]security.LabelSet{
- "root/bob/...": security.LabelSet(security.WriteLabel),
+ "Read": access.ACL{
+ In: []security.BlessingPattern{"other", "self/..."},
},
},
etag: "anEtagForToday",
err: nil,
- },
- })
+ }})
if err := cmd.Execute([]string{"acl", "get", nodeName}); err != nil {
- t.Fatalf("%v, ouput: %v, error: %v", err)
+ t.Fatalf("%v, output: %v, error: %v", err)
}
- if expected, got := "root/bob/... !W\nroot/other R\nroot/self/... XRWADM", strings.TrimSpace(stdout.String()); got != expected {
- t.Fatalf("Unexpected output from get. Got %q, expected %q", got, expected)
+ if expected, got := strings.TrimSpace(`
+other Read
+self/... Admin,Read
+self/bad !Admin
+`), strings.TrimSpace(stdout.String()); got != expected {
+ t.Errorf("Unexpected output from get. Got %q, expected %q", got, expected)
}
if got, expected := tape.Play(), []interface{}{"GetACL"}; !reflect.DeepEqual(expected, got) {
- t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+ t.Errorf("invalid call sequence. Got %#v, want %#v", got, expected)
}
tape.Rewind()
stdout.Reset()
@@ -104,21 +108,21 @@
if err := cmd.Execute([]string{"acl", "set", nodeName, "foo", "!"}); err == nil {
t.Fatalf("failed to detect invalid parameter")
}
- if expected, got := "ERROR: failed to parse LabelSet pair foo, !", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
- t.Fatalf("Unexpected output from list. Got %q, expected prefix %q", got, expected)
+ if expected, got := "ERROR: failed to parse access tags for \"foo\": empty access tag", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
+ t.Errorf("Unexpected output from list. Got %q, expected prefix %q", got, expected)
}
// Correct operation in the absence of errors.
stderr.Reset()
stdout.Reset()
tape.SetResponses([]interface{}{GetACLResponse{
- acl: security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{
- "root/self/...": security.AllLabels,
- "root/other": security.LabelSet(security.ReadLabel),
+ acl: access.TaggedACLMap{
+ "Admin": access.ACL{
+ In: []security.BlessingPattern{"self/..."},
},
- NotIn: map[string]security.LabelSet{
- "root/bob": security.LabelSet(security.WriteLabel),
+ "Read": access.ACL{
+ In: []security.BlessingPattern{"other/...", "self/..."},
+ NotIn: []string{"other/bob"},
},
},
etag: "anEtagForToday",
@@ -126,14 +130,13 @@
},
verror.Make(access.ErrBadEtag, fmt.Sprintf("etag mismatch in:%s vers:%s", "anEtagForToday", "anEtagForTomorrow")),
GetACLResponse{
- acl: security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{
- "root/self/...": security.AllLabels,
- "root/other": security.LabelSet(security.ReadLabel),
+ acl: access.TaggedACLMap{
+ "Admin": access.ACL{
+ In: []security.BlessingPattern{"self/..."},
},
- NotIn: map[string]security.LabelSet{
- "root/bob": security.LabelSet(security.WriteLabel),
- "root/alice/cat": security.LabelSet(security.AdminLabel),
+ "Read": access.ACL{
+ In: []security.BlessingPattern{"other/...", "self/..."},
+ NotIn: []string{"other/bob/baddevice"},
},
},
etag: "anEtagForTomorrow",
@@ -142,7 +145,20 @@
nil,
})
- if err := cmd.Execute([]string{"acl", "set", nodeName, "root/vana/bad", "!XR", "root/vana/...", "RD", "root/other", "0", "root/bob", "!0"}); err != nil {
+ // set command that:
+ // - Adds entry for "friends/..." to "Write" & "Admin"
+ // - Adds a blacklist entry for "friend/alice" for "Admin"
+ // - Edits existing entry for "self/..." (adding "Write" access)
+ // - Removes entry for "other/bob/baddevice"
+ if err := cmd.Execute([]string{
+ "acl",
+ "set",
+ nodeName,
+ "friends/...", "Admin,Write",
+ "friends/alice", "!Admin,Write",
+ "self/...", "Admin,Write,Read",
+ "other/bob/baddevice", "^",
+ }); err != nil {
t.Fatalf("SetACL failed: %v", err)
}
@@ -152,18 +168,22 @@
if expected, got := "WARNING: trying again because of asynchronous change", strings.TrimSpace(stderr.String()); got != expected {
t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
}
-
expected := []interface{}{
"GetACL",
SetACLStimulus{
fun: "SetACL",
- acl: security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{
- "root/self/...": security.AllLabels,
- "root/vana/...": security.LabelSet(security.ReadLabel | security.DebugLabel),
+ acl: access.TaggedACLMap{
+ "Admin": access.ACL{
+ In: []security.BlessingPattern{"friends/...", "self/..."},
+ NotIn: []string{"friends/alice"},
},
- NotIn: map[string]security.LabelSet{
- "root/vana/bad": security.LabelSet(security.ResolveLabel | security.ReadLabel),
+ "Read": access.ACL{
+ In: []security.BlessingPattern{"other/...", "self/..."},
+ NotIn: []string{"other/bob"},
+ },
+ "Write": access.ACL{
+ In: []security.BlessingPattern{"friends/...", "friends/alice", "self/..."},
+ NotIn: []string{},
},
},
etag: "anEtagForToday",
@@ -171,14 +191,18 @@
"GetACL",
SetACLStimulus{
fun: "SetACL",
- acl: security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{
- "root/self/...": security.AllLabels,
- "root/vana/...": security.LabelSet(security.ReadLabel | security.DebugLabel),
+ acl: access.TaggedACLMap{
+ "Admin": access.ACL{
+ In: []security.BlessingPattern{"friends/...", "self/..."},
+ NotIn: []string{"friends/alice"},
},
- NotIn: map[string]security.LabelSet{
- "root/alice/cat": security.LabelSet(security.AdminLabel),
- "root/vana/bad": security.LabelSet(security.ResolveLabel | security.ReadLabel),
+ "Read": access.ACL{
+ In: []security.BlessingPattern{"other/...", "self/..."},
+ NotIn: []string{},
+ },
+ "Write": access.ACL{
+ In: []security.BlessingPattern{"friends/...", "friends/alice", "self/..."},
+ NotIn: []string{},
},
},
etag: "anEtagForTomorrow",
@@ -186,7 +210,7 @@
}
if got := tape.Play(); !reflect.DeepEqual(expected, got) {
- t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+ t.Errorf("invalid call sequence. Got %#v, want %#v", got, expected)
}
tape.Rewind()
stdout.Reset()
@@ -194,17 +218,17 @@
// GetACL fails.
tape.SetResponses([]interface{}{GetACLResponse{
- acl: security.ACL{In: nil, NotIn: nil},
+ acl: access.TaggedACLMap{},
etag: "anEtagForToday",
err: verror.BadArgf("oops!"),
},
})
- if err := cmd.Execute([]string{"acl", "set", nodeName, "root/vana/bad", "!XR", "root/vana/...", "RD", "root/other", "0", "root/bob", "!0"}); err == nil {
+ if err := cmd.Execute([]string{"acl", "set", nodeName, "vana/bad", "Read"}); err == nil {
t.Fatalf("GetACL RPC inside acl set command failed but error wrongly not detected")
}
- if expected, got := "ERROR: GetACL("+nodeName+") failed: oops!", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
- t.Fatalf("Unexpected output from list. Got %q, prefix %q", got, expected)
+ if expected, got := `^ERROR: GetACL\(`+nodeName+`\) failed:.*oops!`, strings.TrimSpace(stderr.String()); !regexp.MustCompile(expected).MatchString(got) {
+ t.Fatalf("Unexpected output from list. Got %q, regexp %q", got, expected)
}
if expected, got := "", strings.TrimSpace(stdout.String()); got != expected {
t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
@@ -214,21 +238,17 @@
}
if got := tape.Play(); !reflect.DeepEqual(expected, got) {
- t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+ t.Errorf("invalid call sequence. Got %#v, want %#v", got, expected)
}
tape.Rewind()
stdout.Reset()
stderr.Reset()
- // SetACL fails with not a bad etag failure.
+ // SetACL fails with something other than a bad etag failure.
tape.SetResponses([]interface{}{GetACLResponse{
- acl: security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{
- "root/self/...": security.AllLabels,
- "root/other": security.LabelSet(security.ReadLabel),
- },
- NotIn: map[string]security.LabelSet{
- "root/bob": security.LabelSet(security.WriteLabel),
+ acl: access.TaggedACLMap{
+ "Read": access.ACL{
+ In: []security.BlessingPattern{"other", "self/..."},
},
},
etag: "anEtagForToday",
@@ -237,27 +257,24 @@
verror.BadArgf("oops!"),
})
- if err := cmd.Execute([]string{"acl", "set", nodeName, "root/vana/bad", "!XR", "root/vana/...", "RD", "root/other", "0", "root/bob", "!0"}); err == nil {
+ if err := cmd.Execute([]string{"acl", "set", nodeName, "friend", "Read"}); err == nil {
t.Fatalf("SetACL should have failed: %v", err)
}
if expected, got := "", strings.TrimSpace(stdout.String()); got != expected {
t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
}
- if expected, got := "ERROR: SetACL("+nodeName+") failed: oops!", strings.TrimSpace(stderr.String()); !strings.HasPrefix(got, expected) {
- t.Fatalf("Unexpected output from list. Got %q, prefix %q", got, expected)
+ if expected, got := `^ERROR: SetACL\(`+nodeName+`\) failed:.*oops!`, strings.TrimSpace(stderr.String()); !regexp.MustCompile(expected).MatchString(got) {
+ t.Fatalf("Unexpected output from list. Got %q, regexp %q", got, expected)
}
expected = []interface{}{
"GetACL",
SetACLStimulus{
fun: "SetACL",
- acl: security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{
- "root/self/...": security.AllLabels,
- "root/vana/...": security.LabelSet(security.ReadLabel | security.DebugLabel),
- },
- NotIn: map[string]security.LabelSet{
- "root/vana/bad": security.LabelSet(security.ResolveLabel | security.ReadLabel),
+ acl: access.TaggedACLMap{
+ "Read": access.ACL{
+ In: []security.BlessingPattern{"friend", "other", "self/..."},
+ NotIn: []string{},
},
},
etag: "anEtagForToday",
@@ -265,46 +282,7 @@
}
if got := tape.Play(); !reflect.DeepEqual(expected, got) {
- t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
- }
- tape.Rewind()
- stdout.Reset()
- stderr.Reset()
-
- // Trying to delete non-existent items.
- stderr.Reset()
- stdout.Reset()
- tape.SetResponses([]interface{}{GetACLResponse{
- acl: security.ACL{
- In: map[security.BlessingPattern]security.LabelSet{},
- NotIn: map[string]security.LabelSet{},
- },
- etag: "anEtagForToday",
- err: nil,
- },
- nil,
- })
-
- if err := cmd.Execute([]string{"acl", "set", nodeName, "root/vana/notin/missing", "!0", "root/vana/in/missing", "0"}); err != nil {
- t.Fatalf("SetACL failed: %v", err)
- }
- if expected, got := "", strings.TrimSpace(stdout.String()); got != expected {
- t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
- }
- if expected, got := "WARNING: ignoring attempt to remove non-existing NotIn ACL for root/vana/notin/missing\nWARNING: ignoring attempt to remove non-existing In ACL for root/vana/in/missing", strings.TrimSpace(stderr.String()); got != expected {
- t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
- }
-
- expected = []interface{}{
- "GetACL",
- SetACLStimulus{
- fun: "SetACL",
- acl: security.ACL{},
- etag: "anEtagForToday",
- },
- }
- if got := tape.Play(); !reflect.DeepEqual(expected, got) {
- t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+ t.Errorf("invalid call sequence. Got %#v, want %#v", got, expected)
}
tape.Rewind()
stdout.Reset()
diff --git a/tools/mgmt/nodex/doc.go b/tools/mgmt/nodex/doc.go
index 6b3ff6d..2ea903d 100644
--- a/tools/mgmt/nodex/doc.go
+++ b/tools/mgmt/nodex/doc.go
@@ -47,8 +47,6 @@
Rate (from 0.0 to 1.0) to sample vtrace traces.
-vmodule=
comma-separated list of pattern=N settings for file-filtered logging
- -vv=0
- log level for V logs
Nodex Install
diff --git a/tools/mgmt/nodex/main.go b/tools/mgmt/nodex/main.go
index 9e5d556..b0e270d 100644
--- a/tools/mgmt/nodex/main.go
+++ b/tools/mgmt/nodex/main.go
@@ -1,5 +1,5 @@
// The following enables go generate to generate the doc.go file.
-//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
+//go:generate go run $VEYRON_ROOT/veyron/go/src/veyron.io/lib/cmdline/testdata/gendoc.go .
package main
diff --git a/tools/mgmt/nodex/nodemanager_mock_test.go b/tools/mgmt/nodex/nodemanager_mock_test.go
index 0fa29fd..6449110 100644
--- a/tools/mgmt/nodex/nodemanager_mock_test.go
+++ b/tools/mgmt/nodex/nodemanager_mock_test.go
@@ -10,6 +10,7 @@
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/services/mgmt/binary"
"veyron.io/veyron/veyron2/services/mgmt/node"
+ "veyron.io/veyron/veyron2/services/security/access"
"veyron.io/veyron/veyron2/vlog"
"veyron.io/veyron/veyron/profiles"
@@ -92,18 +93,18 @@
// Mock ACL getting and setting
type GetACLResponse struct {
- acl security.ACL
+ acl access.TaggedACLMap
etag string
err error
}
type SetACLStimulus struct {
fun string
- acl security.ACL
+ acl access.TaggedACLMap
etag string
}
-func (mni *mockNodeInvoker) SetACL(_ ipc.ServerContext, acl security.ACL, etag string) error {
+func (mni *mockNodeInvoker) SetACL(_ ipc.ServerContext, acl access.TaggedACLMap, etag string) error {
ri := mni.tape.Record(SetACLStimulus{"SetACL", acl, etag})
switch r := ri.(type) {
case nil:
@@ -115,7 +116,7 @@
return nil
}
-func (mni *mockNodeInvoker) GetACL(ipc.ServerContext) (security.ACL, string, error) {
+func (mni *mockNodeInvoker) GetACL(ipc.ServerContext) (access.TaggedACLMap, string, error) {
ir := mni.tape.Record("GetACL")
r := ir.(GetACLResponse)
return r.acl, r.etag, r.err
diff --git a/tools/mounttable/doc.go b/tools/mounttable/doc.go
index fc63e5a..6e8eb92 100644
--- a/tools/mounttable/doc.go
+++ b/tools/mounttable/doc.go
@@ -16,24 +16,41 @@
Run "mounttable help [command]" for command usage.
The global flags are:
- -alsologtostderr=true: log to standard error as well as files
- -log_backtrace_at=:0: when logging hits line file:N, emit a stack trace
- -log_dir=: if non-empty, write log files to this directory
- -logtostderr=false: log to standard error instead of files
- -max_stack_buf_size=4292608: max size in bytes of the buffer to use for logging stack traces
- -stderrthreshold=2: logs at or above this threshold go to stderr
- -v=0: log level for V logs
- -vmodule=: comma-separated list of pattern=N settings for file-filtered logging
- -vv=0: log level for V logs
+ -alsologtostderr=true
+ log to standard error as well as files
+ -log_backtrace_at=:0
+ when logging hits line file:N, emit a stack trace
+ -log_dir=
+ if non-empty, write log files to this directory
+ -logtostderr=false
+ log to standard error instead of files
+ -max_stack_buf_size=4292608
+ max size in bytes of the buffer to use for logging stack traces
+ -stderrthreshold=2
+ logs at or above this threshold go to stderr
+ -v=0
+ log level for V logs
+ -veyron.credentials=
+ directory to use for storing security credentials
+ -veyron.namespace.root=[/proxy.envyor.com:8101]
+ local namespace root; can be repeated to provided multiple roots
+ -veyron.vtrace.cache_size=1024
+ The number of vtrace traces to store in memory.
+ -veyron.vtrace.dump_on_shutdown=false
+ If true, dump all stored traces on runtime shutdown.
+ -veyron.vtrace.sample_rate=0
+ Rate (from 0.0 to 1.0) to sample vtrace traces.
+ -vmodule=
+ comma-separated list of pattern=N settings for file-filtered logging
Mounttable Glob
returns all matching entries in the mount table
Usage:
- mounttable glob <mount name> <pattern>
+ mounttable glob [<mount name>] <pattern>
-<mount name> is a mount name on a mount table.
+<mount name> is a mount name on a mount table. Defaults to namespace root.
<pattern> is a glob pattern that is matched against all the entries below the
specified mount name.
@@ -44,10 +61,10 @@
Usage:
mounttable mount <mount name> <name> <ttl>
-<mount name> is a mount name on a mount table.
-<name> is the rooted object name of the server.
-<ttl> is the TTL of the new entry. It is a decimal number followed by a unit
-suffix (s, m, h). A value of 0s represents an infinite duration.
+<mount name> is a mount name on a mount table. <name> is the rooted object name
+of the server. <ttl> is the TTL of the new entry. It is a decimal number
+followed by a unit suffix (s, m, h). A value of 0s represents an infinite
+duration.
Mounttable Unmount
@@ -56,8 +73,8 @@
Usage:
mounttable unmount <mount name> <name>
-<mount name> is a mount name on a mount table.
-<name> is the rooted object name of the server.
+<mount name> is a mount name on a mount table. <name> is the rooted object name
+of the server.
Mounttable Resolvestep
@@ -71,15 +88,24 @@
Mounttable Help
Help with no args displays the usage of the parent command.
+
Help with args displays the usage of the specified sub-command or help topic.
+
"help ..." recursively displays help for all commands and topics.
+The output is formatted to a target width in runes. The target width is
+determined by checking the environment variable CMDLINE_WIDTH, falling back on
+the terminal width from the OS, falling back on 80 chars. By setting
+CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0 the width is unlimited, and
+if x == 0 or is unset one of the fallbacks is used.
+
Usage:
mounttable help [flags] [command/topic ...]
[command/topic ...] optionally identifies a specific sub-command or help topic.
-The help flags are:
- -style=text: The formatting style for help output, either "text" or "godoc".
+The mounttable help flags are:
+ -style=text
+ The formatting style for help output, either "text" or "godoc".
*/
package main
diff --git a/tools/mounttable/impl.go b/tools/mounttable/impl.go
index efa69fc..6afca98 100644
--- a/tools/mounttable/impl.go
+++ b/tools/mounttable/impl.go
@@ -8,12 +8,11 @@
"veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/options"
- "veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/services/mounttable"
)
func bindMT(ctx context.T, name string) (mounttable.MountTableClientMethods, error) {
- e, err := rt.R().Namespace().ResolveToMountTableX(ctx, name)
+ e, err := runtime.Namespace().ResolveToMountTableX(ctx, name)
if err != nil {
return nil, err
}
@@ -48,7 +47,7 @@
if expected, got := 2, len(args); expected != got {
return cmd.UsageErrorf("glob: incorrect number of arguments, expected %d, got %d", expected, got)
}
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
c, err := bindMT(ctx, args[0])
if err != nil {
@@ -117,9 +116,9 @@
}
}
}
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
- call, err := rt.R().Client().StartCall(ctx, args[0], "Mount", []interface{}{args[1], seconds, 0}, options.NoResolve(true))
+ call, err := runtime.Client().StartCall(ctx, args[0], "Mount", []interface{}{args[1], seconds, 0}, options.NoResolve(true))
if err != nil {
return err
}
@@ -147,9 +146,9 @@
if expected, got := 2, len(args); expected != got {
return cmd.UsageErrorf("unmount: incorrect number of arguments, expected %d, got %d", expected, got)
}
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
- call, err := rt.R().Client().StartCall(ctx, args[0], "Unmount", []interface{}{args[1]}, options.NoResolve(true))
+ call, err := runtime.Client().StartCall(ctx, args[0], "Unmount", []interface{}{args[1]}, options.NoResolve(true))
if err != nil {
return err
}
@@ -176,9 +175,9 @@
if expected, got := 1, len(args); expected != got {
return cmd.UsageErrorf("mount: incorrect number of arguments, expected %d, got %d", expected, got)
}
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
- call, err := rt.R().Client().StartCall(ctx, args[0], "ResolveStepX", []interface{}{}, options.NoResolve(true))
+ call, err := runtime.Client().StartCall(ctx, args[0], "ResolveStepX", []interface{}{}, options.NoResolve(true))
if err != nil {
return err
}
diff --git a/tools/mounttable/impl_test.go b/tools/mounttable/impl_test.go
index 90e83e2..9c59eb6 100644
--- a/tools/mounttable/impl_test.go
+++ b/tools/mounttable/impl_test.go
@@ -85,7 +85,13 @@
}
func TestMountTableClient(t *testing.T) {
- runtime := rt.Init()
+ var err error
+ runtime, err = rt.New()
+ if err != nil {
+ t.Fatalf("Unexpected error initializing runtime: %s", err)
+ }
+ defer runtime.Cleanup()
+
server, endpoint, err := startServer(t, runtime)
if err != nil {
return
diff --git a/tools/mounttable/main.go b/tools/mounttable/main.go
index 9e5d556..3723981 100644
--- a/tools/mounttable/main.go
+++ b/tools/mounttable/main.go
@@ -1,15 +1,23 @@
// The following enables go generate to generate the doc.go file.
-//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
+//go:generate go run $VEYRON_ROOT/veyron/go/src/veyron.io/lib/cmdline/testdata/gendoc.go .
package main
import (
+ "veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/rt"
_ "veyron.io/veyron/veyron/profiles"
)
+var runtime veyron2.Runtime
+
func main() {
- defer rt.Init().Cleanup()
+ var err error
+ runtime, err = rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
root().Main()
}
diff --git a/tools/namespace/doc.go b/tools/namespace/doc.go
index addc28d..a6e8159 100644
--- a/tools/namespace/doc.go
+++ b/tools/namespace/doc.go
@@ -4,9 +4,10 @@
/*
The namespace tool facilitates interaction with the Veyron namespace.
-The namespace roots are set from environment variables that have a name
-starting with NAMESPACE_ROOT, e.g. NAMESPACE_ROOT, NAMESPACE_ROOT_2,
-NAMESPACE_ROOT_GOOGLE, etc.
+The namespace roots are set from the command line via veyron.namespace.root
+options or from environment variables that have a name starting with
+NAMESPACE_ROOT, e.g. NAMESPACE_ROOT, NAMESPACE_ROOT_2, NAMESPACE_ROOT_GOOGLE,
+etc. The command line options override the environment.
Usage:
namespace <command>
@@ -22,15 +23,32 @@
Run "namespace help [command]" for command usage.
The global flags are:
- -alsologtostderr=true: log to standard error as well as files
- -log_backtrace_at=:0: when logging hits line file:N, emit a stack trace
- -log_dir=: if non-empty, write log files to this directory
- -logtostderr=false: log to standard error instead of files
- -max_stack_buf_size=4292608: max size in bytes of the buffer to use for logging stack traces
- -stderrthreshold=2: logs at or above this threshold go to stderr
- -v=0: log level for V logs
- -vmodule=: comma-separated list of pattern=N settings for file-filtered logging
- -vv=0: log level for V logs
+ -alsologtostderr=true
+ log to standard error as well as files
+ -log_backtrace_at=:0
+ when logging hits line file:N, emit a stack trace
+ -log_dir=
+ if non-empty, write log files to this directory
+ -logtostderr=false
+ log to standard error instead of files
+ -max_stack_buf_size=4292608
+ max size in bytes of the buffer to use for logging stack traces
+ -stderrthreshold=2
+ logs at or above this threshold go to stderr
+ -v=0
+ log level for V logs
+ -veyron.credentials=
+ directory to use for storing security credentials
+ -veyron.namespace.root=[/proxy.envyor.com:8101]
+ local namespace root; can be repeated to provided multiple roots
+ -veyron.vtrace.cache_size=1024
+ The number of vtrace traces to store in memory.
+ -veyron.vtrace.dump_on_shutdown=false
+ If true, dump all stored traces on runtime shutdown.
+ -veyron.vtrace.sample_rate=0
+ Rate (from 0.0 to 1.0) to sample vtrace traces.
+ -vmodule=
+ comma-separated list of pattern=N settings for file-filtered logging
Namespace Glob
@@ -49,10 +67,10 @@
Usage:
namespace mount <name> <server> <ttl>
-<name> is the name to add to the namespace.
-<server> is the object address of the server to add.
-<ttl> is the TTL of the new entry. It is a decimal number followed by a unit
-suffix (s, m, h). A value of 0s represents an infinite duration.
+<name> is the name to add to the namespace. <server> is the object address of
+the server to add. <ttl> is the TTL of the new entry. It is a decimal number
+followed by a unit suffix (s, m, h). A value of 0s represents an infinite
+duration.
Namespace Unmount
@@ -61,8 +79,8 @@
Usage:
namespace unmount <name> <server>
-<name> is the name to remove from the namespace.
-<server> is the object address of the server to remove.
+<name> is the name to remove from the namespace. <server> is the object address
+of the server to remove.
Namespace Resolve
@@ -94,15 +112,24 @@
Namespace Help
Help with no args displays the usage of the parent command.
+
Help with args displays the usage of the specified sub-command or help topic.
+
"help ..." recursively displays help for all commands and topics.
+The output is formatted to a target width in runes. The target width is
+determined by checking the environment variable CMDLINE_WIDTH, falling back on
+the terminal width from the OS, falling back on 80 chars. By setting
+CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0 the width is unlimited, and
+if x == 0 or is unset one of the fallbacks is used.
+
Usage:
namespace help [flags] [command/topic ...]
[command/topic ...] optionally identifies a specific sub-command or help topic.
-The help flags are:
- -style=text: The formatting style for help output, either "text" or "godoc".
+The namespace help flags are:
+ -style=text
+ The formatting style for help output, either "text" or "godoc".
*/
package main
diff --git a/tools/namespace/impl.go b/tools/namespace/impl.go
index a1f1f47..b4c1e03 100644
--- a/tools/namespace/impl.go
+++ b/tools/namespace/impl.go
@@ -6,7 +6,6 @@
"veyron.io/lib/cmdline"
"veyron.io/veyron/veyron2/naming"
- "veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/vlog"
)
@@ -27,8 +26,8 @@
return cmd.UsageErrorf("glob: incorrect number of arguments, expected %d, got %d", expected, got)
}
pattern := args[0]
- ns := rt.R().Namespace()
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ns := runtime.Namespace()
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
c, err := ns.Glob(ctx, pattern)
if err != nil {
@@ -40,6 +39,10 @@
for _, s := range res.Servers {
fmt.Fprintf(cmd.Stdout(), " %s (Expires %s)", s.Server, s.Expires)
}
+ if res.Error != nil {
+ fmt.Fprintln(cmd.Stdout())
+ fmt.Fprintf(cmd.Stdout(), "result error: %v", res.Error)
+ }
fmt.Fprintln(cmd.Stdout())
}
return nil
@@ -71,8 +74,8 @@
if err != nil {
return fmt.Errorf("TTL parse error: %v", err)
}
- ns := rt.R().Namespace()
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ns := runtime.Namespace()
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
if err = ns.Mount(ctx, name, server, ttl); err != nil {
vlog.Infof("ns.Mount(%q, %q, %s) failed: %v", name, server, ttl, err)
@@ -100,8 +103,8 @@
}
name := args[0]
server := args[1]
- ns := rt.R().Namespace()
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ns := runtime.Namespace()
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
if err := ns.Unmount(ctx, name, server); err != nil {
vlog.Infof("ns.Unmount(%q, %q) failed: %v", name, server, err)
@@ -125,8 +128,8 @@
return cmd.UsageErrorf("resolve: incorrect number of arguments, expected %d, got %d", expected, got)
}
name := args[0]
- ns := rt.R().Namespace()
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ns := runtime.Namespace()
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
servers, err := ns.Resolve(ctx, name)
if err != nil {
@@ -153,8 +156,8 @@
return cmd.UsageErrorf("resolvetomt: incorrect number of arguments, expected %d, got %d", expected, got)
}
name := args[0]
- ns := rt.R().Namespace()
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ns := runtime.Namespace()
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
e, err := ns.ResolveToMountTableX(ctx, name)
if err != nil {
@@ -181,8 +184,8 @@
return cmd.UsageErrorf("unresolve: incorrect number of arguments, expected %d, got %d", expected, got)
}
name := args[0]
- ns := rt.R().Namespace()
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ns := runtime.Namespace()
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
servers, err := ns.Unresolve(ctx, name)
if err != nil {
diff --git a/tools/namespace/main.go b/tools/namespace/main.go
index 9e5d556..3723981 100644
--- a/tools/namespace/main.go
+++ b/tools/namespace/main.go
@@ -1,15 +1,23 @@
// The following enables go generate to generate the doc.go file.
-//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
+//go:generate go run $VEYRON_ROOT/veyron/go/src/veyron.io/lib/cmdline/testdata/gendoc.go .
package main
import (
+ "veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/rt"
_ "veyron.io/veyron/veyron/profiles"
)
+var runtime veyron2.Runtime
+
func main() {
- defer rt.Init().Cleanup()
+ var err error
+ runtime, err = rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
root().Main()
}
diff --git a/tools/naming/simulator/driver.go b/tools/naming/simulator/driver.go
index 9242830..01685f9 100644
--- a/tools/naming/simulator/driver.go
+++ b/tools/naming/simulator/driver.go
@@ -19,7 +19,6 @@
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron/lib/expect"
- "veyron.io/veyron/veyron/lib/flags/consts"
"veyron.io/veyron/veyron/lib/modules"
_ "veyron.io/veyron/veyron/profiles"
)
@@ -109,11 +108,12 @@
return
}
- shell := modules.NewShell()
- defer shell.Cleanup(os.Stderr, os.Stderr)
- if os.Getenv(consts.VeyronCredentials) == "" {
- shell.CreateAndUseNewCredentials()
+ shell, err := modules.NewShell(nil)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "unexpected error: %s\n", err)
+ os.Exit(1)
}
+ defer shell.Cleanup(os.Stderr, os.Stderr)
scanner := bufio.NewScanner(os.Stdin)
lineno := 1
diff --git a/tools/naming/simulator/driver_test.go b/tools/naming/simulator/driver_test.go
index d4dac88..5ccb5cb 100644
--- a/tools/naming/simulator/driver_test.go
+++ b/tools/naming/simulator/driver_test.go
@@ -40,7 +40,10 @@
}
func TestVariables(t *testing.T) {
- sh := modules.NewShell()
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
sh.SetVar("foo", "bar")
cases := []struct {
input string
diff --git a/tools/principal/main.go b/tools/principal/main.go
index 9096bb3..ed12032 100644
--- a/tools/principal/main.go
+++ b/tools/principal/main.go
@@ -53,7 +53,13 @@
that this tool is running in.
`,
Run: func(cmd *cmdline.Command, args []string) error {
- p := rt.Init().Principal()
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+
+ p := runtime.Principal()
fmt.Printf("Public key : %v\n", p.PublicKey())
fmt.Println("---------------- BlessingStore ----------------")
fmt.Printf("%v", p.BlessingStore().DebugString())
@@ -140,7 +146,14 @@
}
caveats = append(caveats, cav)
}
- blessing, err := rt.Init().Principal().BlessSelf(name, caveats...)
+
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+
+ blessing, err := runtime.Principal().BlessSelf(name, caveats...)
if err != nil {
return fmt.Errorf("failed to create self-signed blessing for name %q: %v", name, err)
}
@@ -189,11 +202,16 @@
if len(args) != 2 {
return fmt.Errorf("require exactly two arguments, provided %d", len(args))
}
- r := rt.Init()
- p := r.Principal()
+
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+
+ p := runtime.Principal()
var with security.Blessings
- var err error
var caveats []security.Caveat
if len(flagBlessWith) > 0 {
if with, err = decodeBlessings(flagBlessWith); err != nil {
@@ -217,7 +235,7 @@
if len(flagBlessRemoteKey) > 0 {
// Send blessings to a "server" started by a "recvblessings" command
granter := &granter{p, with, extension, caveats, flagBlessRemoteKey}
- return sendBlessings(r, tobless, granter, flagBlessRemoteToken)
+ return sendBlessings(runtime, tobless, granter, flagBlessRemoteToken)
}
// Blessing a principal whose key is available locally.
var key security.PublicKey
@@ -261,7 +279,13 @@
blessings set on the store with the "..." pattern).
`,
Run: func(cmd *cmdline.Command, args []string) error {
- return dumpBlessings(rt.Init().Principal().BlessingStore().ForPeer(args...))
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+
+ return dumpBlessings(runtime.Principal().BlessingStore().ForPeer(args...))
},
}
@@ -273,7 +297,13 @@
the environment that this tool is running in.
`,
Run: func(cmd *cmdline.Command, args []string) error {
- return dumpBlessings(rt.Init().Principal().BlessingStore().Default())
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+
+ return dumpBlessings(runtime.Principal().BlessingStore().Default())
},
}
@@ -311,7 +341,14 @@
return fmt.Errorf("failed to decode provided blessings: %v", err)
}
pattern := security.BlessingPattern(args[1])
- p := rt.Init().Principal()
+
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+
+ p := runtime.Principal()
if _, err := p.BlessingStore().Set(blessings, pattern); err != nil {
return fmt.Errorf("failed to set blessings %v for peers %v: %v", blessings, pattern, err)
}
@@ -347,7 +384,14 @@
if err != nil {
return fmt.Errorf("failed to decode provided blessings: %v", err)
}
- p := rt.Init().Principal()
+
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+
+ p := runtime.Principal()
if err := p.BlessingStore().SetDefault(blessings); err != nil {
return fmt.Errorf("failed to set blessings %v as default: %v", blessings, err)
}
@@ -433,7 +477,12 @@
Run: func(cmd *cmdline.Command, args []string) error {
// Initialize the runtime first so that any local errors are reported
// before the HTTP roundtrips for obtaining the macaroon begin.
- r := rt.Init()
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+
blessedChan := make(chan string)
defer close(blessedChan)
macaroonChan, err := getMacaroonForBlessRPC(flagSeekBlessingsFrom, blessedChan)
@@ -442,7 +491,7 @@
}
macaroon := <-macaroonChan
service := <-macaroonChan
- ctx, cancel := r.NewContext().WithTimeout(time.Minute)
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
var reply security.WireBlessings
@@ -459,17 +508,17 @@
<-macaroonChan
if flagSeekBlessingsSetDefault {
- if err := r.Principal().BlessingStore().SetDefault(blessings); err != nil {
+ if err := runtime.Principal().BlessingStore().SetDefault(blessings); err != nil {
return fmt.Errorf("failed to set blessings %v as default: %v", blessings, err)
}
}
if pattern := security.BlessingPattern(flagSeekBlessingsForPeer); len(pattern) > 0 {
- if _, err := r.Principal().BlessingStore().Set(blessings, pattern); err != nil {
+ if _, err := runtime.Principal().BlessingStore().Set(blessings, pattern); err != nil {
return fmt.Errorf("failed to set blessings %v for peers %v: %v", blessings, pattern, err)
}
}
if flagAddToRoots {
- if err := r.Principal().AddToRoots(blessings); err != nil {
+ if err := runtime.Principal().AddToRoots(blessings); err != nil {
return fmt.Errorf("AddToRoots failed: %v", err)
}
}
@@ -514,8 +563,14 @@
if len(args) != 0 {
return fmt.Errorf("command accepts no arguments")
}
- r := rt.Init()
- server, err := r.NewServer()
+
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+
+ server, err := runtime.NewServer()
if err != nil {
return fmt.Errorf("failed to create server to listen for blessings: %v", err)
}
@@ -529,7 +584,7 @@
return fmt.Errorf("unable to generate token: %v", err)
}
service := &recvBlessingsService{
- principal: r.Principal(),
+ principal: runtime.Principal(),
token: base64.URLEncoding.EncodeToString(token[:]),
notify: make(chan error),
}
@@ -542,7 +597,7 @@
fmt.Println("You may want to adjust flags affecting the caveats on this blessing, for example using")
fmt.Println("the --for flag, or change the extension to something more meaningful")
fmt.Println()
- fmt.Printf("principal bless --remote_key=%v --remote_token=%v %v %v\n", r.Principal().PublicKey(), service.token, naming.JoinAddressName(ep.String(), ""), extension)
+ fmt.Printf("principal bless --remote_key=%v --remote_token=%v %v %v\n", runtime.Principal().PublicKey(), service.token, naming.JoinAddressName(ep.String(), ""), extension)
fmt.Println()
fmt.Println("...waiting for sender..")
return <-service.notify
diff --git a/tools/profile/doc.go b/tools/profile/doc.go
index 6a71540..1054a09 100644
--- a/tools/profile/doc.go
+++ b/tools/profile/doc.go
@@ -17,15 +17,32 @@
Run "profile help [command]" for command usage.
The global flags are:
- -alsologtostderr=true: log to standard error as well as files
- -log_backtrace_at=:0: when logging hits line file:N, emit a stack trace
- -log_dir=: if non-empty, write log files to this directory
- -logtostderr=false: log to standard error instead of files
- -max_stack_buf_size=4292608: max size in bytes of the buffer to use for logging stack traces
- -stderrthreshold=2: logs at or above this threshold go to stderr
- -v=0: log level for V logs
- -vmodule=: comma-separated list of pattern=N settings for file-filtered logging
- -vv=0: log level for V logs
+ -alsologtostderr=true
+ log to standard error as well as files
+ -log_backtrace_at=:0
+ when logging hits line file:N, emit a stack trace
+ -log_dir=
+ if non-empty, write log files to this directory
+ -logtostderr=false
+ log to standard error instead of files
+ -max_stack_buf_size=4292608
+ max size in bytes of the buffer to use for logging stack traces
+ -stderrthreshold=2
+ logs at or above this threshold go to stderr
+ -v=0
+ log level for V logs
+ -veyron.credentials=
+ directory to use for storing security credentials
+ -veyron.namespace.root=[/proxy.envyor.com:8101]
+ local namespace root; can be repeated to provided multiple roots
+ -veyron.vtrace.cache_size=1024
+ The number of vtrace traces to store in memory.
+ -veyron.vtrace.dump_on_shutdown=false
+ If true, dump all stored traces on runtime shutdown.
+ -veyron.vtrace.sample_rate=0
+ Rate (from 0.0 to 1.0) to sample vtrace traces.
+ -vmodule=
+ comma-separated list of pattern=N settings for file-filtered logging
Profile Label
@@ -75,15 +92,24 @@
Profile Help
Help with no args displays the usage of the parent command.
+
Help with args displays the usage of the specified sub-command or help topic.
+
"help ..." recursively displays help for all commands and topics.
+The output is formatted to a target width in runes. The target width is
+determined by checking the environment variable CMDLINE_WIDTH, falling back on
+the terminal width from the OS, falling back on 80 chars. By setting
+CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0 the width is unlimited, and
+if x == 0 or is unset one of the fallbacks is used.
+
Usage:
profile help [flags] [command/topic ...]
[command/topic ...] optionally identifies a specific sub-command or help topic.
-The help flags are:
- -style=text: The formatting style for help output, either "text" or "godoc".
+The profile help flags are:
+ -style=text
+ The formatting style for help output, either "text" or "godoc".
*/
package main
diff --git a/tools/profile/impl.go b/tools/profile/impl.go
index 459b2ab..8869787 100644
--- a/tools/profile/impl.go
+++ b/tools/profile/impl.go
@@ -7,7 +7,6 @@
"veyron.io/lib/cmdline"
"veyron.io/veyron/veyron/services/mgmt/profile"
"veyron.io/veyron/veyron/services/mgmt/repository"
- "veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/services/mgmt/build"
)
@@ -26,7 +25,7 @@
}
name := args[0]
p := repository.ProfileClient(name)
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
label, err := p.Label(ctx)
if err != nil {
@@ -51,7 +50,7 @@
}
name := args[0]
p := repository.ProfileClient(name)
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
desc, err := p.Description(ctx)
if err != nil {
@@ -76,7 +75,7 @@
}
name := args[0]
p := repository.ProfileClient(name)
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
spec, err := p.Specification(ctx)
if err != nil {
@@ -111,7 +110,7 @@
Label: "example",
OS: build.Linux,
}
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
if err := p.Put(ctx, spec); err != nil {
return err
@@ -135,7 +134,7 @@
}
name := args[0]
p := repository.ProfileClient(name)
- ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
defer cancel()
if err := p.Remove(ctx); err != nil {
return err
diff --git a/tools/profile/impl_test.go b/tools/profile/impl_test.go
index 1dd149d..b442271 100644
--- a/tools/profile/impl_test.go
+++ b/tools/profile/impl_test.go
@@ -108,7 +108,13 @@
}
func TestProfileClient(t *testing.T) {
- runtime := rt.Init()
+ var err error
+ runtime, err = rt.New()
+ if err != nil {
+ t.Fatalf("Unexpected error initializing runtime: %s", err)
+ }
+ defer runtime.Cleanup()
+
server, endpoint, err := startServer(t, runtime)
if err != nil {
return
diff --git a/tools/profile/main.go b/tools/profile/main.go
index 9e5d556..8423078 100644
--- a/tools/profile/main.go
+++ b/tools/profile/main.go
@@ -1,15 +1,23 @@
// The following enables go generate to generate the doc.go file.
-//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
+//go:generate go run $VEYRON_ROOT/tools/go/src/tools/lib/cmdline/testdata/gendoc.go .
package main
import (
+ "veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/rt"
_ "veyron.io/veyron/veyron/profiles"
)
+var runtime veyron2.Runtime
+
func main() {
- defer rt.Init().Cleanup()
+ var err error
+ runtime, err = rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
root().Main()
}
diff --git a/tools/servicerunner/main.go b/tools/servicerunner/main.go
index 839af0d..2e8560e 100644
--- a/tools/servicerunner/main.go
+++ b/tools/servicerunner/main.go
@@ -64,18 +64,16 @@
vars := map[string]string{}
- sh := modules.NewShell()
- defer sh.Cleanup(os.Stderr, os.Stderr)
- // NOTE(sadovsky): Shell only does this for tests. It would be better if it
- // either always did it or never did it.
- if os.Getenv(consts.VeyronCredentials) == "" {
- panicOnError(sh.CreateAndUseNewCredentials())
- v, ok := sh.GetVar(consts.VeyronCredentials)
- if !ok {
- panic("Missing " + consts.VeyronCredentials)
- }
- vars[consts.VeyronCredentials] = v
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ panic(fmt.Sprintf("modules.NewShell: %s", err))
}
+ defer sh.Cleanup(os.Stderr, os.Stderr)
+ v, ok := sh.GetVar(consts.VeyronCredentials)
+ if !ok {
+ panic("modules.Shell: missing " + consts.VeyronCredentials)
+ }
+ vars[consts.VeyronCredentials] = v
h, err := sh.Start("root", nil, "--", "--veyron.tcp.address=127.0.0.1:0")
panicOnError(err)
diff --git a/tools/servicerunner/servicerunner_test.go b/tools/servicerunner/servicerunner_test.go
new file mode 100644
index 0000000..fb72ff6
--- /dev/null
+++ b/tools/servicerunner/servicerunner_test.go
@@ -0,0 +1,46 @@
+// Runs the servicerunner binary and checks that it outputs a JSON line to
+// stdout with the expected variables.
+package main
+
+import (
+ "bufio"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os/exec"
+ "path"
+ "testing"
+)
+
+func check(t *testing.T, err error) {
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestMain(t *testing.T) {
+ tmpdir, err := ioutil.TempDir("", "servicerunner_test")
+ check(t, err)
+
+ bin := path.Join(tmpdir, "servicerunner")
+ fmt.Println("Building", bin)
+ check(t, exec.Command("veyron", "go", "build", "-o", bin, "veyron.io/veyron/veyron/tools/servicerunner").Run())
+
+ cmd := exec.Command(bin)
+ stdout, err := cmd.StdoutPipe()
+ check(t, err)
+ check(t, cmd.Start())
+
+ line, err := bufio.NewReader(stdout).ReadBytes('\n')
+ check(t, err)
+ vars := map[string]string{}
+ check(t, json.Unmarshal(line, &vars))
+ fmt.Println(vars)
+ for _, name := range []string{"VEYRON_CREDENTIALS", "MT_NAME", "PROXY_ADDR", "WSPR_ADDR"} {
+ if _, ok := vars[name]; !ok {
+ t.Error("Missing", name)
+ }
+ }
+
+ check(t, cmd.Process.Kill())
+}
diff --git a/tools/vrpc/doc.go b/tools/vrpc/doc.go
index a2cb64e..69f90d6 100644
--- a/tools/vrpc/doc.go
+++ b/tools/vrpc/doc.go
@@ -2,9 +2,9 @@
// DO NOT UPDATE MANUALLY
/*
-The vrpc tool facilitates interaction with Veyron RPC servers. In particular,
-it can be used to 1) find out what API a Veyron RPC server exports and
-2) send requests to a Veyron RPC server.
+The vrpc tool facilitates interaction with Veyron RPC servers. In particular, it
+can be used to 1) find out what API a Veyron RPC server exports and 2) send
+requests to a Veyron RPC server.
Usage:
vrpc <command>
@@ -16,59 +16,103 @@
Run "vrpc help [command]" for command usage.
The global flags are:
- -alsologtostderr=true: log to standard error as well as files
- -log_backtrace_at=:0: when logging hits line file:N, emit a stack trace
- -log_dir=: if non-empty, write log files to this directory
- -logtostderr=false: log to standard error instead of files
- -max_stack_buf_size=4292608: max size in bytes of the buffer to use for logging stack traces
- -stderrthreshold=2: logs at or above this threshold go to stderr
- -v=0: log level for V logs
- -vmodule=: comma-separated list of pattern=N settings for file-filtered logging
- -vv=0: log level for V logs
+ -acl=
+ acl is an optional JSON-encoded security.ACL that is used to construct a
+ security.Authorizer. Example: {"In":{"veyron/alice/...":"RW"}} is a
+ JSON-encoded ACL that allows all delegates of "veyron/alice" to access all
+ methods with ReadLabel or WriteLabel. If this flag is provided then the
+ \"--acl_file\" must be absent.
+ -acl_file=
+ acl_file is an optional path to a file containing a JSON-encoded security.ACL
+ that is used to construct a security.Authorizer. If this flag is provided
+ then the "--acl_file" flag must be absent.
+ -alsologtostderr=true
+ log to standard error as well as files
+ -log_backtrace_at=:0
+ when logging hits line file:N, emit a stack trace
+ -log_dir=
+ if non-empty, write log files to this directory
+ -logtostderr=false
+ log to standard error instead of files
+ -max_stack_buf_size=4292608
+ max size in bytes of the buffer to use for logging stack traces
+ -stderrthreshold=2
+ logs at or above this threshold go to stderr
+ -v=0
+ log level for V logs
+ -veyron.credentials=
+ directory to use for storing security credentials
+ -veyron.namespace.root=[/proxy.envyor.com:8101]
+ local namespace root; can be repeated to provided multiple roots
+ -veyron.proxy=
+ object name of proxy service to use to export services across network
+ boundaries
+ -veyron.tcp.address=:0
+ address to listen on
+ -veyron.tcp.protocol=tcp
+ protocol to listen with
+ -veyron.vtrace.cache_size=1024
+ The number of vtrace traces to store in memory.
+ -veyron.vtrace.dump_on_shutdown=false
+ If true, dump all stored traces on runtime shutdown.
+ -veyron.vtrace.sample_rate=0
+ Rate (from 0.0 to 1.0) to sample vtrace traces.
+ -vmodule=
+ comma-separated list of pattern=N settings for file-filtered logging
Vrpc Describe
-Describe connects to the Veyron RPC server identified by <server>, finds out what
-its API is, and outputs a succint summary of this API to the standard output.
+Describe connects to the Veyron RPC server identified by <server>, finds out
+what its API is, and outputs a succint summary of this API to the standard
+output.
Usage:
vrpc describe <server>
-<server> identifies the Veyron RPC server. It can either be the object address of
-the server or an Object name in which case the vrpc will use Veyron's name
+<server> identifies the Veyron RPC server. It can either be the object address
+of the server or an Object name in which case the vrpc will use Veyron's name
resolution to match this name to an end-point.
Vrpc Invoke
-Invoke connects to the Veyron RPC server identified by <server>, invokes the method
-identified by <method>, supplying the arguments identified by <args>, and outputs
-the results of the invocation to the standard output.
+Invoke connects to the Veyron RPC server identified by <server>, invokes the
+method identified by <method>, supplying the arguments identified by <args>, and
+outputs the results of the invocation to the standard output.
Usage:
vrpc invoke <server> <method> <args>
-<server> identifies the Veyron RPC server. It can either be the object address of
-the server or an Object name in which case the vrpc will use Veyron's name
+<server> identifies the Veyron RPC server. It can either be the object address
+of the server or an Object name in which case the vrpc will use Veyron's name
resolution to match this name to an end-point.
<method> identifies the name of the method to be invoked.
<args> identifies the arguments of the method to be invoked. It should be a list
-of values in a VOM JSON format that can be reflected to the correct type
-using Go's reflection.
+of values in a VOM JSON format that can be reflected to the correct type using
+Go's reflection.
Vrpc Help
Help with no args displays the usage of the parent command.
+
Help with args displays the usage of the specified sub-command or help topic.
+
"help ..." recursively displays help for all commands and topics.
+The output is formatted to a target width in runes. The target width is
+determined by checking the environment variable CMDLINE_WIDTH, falling back on
+the terminal width from the OS, falling back on 80 chars. By setting
+CMDLINE_WIDTH=x, if x > 0 the width is x, if x < 0 the width is unlimited, and
+if x == 0 or is unset one of the fallbacks is used.
+
Usage:
vrpc help [flags] [command/topic ...]
[command/topic ...] optionally identifies a specific sub-command or help topic.
-The help flags are:
- -style=text: The formatting style for help output, either "text" or "godoc".
+The vrpc help flags are:
+ -style=text
+ The formatting style for help output, either "text" or "godoc".
*/
package main
diff --git a/tools/vrpc/impl.go b/tools/vrpc/impl.go
index 7d0e560..3133ea4 100644
--- a/tools/vrpc/impl.go
+++ b/tools/vrpc/impl.go
@@ -13,7 +13,6 @@
"veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
- "veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/vdl/vdlutil"
"veyron.io/veyron/veyron2/vom"
"veyron.io/veyron/veyron2/wiretype"
@@ -53,8 +52,6 @@
return cmd.UsageErrorf("describe: incorrect number of arguments, expected 1, got %d", len(args))
}
- runtime := rt.R()
-
client, err := setupClient(cmd, runtime)
if err != nil {
return err
@@ -93,8 +90,6 @@
}
server, method, args := args[0], args[1], args[2:]
- runtime := rt.R()
-
client, err := setupClient(cmd, runtime)
if err != nil {
return err
@@ -126,15 +121,15 @@
// Benj implements support for decoding arbitrary structs to an
// empty interface, this will no longer be needed.
var x1 idl_test_base.Struct
- vom.Register(x1)
+ vdlutil.Register(x1)
var x2 idl_node.Description
- vom.Register(x2)
+ vdlutil.Register(x2)
var x3 idl_binary.Description
- vom.Register(x3)
+ vdlutil.Register(x3)
var x4 naming.VDLMountedServer
- vom.Register(x4)
+ vdlutil.Register(x4)
var x5 naming.VDLMountEntry
- vom.Register(x5)
+ vdlutil.Register(x5)
// Decode the inputs from vomJSON-formatted command-line arguments.
inputs := make([]interface{}, len(args))
diff --git a/tools/vrpc/impl_test.go b/tools/vrpc/impl_test.go
index f585f2b..cfbef5d 100644
--- a/tools/vrpc/impl_test.go
+++ b/tools/vrpc/impl_test.go
@@ -169,7 +169,13 @@
}
func TestVRPC(t *testing.T) {
- runtime := rt.Init()
+ var err error
+ runtime, err = rt.New()
+ if err != nil {
+ t.Fatalf("Unexpected error initializing runtime: %s", err)
+ }
+ defer runtime.Cleanup()
+
// Skip defer runtime.Cleanup() to avoid messing up other tests in the
// same process.
server, endpoint, err := startServer(t, runtime)
diff --git a/tools/vrpc/main.go b/tools/vrpc/main.go
index f52ab6f..3723981 100644
--- a/tools/vrpc/main.go
+++ b/tools/vrpc/main.go
@@ -1,13 +1,23 @@
// The following enables go generate to generate the doc.go file.
-//go:generate go run $VEYRON_ROOT/lib/cmdline/testdata/gendoc.go .
+//go:generate go run $VEYRON_ROOT/veyron/go/src/veyron.io/lib/cmdline/testdata/gendoc.go .
package main
import (
+ "veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/rt"
+
+ _ "veyron.io/veyron/veyron/profiles"
)
+var runtime veyron2.Runtime
+
func main() {
- defer rt.Init().Cleanup()
+ var err error
+ runtime, err = rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
root().Main()
}
diff --git a/tools/vrpc/test_base/test_base.vdl.go b/tools/vrpc/test_base/test_base.vdl.go
index eec0c49..a411da3 100644
--- a/tools/vrpc/test_base/test_base.vdl.go
+++ b/tools/vrpc/test_base/test_base.vdl.go
@@ -9,6 +9,7 @@
__veyron2 "veyron.io/veyron/veyron2"
__context "veyron.io/veyron/veyron2/context"
__ipc "veyron.io/veyron/veyron2/ipc"
+ __vdl "veyron.io/veyron/veyron2/vdl"
__vdlutil "veyron.io/veyron/veyron2/vdl/vdlutil"
__wiretype "veyron.io/veyron/veyron2/wiretype"
)
@@ -23,6 +24,15 @@
Y int32
}
+func (Struct) __VDLReflect(struct {
+ Name string "veyron.io/veyron/veyron/tools/vrpc/test_base.Struct"
+}) {
+}
+
+func init() {
+ __vdl.Register(Struct{})
+}
+
// TypeTesterClientMethods is the client interface
// containing TypeTester methods.
type TypeTesterClientMethods interface {