Merge "veyron/lib/modules: allow Shutdown to return an error status and avoid races."
diff --git a/lib/unixfd/unixfd.go b/lib/unixfd/unixfd.go
new file mode 100644
index 0000000..2fa870f
--- /dev/null
+++ b/lib/unixfd/unixfd.go
@@ -0,0 +1,133 @@
+// Package unixfd provides provides support for Dialing and Listening
+// on already connected file descriptors (like those returned by socketpair).
+package unixfd
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "os"
+ "strconv"
+ "sync"
+ "syscall"
+ "veyron2/ipc/stream"
+)
+
+const Network string = "unixfd"
+
+// singleConnListener implements net.Listener for an already-connected socket.
+// This is different from net.FileListener, which calls syscall.Listen
+// on an unconnected socket.
+type singleConnListener struct {
+ c chan net.Conn
+ addr net.Addr
+ sync.Mutex
+}
+
+func (l *singleConnListener) getChan() chan net.Conn {
+ l.Lock()
+ defer l.Unlock()
+ return l.c
+}
+
+func (l *singleConnListener) Accept() (net.Conn, error) {
+ c := l.getChan()
+ if c == nil {
+ return nil, errors.New("listener closed")
+ }
+ if conn, ok := <-c; ok {
+ return conn, nil
+ }
+ return nil, io.EOF
+}
+
+func (l *singleConnListener) Close() error {
+ l.Lock()
+ defer l.Unlock()
+ lc := l.c
+ if lc == nil {
+ return errors.New("listener already closed")
+ }
+ close(l.c)
+ l.c = nil
+ // If the socket was never Accept'ed we need to close it.
+ if c, ok := <-lc; ok {
+ return c.Close()
+ }
+ return nil
+}
+
+func (l *singleConnListener) Addr() net.Addr {
+ return l.addr
+}
+
+func unixFDConn(address string) (net.Conn, error) {
+ fd, err := strconv.ParseInt(address, 10, 32)
+ if err != nil {
+ return nil, err
+ }
+ file := os.NewFile(uintptr(fd), "tmp")
+ conn, err := net.FileConn(file)
+ // 'file' is not used after this point, but we keep it open
+ // so that 'address' remains valid.
+ if err != nil {
+ file.Close()
+ return nil, err
+ }
+ // We wrap 'conn' so we can customize the address, and also
+ // to close 'file'.
+ return &fdConn{addr(address), file, conn}, nil
+}
+
+type fdConn struct {
+ addr net.Addr
+ sock *os.File
+ net.Conn
+}
+
+func (c *fdConn) Close() (err error) {
+ defer c.sock.Close()
+ return c.Conn.Close()
+}
+
+func (c *fdConn) LocalAddr() net.Addr {
+ return c.addr
+}
+
+func (c *fdConn) RemoteAddr() net.Addr {
+ return c.addr
+}
+
+func unixFDListen(address string) (net.Listener, error) {
+ conn, err := unixFDConn(address)
+ if err != nil {
+ return nil, err
+ }
+ c := make(chan net.Conn, 1)
+ c <- conn
+ return &singleConnListener{c, conn.LocalAddr(), sync.Mutex{}}, nil
+}
+
+type addr string
+
+func (a addr) Network() string { return Network }
+func (a addr) String() string { return string(a) }
+
+// Addr returns a net.Addr for the unixfd network for the given file descriptor.
+func Addr(fd uintptr) net.Addr {
+ return addr(fmt.Sprintf("%d", fd))
+}
+
+// Socketpair returns two connected unix domain sockets, or an error.
+func Socketpair() ([]*os.File, error) {
+ fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
+ if err != nil {
+ return nil, err
+ }
+ return []*os.File{os.NewFile(uintptr(fds[0]), "local"), os.NewFile(uintptr(fds[1]), "remote")}, nil
+}
+
+func init() {
+ stream.RegisterProtocol(Network, unixFDConn, unixFDListen)
+}
diff --git a/lib/unixfd/unixfd_test.go b/lib/unixfd/unixfd_test.go
new file mode 100644
index 0000000..dd46ce9
--- /dev/null
+++ b/lib/unixfd/unixfd_test.go
@@ -0,0 +1,127 @@
+package unixfd
+
+import (
+ "bytes"
+ "io"
+ "net"
+ "os"
+ "reflect"
+ "testing"
+)
+
+type nothing struct{}
+
+func dial(fd *os.File) (net.Conn, error) {
+ addr := Addr(fd.Fd())
+ return unixFDConn(addr.String())
+}
+
+func listen(fd *os.File) (net.Listener, error) {
+ addr := Addr(fd.Fd())
+ return unixFDListen(addr.String())
+}
+
+func testWrite(t *testing.T, c net.Conn, data string) {
+ n, err := c.Write([]byte(data))
+ if err != nil {
+ t.Errorf("Write: %v", err)
+ return
+ }
+ if n != len(data) {
+ t.Errorf("Wrote %d bytes, expected %d", n, len(data))
+ }
+}
+
+func testRead(t *testing.T, c net.Conn, expected string) {
+ buf := make([]byte, len(expected)+2)
+ n, err := c.Read(buf)
+ if err != nil {
+ t.Errorf("Read: %v", err)
+ return
+ }
+ if n != len(expected) || !bytes.Equal(buf[0:n], []byte(expected)) {
+ t.Errorf("got %q, expected %q", buf[0:n], expected)
+ }
+}
+
+func TestDial(t *testing.T) {
+ fds, err := Socketpair()
+ if err != nil {
+ t.Fatalf("socketpair: %v", err)
+ }
+ a, err := dial(fds[0])
+ if err != nil {
+ t.Fatalf("dial: %v", err)
+ }
+ b, err := dial(fds[1])
+ if err != nil {
+ t.Fatalf("dial: %v", err)
+ }
+
+ testWrite(t, a, "TEST1")
+ testRead(t, b, "TEST1")
+ testWrite(t, b, "TEST2")
+ testRead(t, a, "TEST2")
+
+ if !reflect.DeepEqual(a.LocalAddr(), Addr(fds[0].Fd())) {
+ t.Errorf("Invalid address %v, expected %d", a.LocalAddr(), fds[0].Fd())
+ }
+ if !reflect.DeepEqual(a.RemoteAddr(), Addr(fds[0].Fd())) {
+ t.Errorf("Invalid address %v, expected %d", a.RemoteAddr(), fds[0].Fd())
+ }
+ if !reflect.DeepEqual(b.LocalAddr(), Addr(fds[1].Fd())) {
+ t.Errorf("Invalid address %v, expected %d", a.LocalAddr(), fds[1].Fd())
+ }
+ if !reflect.DeepEqual(b.RemoteAddr(), Addr(fds[1].Fd())) {
+ t.Errorf("Invalid address %v, expected %d", a.RemoteAddr(), fds[1].Fd())
+ }
+}
+
+func TestListen(t *testing.T) {
+ fds, err := Socketpair()
+ if err != nil {
+ t.Fatalf("socketpair: %v", err)
+ }
+ a, err := dial(fds[0])
+ if err != nil {
+ t.Fatalf("dial: %v", err)
+ }
+ l, err := listen(fds[1])
+ if err != nil {
+ t.Fatalf("listen: %v", err)
+ }
+ b, err := l.Accept()
+ if err != nil {
+ t.Fatalf("accept: %v", err)
+ }
+ start := make(chan nothing, 0)
+ done := make(chan nothing)
+ go func() {
+ defer close(done)
+ <-start
+ if _, err := l.Accept(); err != io.EOF {
+ t.Fatalf("accept: expected EOF, got %v", err)
+ }
+ }()
+
+ // block until the goroutine starts running
+ start <- nothing{}
+ testWrite(t, a, "LISTEN")
+ testRead(t, b, "LISTEN")
+
+ err = l.Close()
+ if err != nil {
+ t.Fatalf("close: %v", err)
+ }
+ <-done
+
+ // After closed, accept should fail immediately
+ _, err = l.Accept()
+ if err == nil {
+ t.Fatalf("Accept succeeded after close")
+ }
+ err = l.Close()
+ if err == nil {
+ t.Fatalf("Close succeeded twice")
+ }
+}
diff --git a/runtimes/google/ipc/stream/crypto/tls_generate_old.sh b/runtimes/google/ipc/stream/crypto/tls_generate_old.sh
index 0560c33..c66c2e8 100755
--- a/runtimes/google/ipc/stream/crypto/tls_generate_old.sh
+++ b/runtimes/google/ipc/stream/crypto/tls_generate_old.sh
@@ -25,8 +25,7 @@
sed -e 's|// +build go1.4|// +build !go1.4|' |
sed -e 's|"crypto/tls"|tls "veyron/runtimes/google/ipc/stream/crypto/tlsfork"|' >>$OUTFILE
- local -r REPO_ROOT="$(git rev-parse --show-toplevel)"
- "${REPO_ROOT}/scripts/build/go" fmt "${OUTFILE}"
+ "${VEYRON_ROOT}/veyron/scripts/build/go" fmt "${OUTFILE}"
}
main "$@"
diff --git a/runtimes/google/rt/security.go b/runtimes/google/rt/security.go
index a69b33c..8325c02 100644
--- a/runtimes/google/rt/security.go
+++ b/runtimes/google/rt/security.go
@@ -5,10 +5,14 @@
"os"
"os/user"
+ "veyron/lib/unixfd"
isecurity "veyron/runtimes/google/security"
vsecurity "veyron/security"
+ "veyron/security/agent"
+ "veyron2"
"veyron2/security"
+ "veyron2/verror"
"veyron2/vlog"
)
@@ -50,7 +54,10 @@
return nil
}
var err error
- if file := os.Getenv("VEYRON_IDENTITY"); len(file) > 0 {
+ if len(os.Getenv(agent.EndpointVarName)) > 0 {
+ rt.id, err = rt.connectToAgent()
+ return err
+ } else if file := os.Getenv("VEYRON_IDENTITY"); len(file) > 0 {
if rt.id, err = loadIdentityFromFile(file); err != nil || rt.id == nil {
return fmt.Errorf("Could not load identity from the VEYRON_IDENTITY environment variable (%q): %v", file, err)
}
@@ -105,3 +112,25 @@
defer f.Close()
return vsecurity.LoadIdentity(f)
}
+
+func (rt *vrt) connectToAgent() (security.PrivateID, error) {
+ // Verify we're communicating over unix domain sockets so
+ // we know it's safe to use VCSecurityNone.
+ endpoint, err := rt.NewEndpoint(os.Getenv(agent.EndpointVarName))
+ if err != nil {
+ return nil, err
+ }
+ if endpoint.Addr().Network() != unixfd.Network {
+ return nil, verror.BadArgf("invalid agent address %v", endpoint.Addr())
+ }
+
+ client, err := rt.NewClient(veyron2.VCSecurityNone)
+ if err != nil {
+ return nil, err
+ }
+ signer, err := agent.NewAgentSigner(client, endpoint.String(), rt.NewContext())
+ if err != nil {
+ return nil, err
+ }
+ return isecurity.NewPrivateID("selfSigned", signer)
+}
diff --git a/security/agent/agent_test.go b/security/agent/agent_test.go
new file mode 100644
index 0000000..f2e46b2
--- /dev/null
+++ b/security/agent/agent_test.go
@@ -0,0 +1,85 @@
+package agent_test
+
+import (
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "crypto/rand"
+ "os"
+ "reflect"
+ "testing"
+ "veyron/security/agent"
+ "veyron/security/agent/server"
+ "veyron2"
+ "veyron2/rt"
+ "veyron2/security"
+)
+
+type fakesigner struct {
+ key security.PublicKey
+}
+
+type testdata struct {
+ server_conn os.File
+ agent security.Signer
+ signer fakesigner
+}
+
+func setup() *testdata {
+ runtime := rt.Init()
+ result := &testdata{signer: newFakeSigner()}
+ sock, err := server.RunAnonymousAgent(runtime, result.signer)
+ if err != nil {
+ panic(err)
+ }
+ defer sock.Close()
+ client, err := runtime.NewClient(veyron2.VCSecurityNone)
+ if err != nil {
+ panic(err)
+ }
+ if agent, err := agent.NewAgentSigner(client, agent.CreateAgentEndpoint(int(sock.Fd())), runtime.NewContext()); err == nil {
+ result.agent = agent
+ return result
+ } else {
+ panic(err)
+ }
+}
+
+func TestSignature(t *testing.T) {
+ td := setup()
+ sig, err := td.agent.Sign(nil, []byte("abc"))
+
+ if err != nil {
+ t.Error(err)
+ }
+ expected := security.Signature{R: []byte{6}, S: []byte{7}}
+ if !reflect.DeepEqual(sig, expected) {
+ t.Errorf("Bad signature. Got\n%#v\nExpected:\n%#v", sig, expected)
+ }
+}
+
+func TestPublicKey(t *testing.T) {
+ td := setup()
+ expected_key := td.signer.PublicKey()
+ agent_key := td.agent.PublicKey()
+ if !reflect.DeepEqual(expected_key, agent_key) {
+ t.Errorf("Different keys: %v, %v", expected_key, agent_key)
+ }
+}
+
+func newFakeSigner() fakesigner {
+ key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ panic(err)
+ }
+ return fakesigner{security.NewECDSAPublicKey(&key.PublicKey)}
+}
+
+func (fakesigner) Sign(message []byte) (security.Signature, error) {
+ var sig security.Signature
+ sig.R, sig.S = []byte{6}, []byte{7}
+ return sig, nil
+}
+
+func (s fakesigner) PublicKey() security.PublicKey {
+ return s.key
+}
diff --git a/security/agent/agentd/main.go b/security/agent/agentd/main.go
new file mode 100644
index 0000000..9538b1e
--- /dev/null
+++ b/security/agent/agentd/main.go
@@ -0,0 +1,63 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "os/exec"
+ "syscall"
+ "veyron/security/agent"
+ "veyron/security/agent/server"
+ "veyron2/rt"
+)
+
+func main() {
+ flag.Usage = func() {
+ fmt.Fprintf(os.Stderr, `Usage: %s [agent options] command command_args...
+
+Loads the identity specified in VEYRON_IDENTITY into memory, then
+starts the specified command with access to the identity via the
+agent protocol instead of directly reading from disk.
+
+`, os.Args[0])
+ flag.PrintDefaults()
+ }
+ // Load the identity specified in the environment
+ runtime := rt.Init()
+ log := runtime.Logger()
+
+ if len(flag.Args()) < 1 {
+ flag.Usage()
+ os.Exit(1)
+ }
+
+ var err error
+ if err = os.Setenv(agent.EndpointVarName, agent.CreateAgentEndpoint(3)); err != nil {
+ log.Fatalf("setenv: %v", err)
+ }
+ if err = os.Setenv("VEYRON_IDENTITY", ""); err != nil {
+ log.Fatalf("setenv: %v", err)
+ }
+
+ // Start running our server.
+ var sock *os.File
+ if sock, err = server.RunAnonymousAgent(runtime, runtime.Identity()); err != nil {
+ log.Fatalf("RunAgent: %v", err)
+ }
+
+ // Now run the client and wait for it to finish.
+ cmd := exec.Command(flag.Args()[0], flag.Args()[1:]...)
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ cmd.ExtraFiles = []*os.File{sock}
+
+ err = cmd.Start()
+ if err != nil {
+ log.Fatalf("Error starting child: %v", err)
+ }
+ sock.Close()
+ cmd.Wait()
+ status := cmd.ProcessState.Sys().(syscall.WaitStatus)
+ os.Exit(status.ExitStatus())
+}
diff --git a/security/agent/client.go b/security/agent/client.go
new file mode 100644
index 0000000..cb32acc
--- /dev/null
+++ b/security/agent/client.go
@@ -0,0 +1,72 @@
+// Package agent provides a client for communicating with an "Agent"
+// process holding the private key for an identity.
+package agent
+
+import (
+ "fmt"
+ "strconv"
+
+ "veyron/lib/unixfd"
+ "veyron2/context"
+ "veyron2/ipc"
+ "veyron2/naming"
+ "veyron2/security"
+ "veyron2/security/wire"
+)
+
+// EndpointVarName is the name of the environment variable containing
+// the endpoint for talking to the agent.
+const EndpointVarName = "VEYRON_AGENT_ENDPOINT"
+
+type client struct {
+ client ipc.Client
+ name string
+ ctx context.T
+ key security.PublicKey
+}
+
+func (c *client) call(name string, result interface{}, args ...interface{}) (err error) {
+ var call ipc.Call
+ if call, err = c.client.StartCall(c.ctx, c.name, name, args); err == nil {
+ if ierr := call.Finish(result, &err); ierr != nil {
+ err = ierr
+ }
+ }
+ return
+}
+
+// NewAgentSigner returns a Signer using the PrivateKey held in a remote agent process.
+// 'ctx' should not have a deadline, and should never be cancelled.
+func NewAgentSigner(c ipc.Client, endpoint string, ctx context.T) (security.Signer, error) {
+ agent := &client{c, naming.JoinAddressName(endpoint, ""), ctx, nil}
+ if err := agent.fetchPublicKey(); err != nil {
+ return nil, err
+ }
+ return agent, nil
+}
+
+func (c *client) fetchPublicKey() (err error) {
+ var key wire.PublicKey
+ if err = c.call("PublicKey", &key); err != nil {
+ return
+ }
+ c.key, err = key.Decode()
+ return
+}
+
+func (c *client) PublicKey() security.PublicKey {
+ return c.key
+}
+
+func (c *client) Sign(purpose, message []byte) (sig security.Signature, err error) {
+ if purpose != nil {
+ err = fmt.Errorf("purpose not supported")
+ return
+ }
+ err = c.call("Sign", &sig, message)
+ return
+}
+
+func CreateAgentEndpoint(fd int) string {
+ return naming.FormatEndpoint(unixfd.Network, strconv.Itoa(fd))
+}
diff --git a/security/agent/server/server.go b/security/agent/server/server.go
new file mode 100644
index 0000000..71578d7
--- /dev/null
+++ b/security/agent/server/server.go
@@ -0,0 +1,65 @@
+// Package server provides a server which keeps a private key in memory
+// and allows clients to use the key for signing.
+package server
+
+import (
+ "os"
+ "veyron/lib/unixfd"
+ "veyron2"
+ "veyron2/ipc"
+ "veyron2/security"
+ "veyron2/security/wire"
+)
+
+type Signer interface {
+ Sign(message []byte) (security.Signature, error)
+ PublicKey() security.PublicKey
+}
+
+type agentd struct {
+ signer Signer
+}
+
+// RunAnonymousAgent starts the agent server listening on an
+// anonymous unix domain socket. It will respond to SignatureRequests
+// using 'signer'.
+// The returned 'client' is typically passed via cmd.ExtraFiles to a child process.
+func RunAnonymousAgent(runtime veyron2.Runtime, signer Signer) (client *os.File, err error) {
+ // VCSecurityNone is safe since we're using anonymous unix sockets.
+ // Only our child process can possibly communicate on the socket.
+ s, err := runtime.NewServer(veyron2.VCSecurityNone)
+ if err != nil {
+ return nil, err
+ }
+
+ socks, err := unixfd.Socketpair()
+ server_sock := socks[0]
+ client_sock := make(chan *os.File, 1)
+ client_sock <- socks[1]
+ close(client_sock)
+ defer server_sock.Close()
+ defer func() {
+ if sock, ok := <-client_sock; ok {
+ sock.Close()
+ }
+ }()
+
+ serverAgent := NewServerAgent(agentd{signer})
+ addr := unixfd.Addr(server_sock.Fd())
+ if _, err = s.Listen(addr.Network(), addr.String()); err != nil {
+ return
+ }
+ if err = s.Serve("", ipc.LeafDispatcher(serverAgent, nil)); err != nil {
+ return
+ }
+ return <-client_sock, nil
+}
+
+func (a agentd) Sign(_ ipc.ServerContext, message []byte) (security.Signature, error) {
+ return a.signer.Sign(message)
+}
+
+func (a agentd) PublicKey(ipc.ServerContext) (key wire.PublicKey, err error) {
+ err = key.Encode(a.signer.PublicKey())
+ return
+}
diff --git a/security/agent/server/wire.vdl b/security/agent/server/wire.vdl
new file mode 100644
index 0000000..695aad0
--- /dev/null
+++ b/security/agent/server/wire.vdl
@@ -0,0 +1,11 @@
+package server
+
+import (
+ "veyron2/security"
+ "veyron2/security/wire"
+)
+
+type Agent interface {
+ Sign(message []byte) (security.Signature, error)
+ PublicKey() (wire.PublicKey, error)
+}
diff --git a/security/agent/server/wire.vdl.go b/security/agent/server/wire.vdl.go
new file mode 100644
index 0000000..ff7e386
--- /dev/null
+++ b/security/agent/server/wire.vdl.go
@@ -0,0 +1,230 @@
+// This file was auto-generated by the veyron vdl tool.
+// Source: wire.vdl
+
+package server
+
+import (
+ "veyron2/security"
+
+ "veyron2/security/wire"
+
+ // The non-user imports are prefixed with "_gen_" to prevent collisions.
+ _gen_veyron2 "veyron2"
+ _gen_context "veyron2/context"
+ _gen_ipc "veyron2/ipc"
+ _gen_naming "veyron2/naming"
+ _gen_vdlutil "veyron2/vdl/vdlutil"
+ _gen_wiretype "veyron2/wiretype"
+)
+
+// TODO(bprosnitz) Remove this line once signatures are updated to use typevals.
+// It corrects a bug where _gen_wiretype is unused in VDL pacakges where only bootstrap types are used on interfaces.
+const _ = _gen_wiretype.TypeIDInvalid
+
+// Agent is the interface the client binds and uses.
+// Agent_ExcludingUniversal is the interface without internal framework-added methods
+// to enable embedding without method collisions. Not to be used directly by clients.
+type Agent_ExcludingUniversal interface {
+ Sign(ctx _gen_context.T, message []byte, opts ..._gen_ipc.CallOpt) (reply security.Signature, err error)
+ PublicKey(ctx _gen_context.T, opts ..._gen_ipc.CallOpt) (reply wire.PublicKey, err error)
+}
+type Agent interface {
+ _gen_ipc.UniversalServiceMethods
+ Agent_ExcludingUniversal
+}
+
+// AgentService is the interface the server implements.
+type AgentService interface {
+ Sign(context _gen_ipc.ServerContext, message []byte) (reply security.Signature, err error)
+ PublicKey(context _gen_ipc.ServerContext) (reply wire.PublicKey, err error)
+}
+
+// BindAgent returns the client stub implementing the Agent
+// interface.
+//
+// If no _gen_ipc.Client is specified, the default _gen_ipc.Client in the
+// global Runtime is used.
+func BindAgent(name string, opts ..._gen_ipc.BindOpt) (Agent, error) {
+ var client _gen_ipc.Client
+ switch len(opts) {
+ case 0:
+ // Do nothing.
+ case 1:
+ if clientOpt, ok := opts[0].(_gen_ipc.Client); opts[0] == nil || ok {
+ client = clientOpt
+ } else {
+ return nil, _gen_vdlutil.ErrUnrecognizedOption
+ }
+ default:
+ return nil, _gen_vdlutil.ErrTooManyOptionsToBind
+ }
+ stub := &clientStubAgent{defaultClient: client, name: name}
+
+ return stub, nil
+}
+
+// NewServerAgent creates a new server stub.
+//
+// It takes a regular server implementing the AgentService
+// interface, and returns a new server stub.
+func NewServerAgent(server AgentService) interface{} {
+ return &ServerStubAgent{
+ service: server,
+ }
+}
+
+// clientStubAgent implements Agent.
+type clientStubAgent struct {
+ defaultClient _gen_ipc.Client
+ name string
+}
+
+func (__gen_c *clientStubAgent) client(ctx _gen_context.T) _gen_ipc.Client {
+ if __gen_c.defaultClient != nil {
+ return __gen_c.defaultClient
+ }
+ return _gen_veyron2.RuntimeFromContext(ctx).Client()
+}
+
+func (__gen_c *clientStubAgent) Sign(ctx _gen_context.T, message []byte, opts ..._gen_ipc.CallOpt) (reply security.Signature, err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client(ctx).StartCall(ctx, __gen_c.name, "Sign", []interface{}{message}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubAgent) PublicKey(ctx _gen_context.T, opts ..._gen_ipc.CallOpt) (reply wire.PublicKey, err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client(ctx).StartCall(ctx, __gen_c.name, "PublicKey", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubAgent) UnresolveStep(ctx _gen_context.T, opts ..._gen_ipc.CallOpt) (reply []string, err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client(ctx).StartCall(ctx, __gen_c.name, "UnresolveStep", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubAgent) Signature(ctx _gen_context.T, opts ..._gen_ipc.CallOpt) (reply _gen_ipc.ServiceSignature, err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client(ctx).StartCall(ctx, __gen_c.name, "Signature", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubAgent) GetMethodTags(ctx _gen_context.T, method string, opts ..._gen_ipc.CallOpt) (reply []interface{}, err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client(ctx).StartCall(ctx, __gen_c.name, "GetMethodTags", []interface{}{method}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+// ServerStubAgent wraps a server that implements
+// AgentService and provides an object that satisfies
+// the requirements of veyron2/ipc.ReflectInvoker.
+type ServerStubAgent struct {
+ service AgentService
+}
+
+func (__gen_s *ServerStubAgent) GetMethodTags(call _gen_ipc.ServerCall, method string) ([]interface{}, error) {
+ // TODO(bprosnitz) GetMethodTags() will be replaces with Signature().
+ // Note: This exhibits some weird behavior like returning a nil error if the method isn't found.
+ // This will change when it is replaced with Signature().
+ switch method {
+ case "Sign":
+ return []interface{}{}, nil
+ case "PublicKey":
+ return []interface{}{}, nil
+ default:
+ return nil, nil
+ }
+}
+
+func (__gen_s *ServerStubAgent) Signature(call _gen_ipc.ServerCall) (_gen_ipc.ServiceSignature, error) {
+ result := _gen_ipc.ServiceSignature{Methods: make(map[string]_gen_ipc.MethodSignature)}
+ result.Methods["PublicKey"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{},
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 71},
+ {Name: "", Type: 69},
+ },
+ }
+ result.Methods["Sign"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "message", Type: 66},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 68},
+ {Name: "", Type: 69},
+ },
+ }
+
+ result.TypeDefs = []_gen_vdlutil.Any{
+ _gen_wiretype.NamedPrimitiveType{Type: 0x32, Name: "byte", Tags: []string(nil)}, _gen_wiretype.SliceType{Elem: 0x41, Name: "", Tags: []string(nil)}, _gen_wiretype.NamedPrimitiveType{Type: 0x3, Name: "veyron2/security.Hash", Tags: []string(nil)}, _gen_wiretype.StructType{
+ []_gen_wiretype.FieldType{
+ _gen_wiretype.FieldType{Type: 0x42, Name: "Purpose"},
+ _gen_wiretype.FieldType{Type: 0x43, Name: "Hash"},
+ _gen_wiretype.FieldType{Type: 0x42, Name: "R"},
+ _gen_wiretype.FieldType{Type: 0x42, Name: "S"},
+ },
+ "veyron2/security.Signature", []string(nil)},
+ _gen_wiretype.NamedPrimitiveType{Type: 0x1, Name: "error", Tags: []string(nil)}, _gen_wiretype.NamedPrimitiveType{Type: 0x32, Name: "veyron2/security/wire.KeyCurve", Tags: []string(nil)}, _gen_wiretype.StructType{
+ []_gen_wiretype.FieldType{
+ _gen_wiretype.FieldType{Type: 0x46, Name: "Curve"},
+ _gen_wiretype.FieldType{Type: 0x42, Name: "XY"},
+ },
+ "veyron2/security/wire.PublicKey", []string(nil)},
+ }
+
+ return result, nil
+}
+
+func (__gen_s *ServerStubAgent) UnresolveStep(call _gen_ipc.ServerCall) (reply []string, err error) {
+ if unresolver, ok := __gen_s.service.(_gen_ipc.Unresolver); ok {
+ return unresolver.UnresolveStep(call)
+ }
+ if call.Server() == nil {
+ return
+ }
+ var published []string
+ if published, err = call.Server().Published(); err != nil || published == nil {
+ return
+ }
+ reply = make([]string, len(published))
+ for i, p := range published {
+ reply[i] = _gen_naming.Join(p, call.Name())
+ }
+ return
+}
+
+func (__gen_s *ServerStubAgent) Sign(call _gen_ipc.ServerCall, message []byte) (reply security.Signature, err error) {
+ reply, err = __gen_s.service.Sign(call, message)
+ return
+}
+
+func (__gen_s *ServerStubAgent) PublicKey(call _gen_ipc.ServerCall) (reply wire.PublicKey, err error) {
+ reply, err = __gen_s.service.PublicKey(call)
+ return
+}
diff --git a/security/agent/test.sh b/security/agent/test.sh
new file mode 100755
index 0000000..c932314
--- /dev/null
+++ b/security/agent/test.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# Test running an application using the agent.
+
+source "${VEYRON_ROOT}/environment/scripts/lib/shell_test.sh"
+
+build() {
+ local GO="${REPO_ROOT}/scripts/build/go"
+ "${GO}" build veyron/security/agent/agentd || shell_test::fail "line ${LINENO}: failed to build agentd"
+ "${GO}" build -o pingpong veyron/security/agent/test || shell_test::fail "line ${LINENO}: failed to build pingpong"
+}
+
+main() {
+ local workdir="$(shell::tmp_dir)"
+ cd "${workdir}"
+ build
+
+ shell_test::setup_server_test
+ shell_test::start_server ./pingpong --server
+ export VEYRON_PUBLICID_STORE="$(shell::tmp_dir)"
+ echo VEYRON_PUBLICID_STORE=$VEYRON_PUBLICID_STORE
+ ls $VEYRON_PUBLICID_STORE
+ ./agentd --v=4 ./pingpong || shell_test::fail "line ${LINENO}: ping"
+ local identity=$(./agentd bash -c 'echo $VEYRON_IDENTITY')
+ if [[ -n "${identity}" ]]; then
+ shel_test::fail "line ${LINENO}: identity preserved"
+ fi
+
+ shell_test::pass
+}
+
+main "$@"
diff --git a/security/agent/test/main.go b/security/agent/test/main.go
new file mode 100644
index 0000000..0d554fe
--- /dev/null
+++ b/security/agent/test/main.go
@@ -0,0 +1,70 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+
+ "veyron/lib/signals"
+ sflag "veyron/security/flag"
+ "veyron2/ipc"
+ "veyron2/rt"
+)
+
+var runServer = flag.Bool("server", false, "Whether to run in server mode")
+
+type pongd struct{}
+
+func (f *pongd) Ping(_ ipc.ServerContext, message string) (result string, err error) {
+ return "pong", nil
+}
+
+func clientMain() {
+ runtime := rt.Init()
+ log := runtime.Logger()
+ log.Info("Pinging...")
+
+ s, err := BindPingPong("pingpong")
+ if err != nil {
+ log.Fatal("error binding to server: ", err)
+ }
+
+ pong, err := s.Ping(runtime.NewContext(), "ping")
+ if err != nil {
+ log.Fatal("error pinging: ", err)
+ }
+ fmt.Println(pong)
+}
+
+func serverMain() {
+ r := rt.Init()
+ log := r.Logger()
+ s, err := r.NewServer()
+ if err != nil {
+ log.Fatal("failure creating server: ", err)
+ }
+ log.Info("Waiting for ping")
+
+ serverPong := NewServerPingPong(&pongd{})
+
+ if endpoint, err := s.Listen("tcp", "127.0.0.1:0"); err == nil {
+ fmt.Printf("Listening at: %v\n", endpoint)
+ } else {
+ log.Fatal("error listening to service: ", err)
+ }
+
+ if err := s.Serve("pingpong", ipc.LeafDispatcher(serverPong, sflag.NewAuthorizerOrDie())); err != nil {
+ log.Fatal("error serving service: ", err)
+ }
+
+ // Wait forever.
+ <-signals.ShutdownOnSignals()
+}
+
+func main() {
+ flag.Parse()
+ if *runServer {
+ serverMain()
+ } else {
+ clientMain()
+ }
+}
diff --git a/security/agent/test/wire.vdl b/security/agent/test/wire.vdl
new file mode 100644
index 0000000..0fc2ed4
--- /dev/null
+++ b/security/agent/test/wire.vdl
@@ -0,0 +1,6 @@
+package main
+
+// Simple service used in the agent tests.
+type PingPong interface {
+ Ping(message string) (string, error)
+}
diff --git a/security/agent/test/wire.vdl.go b/security/agent/test/wire.vdl.go
new file mode 100644
index 0000000..87378b1
--- /dev/null
+++ b/security/agent/test/wire.vdl.go
@@ -0,0 +1,186 @@
+// This file was auto-generated by the veyron vdl tool.
+// Source: wire.vdl
+
+package main
+
+import (
+ // The non-user imports are prefixed with "_gen_" to prevent collisions.
+ _gen_veyron2 "veyron2"
+ _gen_context "veyron2/context"
+ _gen_ipc "veyron2/ipc"
+ _gen_naming "veyron2/naming"
+ _gen_vdlutil "veyron2/vdl/vdlutil"
+ _gen_wiretype "veyron2/wiretype"
+)
+
+// TODO(bprosnitz) Remove this line once signatures are updated to use typevals.
+// It corrects a bug where _gen_wiretype is unused in VDL pacakges where only bootstrap types are used on interfaces.
+const _ = _gen_wiretype.TypeIDInvalid
+
+// Simple service used in the agent tests.
+// PingPong is the interface the client binds and uses.
+// PingPong_ExcludingUniversal is the interface without internal framework-added methods
+// to enable embedding without method collisions. Not to be used directly by clients.
+type PingPong_ExcludingUniversal interface {
+ Ping(ctx _gen_context.T, message string, opts ..._gen_ipc.CallOpt) (reply string, err error)
+}
+type PingPong interface {
+ _gen_ipc.UniversalServiceMethods
+ PingPong_ExcludingUniversal
+}
+
+// PingPongService is the interface the server implements.
+type PingPongService interface {
+ Ping(context _gen_ipc.ServerContext, message string) (reply string, err error)
+}
+
+// BindPingPong returns the client stub implementing the PingPong
+// interface.
+//
+// If no _gen_ipc.Client is specified, the default _gen_ipc.Client in the
+// global Runtime is used.
+func BindPingPong(name string, opts ..._gen_ipc.BindOpt) (PingPong, error) {
+ var client _gen_ipc.Client
+ switch len(opts) {
+ case 0:
+ // Do nothing.
+ case 1:
+ if clientOpt, ok := opts[0].(_gen_ipc.Client); opts[0] == nil || ok {
+ client = clientOpt
+ } else {
+ return nil, _gen_vdlutil.ErrUnrecognizedOption
+ }
+ default:
+ return nil, _gen_vdlutil.ErrTooManyOptionsToBind
+ }
+ stub := &clientStubPingPong{defaultClient: client, name: name}
+
+ return stub, nil
+}
+
+// NewServerPingPong creates a new server stub.
+//
+// It takes a regular server implementing the PingPongService
+// interface, and returns a new server stub.
+func NewServerPingPong(server PingPongService) interface{} {
+ return &ServerStubPingPong{
+ service: server,
+ }
+}
+
+// clientStubPingPong implements PingPong.
+type clientStubPingPong struct {
+ defaultClient _gen_ipc.Client
+ name string
+}
+
+func (__gen_c *clientStubPingPong) client(ctx _gen_context.T) _gen_ipc.Client {
+ if __gen_c.defaultClient != nil {
+ return __gen_c.defaultClient
+ }
+ return _gen_veyron2.RuntimeFromContext(ctx).Client()
+}
+
+func (__gen_c *clientStubPingPong) Ping(ctx _gen_context.T, message string, opts ..._gen_ipc.CallOpt) (reply string, err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client(ctx).StartCall(ctx, __gen_c.name, "Ping", []interface{}{message}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubPingPong) UnresolveStep(ctx _gen_context.T, opts ..._gen_ipc.CallOpt) (reply []string, err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client(ctx).StartCall(ctx, __gen_c.name, "UnresolveStep", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubPingPong) Signature(ctx _gen_context.T, opts ..._gen_ipc.CallOpt) (reply _gen_ipc.ServiceSignature, err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client(ctx).StartCall(ctx, __gen_c.name, "Signature", nil, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+func (__gen_c *clientStubPingPong) GetMethodTags(ctx _gen_context.T, method string, opts ..._gen_ipc.CallOpt) (reply []interface{}, err error) {
+ var call _gen_ipc.Call
+ if call, err = __gen_c.client(ctx).StartCall(ctx, __gen_c.name, "GetMethodTags", []interface{}{method}, opts...); err != nil {
+ return
+ }
+ if ierr := call.Finish(&reply, &err); ierr != nil {
+ err = ierr
+ }
+ return
+}
+
+// ServerStubPingPong wraps a server that implements
+// PingPongService and provides an object that satisfies
+// the requirements of veyron2/ipc.ReflectInvoker.
+type ServerStubPingPong struct {
+ service PingPongService
+}
+
+func (__gen_s *ServerStubPingPong) GetMethodTags(call _gen_ipc.ServerCall, method string) ([]interface{}, error) {
+ // TODO(bprosnitz) GetMethodTags() will be replaces with Signature().
+ // Note: This exhibits some weird behavior like returning a nil error if the method isn't found.
+ // This will change when it is replaced with Signature().
+ switch method {
+ case "Ping":
+ return []interface{}{}, nil
+ default:
+ return nil, nil
+ }
+}
+
+func (__gen_s *ServerStubPingPong) Signature(call _gen_ipc.ServerCall) (_gen_ipc.ServiceSignature, error) {
+ result := _gen_ipc.ServiceSignature{Methods: make(map[string]_gen_ipc.MethodSignature)}
+ result.Methods["Ping"] = _gen_ipc.MethodSignature{
+ InArgs: []_gen_ipc.MethodArgument{
+ {Name: "message", Type: 3},
+ },
+ OutArgs: []_gen_ipc.MethodArgument{
+ {Name: "", Type: 3},
+ {Name: "", Type: 65},
+ },
+ }
+
+ result.TypeDefs = []_gen_vdlutil.Any{
+ _gen_wiretype.NamedPrimitiveType{Type: 0x1, Name: "error", Tags: []string(nil)}}
+
+ return result, nil
+}
+
+func (__gen_s *ServerStubPingPong) UnresolveStep(call _gen_ipc.ServerCall) (reply []string, err error) {
+ if unresolver, ok := __gen_s.service.(_gen_ipc.Unresolver); ok {
+ return unresolver.UnresolveStep(call)
+ }
+ if call.Server() == nil {
+ return
+ }
+ var published []string
+ if published, err = call.Server().Published(); err != nil || published == nil {
+ return
+ }
+ reply = make([]string, len(published))
+ for i, p := range published {
+ reply[i] = _gen_naming.Join(p, call.Name())
+ }
+ return
+}
+
+func (__gen_s *ServerStubPingPong) Ping(call _gen_ipc.ServerCall, message string) (reply string, err error) {
+ reply, err = __gen_s.service.Ping(call, message)
+ return
+}
diff --git a/services/mgmt/application/applicationd/test.sh b/services/mgmt/application/applicationd/test.sh
index 3f7206a..3b1a56f 100755
--- a/services/mgmt/application/applicationd/test.sh
+++ b/services/mgmt/application/applicationd/test.sh
@@ -9,7 +9,7 @@
source "${VEYRON_ROOT}/environment/scripts/lib/shell_test.sh"
build() {
- local -r GO="${REPO_ROOT}/scripts/build/go"
+ local -r GO="${VEYRON_ROOT}/veyron/scripts/build/go"
"${GO}" build veyron/services/mgmt/application/applicationd || shell_test::fail "line ${LINENO}: failed to build 'applicationd'"
"${GO}" build veyron/tools/application || shell_test::fail "line ${LINENO}: failed to build 'application'"
}
diff --git a/services/mgmt/binary/binaryd/test.sh b/services/mgmt/binary/binaryd/test.sh
index 3763245..8252e36 100755
--- a/services/mgmt/binary/binaryd/test.sh
+++ b/services/mgmt/binary/binaryd/test.sh
@@ -9,7 +9,7 @@
source "${VEYRON_ROOT}/environment/scripts/lib/shell_test.sh"
build() {
- local -r GO="${REPO_ROOT}/scripts/build/go"
+ local -r GO="${VEYRON_ROOT}/veyron/scripts/build/go"
"${GO}" build veyron/services/mgmt/binary/binaryd || shell_test::fail "line ${LINENO}: failed to build 'binaryd'"
"${GO}" build veyron/tools/binary || shell_test::fail "line ${LINENO}: failed to build 'binary'"
}
diff --git a/services/mgmt/build/buildd/test.sh b/services/mgmt/build/buildd/test.sh
index 0cfce9b..68906d5 100755
--- a/services/mgmt/build/buildd/test.sh
+++ b/services/mgmt/build/buildd/test.sh
@@ -17,7 +17,7 @@
source "${VEYRON_ROOT}/environment/scripts/lib/shell_test.sh"
build() {
- local -r GO="${REPO_ROOT}/scripts/build/go"
+ local -r GO="${VEYRON_ROOT}/veyron/scripts/build/go"
"${GO}" build veyron/services/mgmt/build/buildd || shell_test::fail "line ${LINENO}: failed to build 'buildd'"
"${GO}" build veyron/tools/build || shell_test::fail "line ${LINENO}: failed to build 'build'"
}
diff --git a/services/mgmt/profile/profiled/test.sh b/services/mgmt/profile/profiled/test.sh
index 48c87fe..eccf535 100755
--- a/services/mgmt/profile/profiled/test.sh
+++ b/services/mgmt/profile/profiled/test.sh
@@ -10,7 +10,7 @@
source "${VEYRON_ROOT}/environment/scripts/lib/shell_test.sh"
build() {
- local -r GO="${REPO_ROOT}/scripts/build/go"
+ local -r GO="${VEYRON_ROOT}/veyron/scripts/build/go"
"${GO}" build veyron/services/mgmt/profile/profiled || shell_test::fail "line ${LINENO}: failed to build 'profiled'"
"${GO}" build veyron/tools/profile || shell_test::fail "line ${LINENO}: failed to build 'profile'"
}
diff --git a/services/mounttable/mounttabled/test.sh b/services/mounttable/mounttabled/test.sh
index 0f447e4..c0c60d1 100755
--- a/services/mounttable/mounttabled/test.sh
+++ b/services/mounttable/mounttabled/test.sh
@@ -12,7 +12,7 @@
source "${VEYRON_ROOT}/environment/scripts/lib/shell_test.sh"
build() {
- local -r GO="${REPO_ROOT}/scripts/build/go"
+ local -r GO="${VEYRON_ROOT}/veyron/scripts/build/go"
"${GO}" build veyron/services/mounttable/mounttabled || shell_test::fail "line ${LINENO}: failed to build mounttabled"
"${GO}" build veyron/tools/mounttable || shell_test::fail "line ${LINENO}: failed to build mounttable"
}
diff --git a/services/wsprd/app/app.go b/services/wsprd/app/app.go
index 7c970d3..1323ec8 100644
--- a/services/wsprd/app/app.go
+++ b/services/wsprd/app/app.go
@@ -5,6 +5,7 @@
import (
"bytes"
"encoding/json"
+ "flag"
"fmt"
"io"
"sync"
@@ -28,6 +29,19 @@
wiretype_build "veyron2/wiretype/build"
)
+// TODO(bjornick,nlacasse): Remove the retryTimeout flag once we able
+// to pass it in from javascript. For now all RPCs have the same
+// retryTimeout, set by command line flag.
+var retryTimeoutOpt ipc.CallOpt
+
+func init() {
+ // TODO(bjornick,nlacasse): Remove the retryTimeout flag once we able
+ // to pass it in from javascript. For now all RPCs have the same
+ // retryTimeout, set by command line flag.
+ retryTimeout := flag.Int("retry-timeout", 0, "Duration in seconds to retry starting an RPC call. 0 means never retry.")
+ retryTimeoutOpt = veyron2.RetryTimeoutOpt(time.Duration(*retryTimeout) * time.Second)
+}
+
// Temporary holder of RPC so that we can store the unprocessed args.
type veyronTempRPC struct {
Name string
@@ -201,10 +215,7 @@
return nil, verror.BadArgf("no client created")
}
methodName := lib.UppercaseFirstCharacter(msg.Method)
- // TODO(bjornick): Remove the retry option once we able to pass it in
- // from javascript. If we don't have this, then trying to make an
- // rpc to unknown name will hang.
- clientCall, err := c.client.StartCall(ctx, msg.Name, methodName, msg.InArgs, veyron2.RetryTimeoutOpt(0))
+ clientCall, err := c.client.StartCall(ctx, msg.Name, methodName, msg.InArgs, retryTimeoutOpt)
if err != nil {
return nil, fmt.Errorf("error starting call (name: %v, method: %v, args: %v): %v", msg.Name, methodName, msg.InArgs, err)
}
@@ -482,7 +493,7 @@
}
// Fetch and adapt signature from the SignatureManager
- sig, err := c.signatureManager.Signature(ctx, tempMsg.Name, c.client)
+ sig, err := c.signatureManager.Signature(ctx, tempMsg.Name, c.client, retryTimeoutOpt)
if err != nil {
return nil, nil, verror.Internalf("error getting service signature for %s: %v", tempMsg.Name, err)
}
diff --git a/services/wsprd/lib/signature_manager.go b/services/wsprd/lib/signature_manager.go
index 23d5e87..b31d4df 100644
--- a/services/wsprd/lib/signature_manager.go
+++ b/services/wsprd/lib/signature_manager.go
@@ -4,13 +4,12 @@
"sync"
"time"
- "veyron2"
"veyron2/context"
"veyron2/ipc"
)
type SignatureManager interface {
- Signature(ctx context.T, name string, client ipc.Client) (*ipc.ServiceSignature, error)
+ Signature(ctx context.T, name string, client ipc.Client, opts ...ipc.CallOpt) (*ipc.ServiceSignature, error)
}
// signatureManager can be used to discover the signature of a remote service
@@ -46,7 +45,7 @@
// signature uses the given client to fetch the signature for the given service name.
// It locks until it fetches the service signature from the remote server, if not a cache hit.
-func (sm *signatureManager) Signature(ctx context.T, name string, client ipc.Client) (*ipc.ServiceSignature, error) {
+func (sm *signatureManager) Signature(ctx context.T, name string, client ipc.Client, opts ...ipc.CallOpt) (*ipc.ServiceSignature, error) {
sm.Lock()
defer sm.Unlock()
@@ -56,10 +55,7 @@
}
// cache expired or not found, fetch it from the remote server
- // TODO(bjornick): Remove the retry option once we able to pass it in
- // from javascript. If we don't have this, then trying to make an
- // rpc to unknown name will hang.
- signatureCall, err := client.StartCall(ctx, name, "Signature", []interface{}{}, veyron2.RetryTimeoutOpt(0))
+ signatureCall, err := client.StartCall(ctx, name, "Signature", []interface{}{}, opts...)
if err != nil {
return nil, err
}
diff --git a/tools/identity/test.sh b/tools/identity/test.sh
index e5b4277..30e6ecc 100755
--- a/tools/identity/test.sh
+++ b/tools/identity/test.sh
@@ -11,7 +11,7 @@
main() {
# Build binaries.
cd "${TMPDIR}"
- local -r GO="${REPO_ROOT}/scripts/build/go"
+ local -r GO="${VEYRON_ROOT}/veyron/scripts/build/go"
"${GO}" build veyron/tools/identity || shell_test::fail "line ${LINENO}: failed to build identity"
./identity print >/dev/null || shell_test::fail "line ${LINENO}: print failed"
diff --git a/tools/playground/.gitignore b/tools/playground/.gitignore
new file mode 100644
index 0000000..a25285a
--- /dev/null
+++ b/tools/playground/.gitignore
@@ -0,0 +1 @@
+builder/netrc
diff --git a/tools/playground/builder/netrc b/tools/playground/builder/netrc
deleted file mode 100644
index 2cb442c..0000000
--- a/tools/playground/builder/netrc
+++ /dev/null
@@ -1,2 +0,0 @@
-machine veyron.googlesource.com login git-nlacasse.google.com password 1/jK0LNfz-nCtEoI2zixqeuGJww3CIlfN_vY31eVDRMck
-machine veyron-review.googlesource.com login git-nlacasse.google.com password 1/jK0LNfz-nCtEoI2zixqeuGJww3CIlfN_vY31eVDRMck
diff --git a/tools/playground/builder/services.go b/tools/playground/builder/services.go
index ded83f9..2ad1b3a 100644
--- a/tools/playground/builder/services.go
+++ b/tools/playground/builder/services.go
@@ -6,19 +6,42 @@
"fmt"
"io"
"log"
+ "math/rand"
"os"
"path"
"regexp"
"strconv"
+ "syscall"
"time"
)
var (
- proxyPort = 1234
- proxyName = "proxy"
- wsprBasePort = 1235
+ proxyName = "proxy"
)
+// Note: This was copied from veyron/go/src/veyron/tools/findunusedport.
+// I would like to be able to import that package directly, but it defines a
+// main(), so can't be imported. An alternative solution would be to call the
+// 'findunusedport' binary, but that would require starting another process and
+// parsing the output. It seemed simpler to just copy the function here.
+func findUnusedPort() (int, error) {
+ rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
+ for i := 0; i < 1000; i++ {
+ port := 1024 + rnd.Int31n(64512)
+ fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
+ if err != nil {
+ continue
+ }
+ sa := &syscall.SockaddrInet4{Port: int(port)}
+ if err := syscall.Bind(fd, sa); err != nil {
+ continue
+ }
+ syscall.Close(fd)
+ return int(port), nil
+ }
+ return 0, fmt.Errorf("Can't find unused port.")
+}
+
// startMount starts a mounttabled process, and sets the NAMESPACE_ROOT env
// variable to the mounttable's location. We run one mounttabled process for
// the entire environment.
@@ -60,7 +83,11 @@
// startProxy starts a proxyd process. We run one proxyd process for the
// entire environment.
func startProxy() (proc *os.Process, err error) {
- cmd := makeCmdJsonEvent("", "proxyd", "-name="+proxyName, "-address=:"+strconv.Itoa(proxyPort))
+ port, err := findUnusedPort()
+ if err != nil {
+ return nil, err
+ }
+ cmd := makeCmdJsonEvent("", "proxyd", "-name="+proxyName, "-address=localhost:"+strconv.Itoa(port))
err = cmd.Start()
if err != nil {
return nil, err
@@ -69,15 +96,22 @@
}
// startWspr starts a wsprd process. We run one wsprd process for each
-// javascript file being run. The 'index' argument is used to pick a distinct
-// port for each wsprd process.
+// javascript file being run.
func startWspr(f *codeFile) (proc *os.Process, port int, err error) {
- port = wsprBasePort + f.index
+ port, err = findUnusedPort()
+ if err != nil {
+ return nil, port, err
+ }
cmd := makeCmdJsonEvent(f.Name,
"wsprd",
"-v=-1",
"-vproxy="+proxyName,
"-port="+strconv.Itoa(port),
+ // Retry starting RPC calls for 3 seconds.
+ // TODO(nlacasse): Remove this when javascript can tell wspr
+ // how long to retry for. Right now its a global setting in
+ // wspr.
+ "-retry-timeout=3",
// The identd server won't be used, so pass a fake name.
"-identd=/unused")
diff --git a/tools/playground/builder/vbuild.go b/tools/playground/builder/vbuild.go
index cc9188e..df63c38 100644
--- a/tools/playground/builder/vbuild.go
+++ b/tools/playground/builder/vbuild.go
@@ -25,7 +25,7 @@
"veyron/tools/playground/event"
)
-const RUN_TIMEOUT = time.Second
+const runTimeout = 3 * time.Second
var (
verbose = flag.Bool("v", false, "Verbose mode")
@@ -153,7 +153,7 @@
log.Fatal(err)
}
- mt, err := startMount(RUN_TIMEOUT)
+ mt, err := startMount(runTimeout)
if err != nil {
log.Fatal(err)
}
@@ -222,12 +222,13 @@
}
}
- timeout := time.After(RUN_TIMEOUT)
+ timeout := time.After(runTimeout)
for running > 0 {
select {
case <-timeout:
writeEvent("", "Playground exceeded deadline.", "stderr")
+ stopAll(files)
case status := <-exit:
if status.err == nil {
writeEvent(status.name, "Exited.", "stdout")
@@ -376,6 +377,17 @@
cmd := exec.Command(prog, args...)
cmd.Env = os.Environ()
+ // TODO(nlacasse): There is a bug in this code which results in
+ // "read |0: bad file descriptor".
+ // The error seems to be caused by our use of cmd.StdoutPipe/StderrPipe
+ // and cmd.Wait. In particular, we seem to be calling Wait after the
+ // pipes have been closed.
+ // See: http://stackoverflow.com/questions/20134095/why-do-i-get-bad-file-descriptor-in-this-go-program-using-stderr-and-ioutil-re
+ // and https://code.google.com/p/go/issues/detail?id=2266
+ //
+ // One solution is to wrap cmd.Start/Run, so that wait is never called
+ // before the pipes are closed.
+
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
diff --git a/tools/playground/test.sh b/tools/playground/test.sh
index ccf8d3c..90d4e8f 100755
--- a/tools/playground/test.sh
+++ b/tools/playground/test.sh
@@ -4,8 +4,20 @@
source "${VEYRON_ROOT}/environment/scripts/lib/shell_test.sh"
+install_veyron_js() {
+ # This installs the veyron.js library, and makes it accessable to javascript
+ # files in the veyron playground test folder under the module name 'veyron'.
+ #
+ # TODO(nlacasse): Once veyron.js is installed in a public npm registry, this
+ # should be replaced with just "npm install veyron".
+ pushd "${VEYRON_ROOT}/veyron.js"
+ "${VEYRON_ROOT}/environment/cout/node/bin/npm" link
+ popd
+ "${VEYRON_ROOT}/environment/cout/node/bin/npm" link veyron
+}
+
build() {
- local -r GO="${REPO_ROOT}/scripts/build/go"
+ local -r GO="${VEYRON_ROOT}/veyron/scripts/build/go"
"${GO}" build veyron/tools/identity || shell_test::fail "line ${LINENO}: failed to build 'identity'"
"${GO}" build veyron/services/proxy/proxyd || shell_test::fail "line ${LINENO}: failed to build 'proxyd'"
"${GO}" build veyron/services/mounttable/mounttabled || shell_test::fail "line ${LINENO}: failed to build 'mounttabled'"
@@ -35,10 +47,11 @@
main() {
cd $(shell::tmp_dir)
build
+ install_veyron_js
- local -r DIR="${REPO_ROOT}/go/src/veyron/tools/playground/testdata"
+ local -r DIR="${VEYRON_ROOT}/veyron/go/src/veyron/tools/playground/testdata"
- export GOPATH="$(pwd)":$VEYRON_ROOT/veyron/go
+ export GOPATH="$(pwd)":"${VEYRON_ROOT}/veyron/go"
export PATH="$(pwd):$PATH"
# Test without identities