Merge "mounttable: Remove the MountX call."
diff --git a/cmd/gclogs/doc.go b/cmd/gclogs/doc.go
index 2fd759b..eb15ca1 100644
--- a/cmd/gclogs/doc.go
+++ b/cmd/gclogs/doc.go
@@ -31,5 +31,50 @@
".*test".
-verbose=false
If true, each deleted file is shown on stdout.
+
+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
+ -vanadium.i18n_catalogue=
+ 18n catalogue files to load, comma separated
+ -veyron.acl.file=map[]
+ specify an acl file as <name>:<aclfile>
+ -veyron.acl.literal=
+ explicitly specify the runtime acl as a JSON-encoded access.Permissions.
+ Overrides all --veyron.acl.file flags.
+ -veyron.credentials=
+ directory to use for storing security credentials
+ -veyron.namespace.root=[/ns.dev.v.io: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=
+ address to listen on
+ -veyron.tcp.protocol=wsh
+ protocol to listen with
+ -veyron.vtrace.cache_size=1024
+ The number of vtrace traces to store in memory.
+ -veyron.vtrace.collect_regexp=
+ Spans and annotations that match this regular expression will trigger trace
+ collection.
+ -veyron.vtrace.dump_on_shutdown=true
+ 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
*/
package main
diff --git a/cmd/gclogs/gclogs.go b/cmd/gclogs/gclogs.go
index d19b8b0..1939b25 100644
--- a/cmd/gclogs/gclogs.go
+++ b/cmd/gclogs/gclogs.go
@@ -13,7 +13,9 @@
"regexp"
"time"
+ "v.io/v23"
"v.io/x/lib/cmdline"
+ _ "v.io/x/ref/profiles/static"
)
var (
@@ -49,6 +51,9 @@
}
func garbageCollectLogs(cmd *cmdline.Command, args []string) error {
+ _, shutdown := v23.Init()
+ defer shutdown()
+
if len(args) == 0 {
cmd.UsageErrorf("gclogs requires at least one argument")
}
diff --git a/cmd/gclogs/main.go b/cmd/gclogs/main.go
index 8468057..b8e066f 100644
--- a/cmd/gclogs/main.go
+++ b/cmd/gclogs/main.go
@@ -7,7 +7,9 @@
package main
-import "os"
+import (
+ "os"
+)
func main() {
os.Exit(cmdGCLogs.Main())
diff --git a/cmd/uniqueid/doc.go b/cmd/uniqueid/doc.go
index 79eeba3..67b7de5 100644
--- a/cmd/uniqueid/doc.go
+++ b/cmd/uniqueid/doc.go
@@ -18,6 +18,51 @@
help Display help for commands or topics
Run "uniqueid 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
+ -vanadium.i18n_catalogue=
+ 18n catalogue files to load, comma separated
+ -veyron.acl.file=map[]
+ specify an acl file as <name>:<aclfile>
+ -veyron.acl.literal=
+ explicitly specify the runtime acl as a JSON-encoded access.Permissions.
+ Overrides all --veyron.acl.file flags.
+ -veyron.credentials=
+ directory to use for storing security credentials
+ -veyron.namespace.root=[/ns.dev.v.io: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=
+ address to listen on
+ -veyron.tcp.protocol=wsh
+ protocol to listen with
+ -veyron.vtrace.cache_size=1024
+ The number of vtrace traces to store in memory.
+ -veyron.vtrace.collect_regexp=
+ Spans and annotations that match this regular expression will trigger trace
+ collection.
+ -veyron.vtrace.dump_on_shutdown=true
+ 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
+
Uniqueid Generate
Generates unique ids and outputs them to standard out.
diff --git a/cmd/uniqueid/main.go b/cmd/uniqueid/main.go
index 1d6b598..3f8a16a 100644
--- a/cmd/uniqueid/main.go
+++ b/cmd/uniqueid/main.go
@@ -14,14 +14,24 @@
"os"
"regexp"
+ "v.io/v23"
"v.io/v23/uniqueid"
"v.io/x/lib/cmdline"
+ _ "v.io/x/ref/profiles/static"
)
func main() {
os.Exit(cmdUniqueId.Main())
}
+func runHelper(run cmdline.Runner) cmdline.Runner {
+ return func(cmd *cmdline.Command, args []string) error {
+ _, shutdown := v23.Init()
+ defer shutdown()
+ return run(cmd, args)
+ }
+}
+
var cmdUniqueId = &cmdline.Command{
Name: "uniqueid",
Short: "Generates UniqueIds.",
@@ -34,7 +44,7 @@
}
var cmdGenerate = &cmdline.Command{
- Run: runGenerate,
+ Run: runHelper(runGenerate),
Name: "generate",
Short: "Generates UniqueIds",
Long: `
@@ -45,7 +55,7 @@
}
var cmdInject = &cmdline.Command{
- Run: runInject,
+ Run: runHelper(runInject),
Name: "inject",
Short: "Injects UniqueIds into existing files",
Long: `
diff --git a/cmd/vdl/arith_test.go b/cmd/vdl/arith_test.go
index 5a4e54a..0c2600f 100644
--- a/cmd/vdl/arith_test.go
+++ b/cmd/vdl/arith_test.go
@@ -22,7 +22,6 @@
"v.io/x/ref/lib/vdl/testdata/arith"
"v.io/x/ref/lib/vdl/testdata/base"
- _ "v.io/x/ref/profiles"
"v.io/x/ref/test"
)
diff --git a/cmd/vdl/doc.go b/cmd/vdl/doc.go
index 4cc7b5f..0e06c1f 100644
--- a/cmd/vdl/doc.go
+++ b/cmd/vdl/doc.go
@@ -39,6 +39,51 @@
-vdl.config=vdl.config
Basename of the optional per-package config file.
+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
+ -vanadium.i18n_catalogue=
+ 18n catalogue files to load, comma separated
+ -veyron.acl.file=map[]
+ specify an acl file as <name>:<aclfile>
+ -veyron.acl.literal=
+ explicitly specify the runtime acl as a JSON-encoded access.Permissions.
+ Overrides all --veyron.acl.file flags.
+ -veyron.credentials=
+ directory to use for storing security credentials
+ -veyron.namespace.root=[/ns.dev.v.io: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=
+ address to listen on
+ -veyron.tcp.protocol=wsh
+ protocol to listen with
+ -veyron.vtrace.cache_size=1024
+ The number of vtrace traces to store in memory.
+ -veyron.vtrace.collect_regexp=
+ Spans and annotations that match this regular expression will trigger trace
+ collection.
+ -veyron.vtrace.dump_on_shutdown=true
+ 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
+
Vdl Generate
Generate compiles packages and their transitive dependencies, and generates code
diff --git a/cmd/vdl/main.go b/cmd/vdl/main.go
index 2e7666f..f7e802d 100644
--- a/cmd/vdl/main.go
+++ b/cmd/vdl/main.go
@@ -16,6 +16,7 @@
"path/filepath"
"strings"
+ "v.io/v23"
"v.io/v23/vdlroot/vdltool"
"v.io/x/lib/cmdline"
"v.io/x/lib/textutil"
@@ -25,6 +26,7 @@
"v.io/x/ref/lib/vdl/codegen/javascript"
"v.io/x/ref/lib/vdl/compile"
"v.io/x/ref/lib/vdl/vdlutil"
+ _ "v.io/x/ref/profiles/static"
)
func init() {
@@ -47,6 +49,9 @@
// targets, and calls the supplied run function.
func runHelper(run func(targets []*build.Package, env *compile.Env)) func(cmd *cmdline.Command, args []string) error {
return func(cmd *cmdline.Command, args []string) error {
+ _, shutdown := v23.Init()
+ defer shutdown()
+
if flagVerbose {
vdlutil.SetVerbose()
}
diff --git a/cmd/vom/doc.go b/cmd/vom/doc.go
index 842764d..4fc7d25 100644
--- a/cmd/vom/doc.go
+++ b/cmd/vom/doc.go
@@ -17,6 +17,51 @@
help Display help for commands or topics
Run "vom 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
+ -vanadium.i18n_catalogue=
+ 18n catalogue files to load, comma separated
+ -veyron.acl.file=map[]
+ specify an acl file as <name>:<aclfile>
+ -veyron.acl.literal=
+ explicitly specify the runtime acl as a JSON-encoded access.Permissions.
+ Overrides all --veyron.acl.file flags.
+ -veyron.credentials=
+ directory to use for storing security credentials
+ -veyron.namespace.root=[/ns.dev.v.io: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=
+ address to listen on
+ -veyron.tcp.protocol=wsh
+ protocol to listen with
+ -veyron.vtrace.cache_size=1024
+ The number of vtrace traces to store in memory.
+ -veyron.vtrace.collect_regexp=
+ Spans and annotations that match this regular expression will trigger trace
+ collection.
+ -veyron.vtrace.dump_on_shutdown=true
+ 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
+
Vom Decode
Decode decodes data encoded in the vom format. If no arguments are provided,
diff --git a/cmd/vom/vom.go b/cmd/vom/vom.go
index 3126f90..2e2d2ce 100644
--- a/cmd/vom/vom.go
+++ b/cmd/vom/vom.go
@@ -17,15 +17,25 @@
"strings"
"unicode"
+ "v.io/v23"
"v.io/v23/vdl"
"v.io/v23/vom"
"v.io/x/lib/cmdline"
+ _ "v.io/x/ref/profiles/static"
)
func main() {
os.Exit(cmdVom.Main())
}
+func runHelper(run cmdline.Runner) cmdline.Runner {
+ return func(cmd *cmdline.Command, args []string) error {
+ _, shutdown := v23.Init()
+ defer shutdown()
+ return run(cmd, args)
+ }
+}
+
var cmdVom = &cmdline.Command{
Name: "vom",
Short: "Vanadium Object Marshaling debugging tool",
@@ -36,7 +46,7 @@
}
var cmdDecode = &cmdline.Command{
- Run: runDecode,
+ Run: runHelper(runDecode),
Name: "decode",
Short: "Decode data encoded in the vom format",
Long: `
@@ -53,7 +63,7 @@
}
var cmdDump = &cmdline.Command{
- Run: runDump,
+ Run: runHelper(runDump),
Name: "dump",
Short: "Dump data encoded in the vom format into formatted output",
Long: `
diff --git a/cmd/vomtestgen/doc.go b/cmd/vomtestgen/doc.go
index 78c9098..e21085b 100644
--- a/cmd/vomtestgen/doc.go
+++ b/cmd/vomtestgen/doc.go
@@ -30,5 +30,50 @@
Comma-separated list of valid VDL file name extensions.
-max_errors=-1
Stop processing after this many errors, or -1 for unlimited.
+
+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
+ -vanadium.i18n_catalogue=
+ 18n catalogue files to load, comma separated
+ -veyron.acl.file=map[]
+ specify an acl file as <name>:<aclfile>
+ -veyron.acl.literal=
+ explicitly specify the runtime acl as a JSON-encoded access.Permissions.
+ Overrides all --veyron.acl.file flags.
+ -veyron.credentials=
+ directory to use for storing security credentials
+ -veyron.namespace.root=[/ns.dev.v.io: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=
+ address to listen on
+ -veyron.tcp.protocol=wsh
+ protocol to listen with
+ -veyron.vtrace.cache_size=1024
+ The number of vtrace traces to store in memory.
+ -veyron.vtrace.collect_regexp=
+ Spans and annotations that match this regular expression will trigger trace
+ collection.
+ -veyron.vtrace.dump_on_shutdown=true
+ 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
*/
package main
diff --git a/cmd/vomtestgen/generate.go b/cmd/vomtestgen/generate.go
index ca4a434..63f9f88 100644
--- a/cmd/vomtestgen/generate.go
+++ b/cmd/vomtestgen/generate.go
@@ -13,6 +13,7 @@
"path/filepath"
"strings"
+ "v.io/v23"
"v.io/v23/vdl"
"v.io/v23/vom"
"v.io/x/lib/cmdline"
@@ -20,6 +21,7 @@
"v.io/x/ref/lib/vdl/codegen"
"v.io/x/ref/lib/vdl/codegen/vdlgen"
"v.io/x/ref/lib/vdl/compile"
+ _ "v.io/x/ref/profiles/static"
)
const (
@@ -63,6 +65,9 @@
}
func runGenerate(cmd *cmdline.Command, args []string) error {
+ _, shutdown := v23.Init()
+ defer shutdown()
+
debug := new(bytes.Buffer)
defer dumpDebug(cmd.Stderr(), debug)
env := compile.NewEnv(optGenMaxErrors)
diff --git a/cmd/vomtestgen/main.go b/cmd/vomtestgen/main.go
index ece59fa..fa43955 100644
--- a/cmd/vomtestgen/main.go
+++ b/cmd/vomtestgen/main.go
@@ -7,7 +7,9 @@
package main
-import "os"
+import (
+ "os"
+)
func main() {
os.Exit(cmdGenerate.Main())
diff --git a/cmd/vrun/vrun.go b/cmd/vrun/vrun.go
index e115e98..820ace6 100644
--- a/cmd/vrun/vrun.go
+++ b/cmd/vrun/vrun.go
@@ -5,7 +5,6 @@
package main
import (
- "flag"
"os"
"path/filepath"
"syscall"
@@ -15,6 +14,7 @@
"v.io/x/ref/lib/flags/consts"
"v.io/x/ref/security/agent"
"v.io/x/ref/security/agent/keymgr"
+ isecurity "v.io/x/ref/services/security"
"v.io/v23"
"v.io/v23/context"
@@ -24,8 +24,11 @@
_ "v.io/x/ref/profiles"
)
-var durationFlag time.Duration
-var nameOverride string
+var (
+ durationFlag time.Duration
+ name string
+ role string
+)
var cmdVrun = &cmdline.Command{
Run: vrun,
@@ -39,10 +42,9 @@
syscall.CloseOnExec(3)
syscall.CloseOnExec(4)
- flag.DurationVar(&durationFlag, "duration", 1*time.Hour, "Duration for the blessing.")
- flag.StringVar(&nameOverride, "name", "", "Name to use for the blessing. Uses the command name if unset.")
cmdVrun.Flags.DurationVar(&durationFlag, "duration", 1*time.Hour, "Duration for the blessing.")
- cmdVrun.Flags.StringVar(&nameOverride, "name", "", "Name to use for the blessing. Uses the command name if unset.")
+ cmdVrun.Flags.StringVar(&name, "name", "", "Name to use for the blessing. Uses the command name if unset.")
+ cmdVrun.Flags.StringVar(&role, "role", "", "Role object from which to request the blessing. If set, the blessings from this role server are used and --name is ignored. If not set, the default blessings of the calling principal are extended with --name.")
os.Exit(cmdVrun.Main())
}
@@ -58,10 +60,30 @@
if err != nil {
return err
}
- err = bless(ctx, principal, filepath.Base(args[0]))
- if err != nil {
- return err
+ if len(role) == 0 {
+ if len(name) == 0 {
+ name = filepath.Base(args[0])
+ }
+ if err := bless(ctx, principal, name); err != nil {
+ return err
+ }
+ } else {
+ // The role server expects the client's blessing name to end
+ // with RoleSuffix. This is to avoid accidentally granting role
+ // access to anything else that might have been blessed by the
+ // same principal.
+ if err := bless(ctx, principal, isecurity.RoleSuffix); err != nil {
+ return err
+ }
+ rCtx, err := v23.SetPrincipal(ctx, principal)
+ if err != nil {
+ return err
+ }
+ if err := setupRoleBlessings(rCtx, role); err != nil {
+ return err
+ }
}
+
return doExec(args, conn)
}
@@ -71,9 +93,6 @@
vlog.Errorf("Couldn't create caveat")
return err
}
- if 0 != len(nameOverride) {
- name = nameOverride
- }
rp := v23.GetPrincipal(ctx)
blessing, err := rp.Bless(p.PublicKey(), rp.BlessingStore().Default(), name, caveat)
@@ -139,3 +158,21 @@
}
return principal, conn, nil
}
+
+func setupRoleBlessings(ctx *context.T, role string) error {
+ b, err := isecurity.RoleClient(role).SeekBlessings(ctx)
+ if err != nil {
+ return err
+ }
+ p := v23.GetPrincipal(ctx)
+ // TODO(rthellend,ashankar): Revisit this configuration.
+ // SetDefault: Should we expect users to want to act as a server on behalf of the role (by default?)
+ // AllPrincipals: Do we not want to be discriminating about which services we use the role blessing at.
+ if err := p.BlessingStore().SetDefault(b); err != nil {
+ return err
+ }
+ if _, err := p.BlessingStore().Set(b, security.AllPrincipals); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/examples/tunnel/tunneld/main.go b/examples/tunnel/tunneld/main.go
index 090c416..fb20a8b 100644
--- a/examples/tunnel/tunneld/main.go
+++ b/examples/tunnel/tunneld/main.go
@@ -54,14 +54,9 @@
defer server.Stop()
listenSpec := v23.GetListenSpec(ctx)
- eps, err := server.Listen(listenSpec)
- if err != nil {
+ if _, err := server.Listen(listenSpec); err != nil {
vlog.Fatalf("Listen(%v) failed: %v", listenSpec, err)
}
- vlog.Infof("Listening on: %v", eps)
- if len(eps) > 0 {
- fmt.Printf("NAME=%s\n", eps[0].Name())
- }
hwaddr, err := firstHardwareAddrInUse()
if err != nil {
vlog.Fatalf("Couldn't find a good hw address: %v", err)
@@ -85,6 +80,11 @@
if !published {
vlog.Fatalf("Failed to publish with any of %v", names)
}
+ status := server.Status()
+ vlog.Infof("Listening on: %v", status.Endpoints)
+ if len(status.Endpoints) > 0 {
+ fmt.Printf("NAME=%s\n", status.Endpoints[0].Name())
+ }
vlog.Infof("Published as %v", names)
<-signals.ShutdownOnSignals(ctx)
diff --git a/lib/exec/child.go b/lib/exec/child.go
index cf2a141..b80033c 100644
--- a/lib/exec/child.go
+++ b/lib/exec/child.go
@@ -6,19 +6,22 @@
import (
"encoding/binary"
- "errors"
"io"
"os"
"strconv"
"sync"
"unicode/utf8"
+ "v.io/v23/verror"
"v.io/x/ref/lib/exec/consts"
)
var (
- ErrNoVersion = errors.New(consts.ExecVersionVariable + " environment variable missing")
- ErrUnsupportedVersion = errors.New("Unsupported version of v.io/x/ref/lib/exec request by " + consts.ExecVersionVariable + " environment variable")
+ ErrNoVersion = verror.Register(pkgPath+".ErrNoVersion", verror.NoRetry, "{1:}{2:} "+consts.ExecVersionVariable+" environment variable missing{:_}")
+ ErrUnsupportedVersion = verror.Register(pkgPath+".ErrUnsupportedVersion", verror.NoRetry, "{1:}{2:} Unsupported version of v.io/x/ref/lib/exec request by "+consts.ExecVersionVariable+" environment variable{:_}")
+
+ errDifferentStatusSent = verror.Register(pkgPath+".errDifferentStatusSent", verror.NoRetry, "{1:}{2:} A different status: {3} has already been sent{:_}")
+ errPartialRead = verror.Register(pkgPath+".PartialRead", verror.NoRetry, "{1:}{2:} partial read{:_}")
)
type ChildHandle struct {
@@ -92,7 +95,7 @@
_, c.statusErr = c.statusPipe.Write(toWrite)
c.statusPipe.Close()
} else if c.sentStatus != status {
- return errors.New("A different status: " + c.sentStatus + " has already been sent.")
+ return verror.New(errDifferentStatusSent, nil, c.sentStatus)
}
return c.statusErr
}
@@ -123,11 +126,11 @@
// version #s.
switch os.Getenv(consts.ExecVersionVariable) {
case "":
- return nil, ErrNoVersion
+ return nil, verror.New(ErrNoVersion, nil)
case version1:
os.Setenv(consts.ExecVersionVariable, "")
default:
- return nil, ErrUnsupportedVersion
+ return nil, verror.New(ErrUnsupportedVersion, nil)
}
dataPipe := os.NewFile(3, "data_rd")
serializedConfig, err := decodeString(dataPipe)
@@ -160,7 +163,7 @@
if err != nil {
return "", err
} else {
- return "", errors.New("partial read")
+ return "", verror.New(errPartialRead, nil)
}
}
return string(data), nil
diff --git a/lib/exec/exec_test.go b/lib/exec/exec_test.go
index b930bd7..0eaf7af 100644
--- a/lib/exec/exec_test.go
+++ b/lib/exec/exec_test.go
@@ -17,6 +17,7 @@
"time"
"unicode/utf8"
+ "v.io/v23/verror"
vexec "v.io/x/ref/lib/exec"
"v.io/x/ref/lib/exec/consts"
// Use mock timekeeper to avoid actually sleeping during the test.
@@ -160,7 +161,7 @@
func TestNoVersion(t *testing.T) {
// Make sure that Init correctly tests for the presence of VEXEC_VERSION
_, err := vexec.GetChildHandle()
- if err != vexec.ErrNoVersion {
+ if verror.ErrorID(err) != vexec.ErrNoVersion.ID {
t.Errorf("Should be missing Version")
}
}
@@ -266,7 +267,7 @@
cmd := helperCommand(name, "failed", "to", "start")
ph := vexec.NewParentHandle(cmd)
err := waitForReady(t, cmd, name, 4, ph)
- if err == nil || err.Error() != "failed to start" {
+ if err == nil || !strings.Contains(err.Error(), "failed to start") {
t.Errorf("unexpected error: %v", err)
}
}
@@ -276,7 +277,7 @@
cmd := helperCommand(name, "invalid", "utf8", string([]byte{0xFF}), "in", string([]byte{0xFC}), "error", "message")
ph := vexec.NewParentHandle(cmd)
err := waitForReady(t, cmd, name, 4, ph)
- if err == nil || err.Error() != "invalid utf8 "+string(utf8.RuneError)+" in "+string(utf8.RuneError)+" error message" {
+ if err == nil || !strings.Contains(err.Error(), "invalid utf8 "+string(utf8.RuneError)+" in "+string(utf8.RuneError)+" error message") {
t.Errorf("unexpected error: %v", err)
}
}
@@ -288,7 +289,7 @@
stderr, _ := cmd.StderrPipe()
ph := vexec.NewParentHandle(cmd)
err := waitForReady(t, cmd, name, 1, ph)
- if err != vexec.ErrTimeout {
+ if verror.ErrorID(err) != vexec.ErrTimeout.ID {
t.Errorf("Failed to get timeout: got %v\n", err)
} else {
// block waiting for error from child
@@ -322,7 +323,7 @@
tk.AdvanceTime(toWait)
}()
err = waitForReady(t, cmd, name, 1, ph)
- if err != vexec.ErrTimeout {
+ if verror.ErrorID(err) != vexec.ErrTimeout.ID {
t.Errorf("Failed to get timeout: got %v\n", err)
} else {
// After the parent timed out, wake up the child and let it
@@ -449,7 +450,7 @@
<-tk.Requests()
tk.AdvanceTime(2 * time.Second)
}()
- if got, want := ph.Wait(time.Second), vexec.ErrTimeout; got == nil || got.Error() != want.Error() {
+ if got, want := ph.Wait(time.Second), vexec.ErrTimeout.ID; got == nil || verror.ErrorID(got) != want {
t.Errorf("Wait returned %v, wanted %v instead", got, want)
}
if got, want := ph.Clean(), "signal: killed"; got == nil || got.Error() != want {
diff --git a/lib/exec/noprotocol_test.go b/lib/exec/noprotocol_test.go
index 8e9aa21..78564c9 100644
--- a/lib/exec/noprotocol_test.go
+++ b/lib/exec/noprotocol_test.go
@@ -12,6 +12,7 @@
"testing"
"time"
+ "v.io/v23/verror"
vexec "v.io/x/ref/lib/exec"
"v.io/x/ref/lib/exec/consts"
)
@@ -23,7 +24,7 @@
if err := ph.Start(); err != nil {
t.Fatal(err)
}
- if got, want := ph.WaitForReady(time.Minute), vexec.ErrNotUsingProtocol; got != want {
+ if got, want := ph.WaitForReady(time.Minute), vexec.ErrNotUsingProtocol.ID; verror.ErrorID(got) != want {
t.Fatalf("got %v, want %v", got, want)
}
re := regexp.MustCompile(fmt.Sprintf(".*%s=.*", consts.ExecVersionVariable))
diff --git a/lib/exec/parent.go b/lib/exec/parent.go
index fc11a2b..d011cfb 100644
--- a/lib/exec/parent.go
+++ b/lib/exec/parent.go
@@ -7,7 +7,6 @@
import (
"bytes"
"encoding/binary"
- "errors"
"fmt"
"io"
"os"
@@ -18,17 +17,27 @@
"syscall"
"time"
+ "v.io/v23/verror"
+
"v.io/x/lib/vlog"
"v.io/x/ref/lib/exec/consts"
"v.io/x/ref/lib/timekeeper"
)
+const pkgPath = "v.io/x/ref/lib/exec"
+
var (
- ErrAuthTimeout = errors.New("timeout in auth handshake")
- ErrTimeout = errors.New("timeout waiting for child")
- ErrSecretTooLarge = errors.New("secret is too large")
- ErrNotUsingProtocol = errors.New("not using parent/child exec protocol")
+ ErrAuthTimeout = verror.Register(pkgPath+".ErrAuthTimeout", verror.NoRetry, "{1:}{2:} timeout in auth handshake{:_}")
+ ErrTimeout = verror.Register(pkgPath+".ErrTimeout", verror.NoRetry, "{1:}{2:} timeout waiting for child{:_}")
+ ErrSecretTooLarge = verror.Register(pkgPath+".ErrSecretTooLarge", verror.NoRetry, "{1:}{2:} secret is too large{:_}")
+ ErrNotUsingProtocol = verror.Register(pkgPath+".ErrNotUsingProtocol", verror.NoRetry, "{1:}{2:} not using parent/child exec protocol{:_}")
+
+ errFailedStatus = verror.Register(pkgPath+".errFailedStatus", verror.NoRetry, "{1:}{2:} {_}")
+ errUnrecognizedStatus = verror.Register(pkgPath+".errUnrecognizedStatus", verror.NoRetry, "{1:}{2:} unrecognised status from subprocess{:_}")
+ errUnexpectedType = verror.Register(pkgPath+".errUnexpectedType", verror.NoRetry, "{1:}{2:} unexpected type {3}{:_}")
+ errNoSuchProcess = verror.Register(pkgPath+".errNoSuchProcess", verror.NoRetry, "{1:}{2:} no such process{:_}")
+ errPartialWrite = verror.Register(pkgPath+".errPartialWrite", verror.NoRetry, "{1:}{2:} partial write{:_}")
)
// A ParentHandle is the Parent process' means of managing a single child.
@@ -234,7 +243,7 @@
// WaitForReady will wait for the child process to become ready.
func (p *ParentHandle) WaitForReady(timeout time.Duration) error {
if !p.protocol {
- return ErrNotUsingProtocol
+ return verror.New(ErrNotUsingProtocol, nil)
}
// An invariant of WaitForReady is that both statusWrite and statusRead
// get closed before WaitForStatus returns (statusRead gets closed by
@@ -260,11 +269,11 @@
return nil
}
if strings.HasPrefix(m, failedStatus) {
- return fmt.Errorf("%s", strings.TrimPrefix(m, failedStatus))
+ return verror.New(errFailedStatus, nil, strings.TrimPrefix(m, failedStatus))
}
- return fmt.Errorf("unrecognised status from subprocess: %q", m)
+ return verror.New(errUnrecognizedStatus, nil, m)
default:
- return fmt.Errorf("unexpected type %T", m)
+ return verror.New(errUnexpectedType, nil, fmt.Sprintf("%T", m))
}
case <-p.tk.After(timeout):
vlog.Errorf("Timed out waiting for child status")
@@ -281,7 +290,7 @@
// c. Waiting on c ensures that r.Close() in waitForStatus
// already executed.
<-c
- return ErrTimeout
+ return verror.New(ErrTimeout, nil)
}
panic("unreachable")
}
@@ -315,7 +324,7 @@
if timeout > 0 {
select {
case <-p.tk.After(timeout):
- return ErrTimeout
+ return verror.New(ErrTimeout, nil)
case err := <-c:
return err
}
@@ -350,7 +359,7 @@
// Kill kills the child process.
func (p *ParentHandle) Kill() error {
if p.c.Process == nil {
- return errors.New("no such process")
+ return verror.New(errNoSuchProcess, nil)
}
return p.c.Process.Kill()
}
@@ -358,7 +367,7 @@
// Signal sends the given signal to the child process.
func (p *ParentHandle) Signal(sig syscall.Signal) error {
if p.c.Process == nil {
- return errors.New("no such process")
+ return verror.New(errNoSuchProcess, nil)
}
return syscall.Kill(p.c.Process.Pid, sig)
}
@@ -380,7 +389,7 @@
if err != nil {
return err
} else {
- return errors.New("partial write")
+ return verror.New(errPartialWrite, nil)
}
}
return nil
diff --git a/lib/exec/util.go b/lib/exec/util.go
index 813730c..323097b 100644
--- a/lib/exec/util.go
+++ b/lib/exec/util.go
@@ -5,8 +5,13 @@
package exec
import (
- "errors"
"strings"
+
+ "v.io/v23/verror"
+)
+
+var (
+ errNotFound = verror.Register(pkgPath+".errNotFound", verror.NoRetry, "{1:}{2:} not found{:_}")
)
// Getenv retrieves the value of the given variable from the given
@@ -17,7 +22,7 @@
return strings.TrimPrefix(v, name+"="), nil
}
}
- return "", errors.New("not found")
+ return "", verror.New(errNotFound, nil)
}
// Setenv updates / adds the value assignment for the given variable
diff --git a/profiles/internal/naming/endpoint.go b/profiles/internal/naming/endpoint.go
index 17f3e3e..88b333c 100644
--- a/profiles/internal/naming/endpoint.go
+++ b/profiles/internal/naming/endpoint.go
@@ -40,6 +40,7 @@
MaxRPCVersion version.RPCVersion
Blessings []string
IsMountTable bool
+ IsLeaf bool
}
// NewEndpoint creates a new endpoint from a string as per naming.NewEndpoint
@@ -136,24 +137,23 @@
return strconv.FormatUint(uint64(v), 10)
}
-func parseMountTableFlag(input string) (bool, error) {
+func parseMountTableFlag(input string) (bool, bool, error) {
switch len(input) {
case 0:
- return true, nil
+ return true, false, nil
case 1:
switch f := input[0]; f {
+ case 'l':
+ return false, true, nil
case 'm':
- return true, nil
- case 's', 'l':
- // TODO(rthellend): 'l' will be used to indicate leaf
- // servers in a future version. For now, treat it the
- // the same as 's'.
- return false, nil
+ return true, false, nil
+ case 's':
+ return false, false, nil
default:
- return false, fmt.Errorf("%c is not one of 'm' or 's'", f)
+ return false, false, fmt.Errorf("%c is not one of 'l', 'm', or 's'", f)
}
}
- return false, fmt.Errorf("flag is either missing or too long")
+ return false, false, fmt.Errorf("flag is either missing or too long")
}
func (ep *Endpoint) parseV2(parts []string) error {
@@ -181,7 +181,7 @@
if err = ep.parseV2(parts[:6]); err != nil {
return err
}
- if ep.IsMountTable, err = parseMountTableFlag(parts[6]); err != nil {
+ if ep.IsMountTable, ep.IsLeaf, err = parseMountTableFlag(parts[6]); err != nil {
return fmt.Errorf("invalid mount table flag: %v", err)
}
return nil
@@ -233,10 +233,13 @@
mt)
case 4:
mt := "s"
- blessings := strings.Join(ep.Blessings, blessingsSeparator)
- if ep.IsMountTable {
+ switch {
+ case ep.IsLeaf:
+ mt = "l"
+ case ep.IsMountTable:
mt = "m"
}
+ blessings := strings.Join(ep.Blessings, blessingsSeparator)
return fmt.Sprintf("@4@%s@%s@%s@%s@%s@%s@%s@@",
ep.Protocol, ep.Address, ep.RID,
printRPCVersion(ep.MinRPCVersion), printRPCVersion(ep.MaxRPCVersion),
@@ -269,6 +272,11 @@
return ep.IsMountTable
}
+func (ep *Endpoint) ServesLeaf() bool {
+ //nologcall
+ return ep.IsLeaf
+}
+
func (ep *Endpoint) BlessingNames() []string {
//nologcall
return ep.Blessings
diff --git a/profiles/internal/naming/namespace/all_test.go b/profiles/internal/naming/namespace/all_test.go
index 7ff78dc..c8981c9 100644
--- a/profiles/internal/naming/namespace/all_test.go
+++ b/profiles/internal/naming/namespace/all_test.go
@@ -574,8 +574,12 @@
boom(t, "Failed to detect cycle")
}
- // Perform the glob with a response length limit.
- doGlob(t, c, ns, "c1/...", 1000)
+ // Perform the glob with a response length limit and dup suppression. The dup supression
+ // should win.
+ r := doGlob(t, c, ns, "c1/...", 1000)
+ if len(r) != 6 {
+ t.Fatal("expected 6 replies, got %v", r)
+ }
}
// TestGoroutineLeaks tests for leaking goroutines - we have many:-(
diff --git a/profiles/internal/naming/namespace/glob.go b/profiles/internal/naming/namespace/glob.go
index 0af01c3..16cdc11 100644
--- a/profiles/internal/naming/namespace/glob.go
+++ b/profiles/internal/naming/namespace/glob.go
@@ -7,6 +7,7 @@
import (
"io"
"strings"
+ "sync"
"v.io/x/ref/lib/glob"
@@ -18,6 +19,25 @@
"v.io/x/lib/vlog"
)
+type tracks struct {
+ m sync.Mutex
+ places map[string]struct{}
+}
+
+func (tr *tracks) beenThereDoneThat(servers []string, pstr string) bool {
+ tr.m.Lock()
+ defer tr.m.Unlock()
+ found := false
+ for _, s := range servers {
+ x := s + "!" + pstr
+ if _, ok := tr.places[x]; ok {
+ found = true
+ }
+ tr.places[x] = struct{}{}
+ }
+ return found
+}
+
// task is a sub-glob that has to be performed against a mount table. Tasks are
// done in parrallel to speed up the glob.
type task struct {
@@ -30,7 +50,7 @@
// globAtServer performs a Glob on the servers at a mount point. It cycles through the set of
// servers until it finds one that replies.
-func (ns *namespace) globAtServer(ctx *context.T, t *task, replies chan *task) {
+func (ns *namespace) globAtServer(ctx *context.T, t *task, replies chan *task, tr *tracks) {
defer func() {
if t.error == nil {
replies <- nil
@@ -53,6 +73,13 @@
t.error = nil
return
}
+
+ // If we've been there before with the same request, give up.
+ if tr.beenThereDoneThat(servers, pstr) {
+ t.error = nil
+ return
+ }
+
call, err := ns.parallelStartCall(ctx, client, servers, rpc.GlobMethod, []interface{}{pstr})
if err != nil {
t.error = err
@@ -119,7 +146,7 @@
}
// globLoop fires off a go routine for each server and read backs replies.
-func (ns *namespace) globLoop(ctx *context.T, e *naming.MountEntry, prefix string, pattern *glob.Glob, reply chan interface{}) {
+func (ns *namespace) globLoop(ctx *context.T, e *naming.MountEntry, prefix string, pattern *glob.Glob, reply chan interface{}, tr *tracks) {
defer close(reply)
// Provide enough buffers to avoid too much switching between the readers and the writers.
@@ -201,7 +228,7 @@
// Perform a glob at the next server.
inFlight++
t.pattern = suffix
- go ns.globAtServer(ctx, t, replies)
+ go ns.globAtServer(ctx, t, replies, tr)
}
}
}
@@ -221,6 +248,8 @@
return nil, err
}
+ tr := &tracks{places: make(map[string]struct{})}
+
// If pattern was already rooted, make sure we tack that root
// onto all returned names. Otherwise, just return the relative
// name.
@@ -230,6 +259,6 @@
}
e.Name = ""
reply := make(chan interface{}, 100)
- go ns.globLoop(ctx, e, prefix, g, reply)
+ go ns.globLoop(ctx, e, prefix, g, reply, tr)
return reply, nil
}
diff --git a/profiles/internal/rpc/server.go b/profiles/internal/rpc/server.go
index a4b286d..8efaf7e 100644
--- a/profiles/internal/rpc/server.go
+++ b/profiles/internal/rpc/server.go
@@ -464,7 +464,6 @@
}
for _, iep := range ls.ieps {
- s.publisher.AddServer(iep.String())
eps = append(eps, iep)
}
}
@@ -490,6 +489,7 @@
s.proxies[proxy] = proxyState{iep, nil}
s.Unlock()
iep.IsMountTable = s.servesMountTable
+ iep.IsLeaf = s.isLeaf
s.publisher.AddServer(iep.String())
return iep, ln, nil
}
@@ -730,6 +730,8 @@
if ls != nil && ls.roaming {
niep := ls.protoIEP
niep.Address = net.JoinHostPort(host, ls.port)
+ niep.IsMountTable = s.servesMountTable
+ niep.IsLeaf = s.isLeaf
ls.ieps = append(ls.ieps, &niep)
vlog.VI(2).Infof("rpc: dhcp adding: %s", niep)
s.publisher.AddServer(niep.String())
@@ -761,10 +763,21 @@
if err != nil {
return verror.New(verror.ErrBadArg, s.ctx, fmt.Sprintf("bad object: %v", err))
}
- s.isLeaf = true
+ s.setLeaf(true)
return s.ServeDispatcher(name, &leafDispatcher{invoker, authorizer})
}
+func (s *server) setLeaf(value bool) {
+ s.Lock()
+ defer s.Unlock()
+ s.isLeaf = value
+ for ls, _ := range s.listenState {
+ for i := range ls.ieps {
+ ls.ieps[i].IsLeaf = s.isLeaf
+ }
+ }
+}
+
func (s *server) ServeDispatcher(name string, disp rpc.Dispatcher) error {
defer vlog.LogCall()()
if disp == nil {
@@ -778,6 +791,11 @@
vtrace.GetSpan(s.ctx).Annotate("Serving under name: " + name)
s.disp = disp
if len(name) > 0 {
+ for ls, _ := range s.listenState {
+ for _, iep := range ls.ieps {
+ s.publisher.AddServer(iep.String())
+ }
+ }
s.publisher.AddName(name, s.servesMountTable, s.isLeaf)
}
return nil
diff --git a/profiles/internal/rpc/server_test.go b/profiles/internal/rpc/server_test.go
index ce935c9..3074ed5 100644
--- a/profiles/internal/rpc/server_test.go
+++ b/profiles/internal/rpc/server_test.go
@@ -323,6 +323,7 @@
if err = server.Serve("foo", &testServer{}, nil); err != nil {
t.Fatal(err)
}
+ setLeafEndpoints(eps)
status := server.Status()
if got, want := len(status.Mounts), 2; got != want {
t.Fatalf("got %d, want %d", got, want)
@@ -470,6 +471,7 @@
if err = server.Serve("foo", &testServer{}, nil); err != nil {
t.Fatal(err)
}
+ setLeafEndpoints(eps)
if err = server.AddName("bar"); err != nil {
t.Fatal(err)
}
@@ -560,7 +562,7 @@
status = server.Status()
if got, want := len(status.Mounts), 0; got != want {
- t.Fatalf("got %d, want %d", got, want)
+ t.Fatalf("got %d, want %d: %v", got, want, status.Mounts)
}
roaming <- rpc.NewAddAddrsSetting([]rpc.Address{n1})
@@ -607,6 +609,7 @@
if err = server.Serve("foo", &testServer{}, nil); err != nil {
t.Fatal(err)
}
+ setLeafEndpoints(eps)
// Set a watcher that we never read from - the intent is to make sure
// that the listener still listens to changes even though there is no
@@ -642,3 +645,9 @@
}
}
+
+func setLeafEndpoints(eps []naming.Endpoint) {
+ for i := range eps {
+ eps[i].(*inaming.Endpoint).IsLeaf = true
+ }
+}
diff --git a/profiles/internal/rpc/stream/vc/vc_test.go b/profiles/internal/rpc/stream/vc/vc_test.go
index 958b6ad..6bc422d 100644
--- a/profiles/internal/rpc/stream/vc/vc_test.go
+++ b/profiles/internal/rpc/stream/vc/vc_test.go
@@ -617,5 +617,6 @@
func (e endpoint) RoutingID() naming.RoutingID { return naming.RoutingID(e) }
func (e endpoint) Addr() net.Addr { return nil }
func (e endpoint) ServesMountTable() bool { return false }
+func (e endpoint) ServesLeaf() bool { return false }
func (e endpoint) BlessingNames() []string { return nil }
func (e endpoint) RPCVersionRange() version.RPCVersionRange { return version.RPCVersionRange{} }
diff --git a/profiles/internal/rt/mgmt.go b/profiles/internal/rt/mgmt.go
index d27d689..6033e93 100644
--- a/profiles/internal/rt/mgmt.go
+++ b/profiles/internal/rt/mgmt.go
@@ -26,12 +26,14 @@
func (rt *Runtime) initMgmt(ctx *context.T) error {
handle, err := exec.GetChildHandle()
- if err == exec.ErrNoVersion {
+ if err == nil {
+ // No error; fall through.
+ } else if verror.ErrorID(err) == exec.ErrNoVersion.ID {
// Do not initialize the mgmt runtime if the process has not
// been started through the vanadium exec library by a device
// manager.
return nil
- } else if err != nil {
+ } else {
return err
}
parentName, err := handle.Config.Get(mgmt.ParentNameConfigKey)
diff --git a/profiles/internal/rt/security.go b/profiles/internal/rt/security.go
index ccdc157..9bdbb59 100644
--- a/profiles/internal/rt/security.go
+++ b/profiles/internal/rt/security.go
@@ -15,6 +15,7 @@
"v.io/v23/mgmt"
"v.io/v23/rpc"
"v.io/v23/security"
+ "v.io/v23/verror"
"v.io/x/ref/lib/exec"
vsecurity "v.io/x/ref/security"
@@ -71,7 +72,7 @@
// agent.
func agentFD() (int, error) {
handle, err := exec.GetChildHandle()
- if err != nil && err != exec.ErrNoVersion {
+ if err != nil && verror.ErrorID(err) != exec.ErrNoVersion.ID {
return -1, err
}
var fd string
diff --git a/profiles/internal/testing/mocks/mocknet/mocknet.go b/profiles/internal/testing/mocks/mocknet/mocknet.go
new file mode 100644
index 0000000..0252003
--- /dev/null
+++ b/profiles/internal/testing/mocks/mocknet/mocknet.go
@@ -0,0 +1,262 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package mocknet implements a mock net.Conn that can simulate a variety of
+// network errors and/or be used for tracing.
+package mocknet
+
+import (
+ "net"
+ "sync"
+ "time"
+)
+
+// TODO(cnicolaou): consider extending Dialer/Listener API to include a cipher
+// to allow access to encrypted data.
+
+type Mode int
+
+const (
+ Trace Mode = iota // Log the sizes of each read/write call
+ Close // Close the connection after a specified #bytes are read/written
+ Drop // Drop byes as per a policy specified in opts
+)
+
+type Opts struct {
+ // The underlying network protocol to use, e.g. "tcp", defaults to tcp.
+ UnderlyingProtocol string
+
+ // The mode to operate under.
+ Mode Mode
+
+ // Buffers to store the transmit and receive message sizes when
+ // in Trace mode.
+ Tx, Rx chan int
+
+ // The number of rx and tx bytes respectively to be seen before the
+ // connection is closed when in Close mode.
+ RxCloseAt, TxCloseAt int
+
+ // TXDropAfter is called to obtain the number of tx bytes to be sent
+ // before dropping the rest of the data passed to that write call. The
+ // number of bytes returned by TxDroptAfter will always be written,
+ // but the number of bytes dropped is unspecified since it depends
+ // on the size of the buffer passed to that write call. TxDropAfter
+ // will be called again after each drop and the current count of
+ // byte sent reset to zero.
+ TxDropAfter func() (pos int)
+}
+
+// DialerWithOpts is intended for use with rpc.RegisterProtocol via
+// a closure:
+//
+// dialer := func(network, address string, timeout time.Duration) (net.Conn, error) {
+// return mocknet.DialerWithOpts(mocknet.Opts{UnderlyingProtocol:"tcp"}, network, address, timeout)
+// }
+// rpc.RegisterProtocol("brkDial", dialer, net.Listen)
+//
+func DialerWithOpts(opts Opts, network, address string, timeout time.Duration) (net.Conn, error) {
+ protocol := opts.UnderlyingProtocol
+ if len(protocol) == 0 {
+ protocol = "tcp"
+ }
+ c, err := net.DialTimeout(protocol, address, timeout)
+ if err != nil {
+ return nil, err
+ }
+ return newMockConn(opts, c), nil
+}
+
+// ListenerWithOpts is intended for use with rpc.RegisterProtocol via
+// a closure as per DialerWithOpts.
+func ListenerWithOpts(opts Opts, network, laddr string) (net.Listener, error) {
+ protocol := opts.UnderlyingProtocol
+ if len(protocol) == 0 {
+ protocol = "tcp"
+ }
+ ln, err := net.Listen(protocol, laddr)
+ if err != nil {
+ return nil, err
+ }
+ return &listener{opts, ln}, nil
+}
+
+func newMockConn(opts Opts, c net.Conn) net.Conn {
+ switch opts.Mode {
+ case Trace:
+ return &traceConn{
+ conn: c,
+ rx: opts.Rx,
+ tx: opts.Tx}
+ case Close:
+ return &closeConn{
+ conn: c,
+ rxCloseAt: opts.RxCloseAt,
+ txCloseAt: opts.TxCloseAt,
+ }
+ case Drop:
+ return &dropConn{
+ opts: opts,
+ conn: c,
+ txDropAfter: opts.TxDropAfter(),
+ }
+ }
+ return nil
+}
+
+type dropConn struct {
+ sync.Mutex
+ opts Opts
+ conn net.Conn
+ tx int
+ txDropAfter int
+}
+
+func (c *dropConn) Read(b []byte) (n int, err error) {
+ return c.conn.Read(b)
+}
+
+func (c *dropConn) Write(b []byte) (n int, err error) {
+ c.Lock()
+ defer c.Unlock()
+ dropped := false
+ if c.tx+len(b) >= c.txDropAfter {
+ b = b[0 : c.txDropAfter-c.tx]
+ c.txDropAfter = c.opts.TxDropAfter()
+ dropped = true
+ }
+ n, err = c.conn.Write(b)
+ if dropped {
+ c.tx = 0
+ } else {
+ c.tx += n
+ }
+ return
+}
+
+func (c *dropConn) Close() error { return c.conn.Close() }
+func (c *dropConn) LocalAddr() net.Addr { return c.conn.LocalAddr() }
+func (c *dropConn) RemoteAddr() net.Addr {
+ return c.conn.RemoteAddr()
+}
+func (c *dropConn) SetDeadline(t time.Time) error {
+ return c.conn.SetDeadline(t)
+}
+func (c *dropConn) SetReadDeadline(t time.Time) error {
+ return c.conn.SetReadDeadline(t)
+}
+func (c *dropConn) SetWriteDeadline(t time.Time) error {
+ return c.conn.SetWriteDeadline(t)
+}
+
+type closeConn struct {
+ sync.Mutex
+ conn net.Conn
+ rx, tx int
+ rxCloseAt, txCloseAt int
+ closed bool
+}
+
+func (c *closeConn) Read(b []byte) (n int, err error) {
+ c.Lock()
+ defer c.Unlock()
+ n = len(b)
+ if c.rx+n >= c.rxCloseAt {
+ n = c.rxCloseAt - c.rx
+ }
+ b = b[:n]
+ n, err = c.conn.Read(b[:n])
+ c.rx += n
+ if c.rx == c.rxCloseAt {
+ c.conn.Close()
+ }
+ return
+}
+
+func (c *closeConn) Write(b []byte) (n int, err error) {
+ c.Lock()
+ defer c.Unlock()
+ n = len(b)
+ if c.tx+n >= c.txCloseAt {
+ n = c.txCloseAt - c.tx
+ }
+ n, err = c.conn.Write(b[:n])
+ c.tx += n
+ if c.tx == c.txCloseAt {
+ c.conn.Close()
+ }
+ return
+}
+
+func (c *closeConn) Close() error { return c.conn.Close() }
+func (c *closeConn) LocalAddr() net.Addr { return c.conn.LocalAddr() }
+func (c *closeConn) RemoteAddr() net.Addr {
+ return c.conn.RemoteAddr()
+}
+func (c *closeConn) SetDeadline(t time.Time) error {
+ return c.conn.SetDeadline(t)
+}
+func (c *closeConn) SetReadDeadline(t time.Time) error {
+ return c.conn.SetReadDeadline(t)
+}
+func (c *closeConn) SetWriteDeadline(t time.Time) error {
+ return c.conn.SetWriteDeadline(t)
+}
+
+type traceConn struct {
+ conn net.Conn
+ tx, rx chan int
+}
+
+func (c *traceConn) Read(b []byte) (n int, err error) {
+ n, err = c.conn.Read(b)
+ c.rx <- n
+ return n, err
+}
+
+func (c *traceConn) Write(b []byte) (n int, err error) {
+ n, err = c.conn.Write(b)
+ c.tx <- n
+ return
+}
+
+func (c *traceConn) Close() error {
+ c.rx <- -1
+ c.tx <- -1
+ return c.conn.Close()
+}
+
+func (c *traceConn) LocalAddr() net.Addr { return c.conn.LocalAddr() }
+func (c *traceConn) RemoteAddr() net.Addr { return c.conn.RemoteAddr() }
+func (c *traceConn) SetDeadline(t time.Time) error {
+ return c.conn.SetDeadline(t)
+}
+func (c *traceConn) SetReadDeadline(t time.Time) error {
+ return c.conn.SetReadDeadline(t)
+}
+func (c *traceConn) SetWriteDeadline(t time.Time) error {
+ return c.conn.SetWriteDeadline(t)
+}
+
+// listener is a wrapper around net.Listener.
+type listener struct {
+ opts Opts
+ netLn net.Listener
+}
+
+func (ln *listener) Accept() (net.Conn, error) {
+ c, err := ln.netLn.Accept()
+ if err != nil {
+ return nil, err
+ }
+ return newMockConn(ln.opts, c), nil
+}
+
+func (ln *listener) Close() error {
+ return ln.netLn.Close()
+}
+
+func (ln *listener) Addr() net.Addr {
+ return ln.netLn.Addr()
+}
diff --git a/profiles/internal/testing/mocks/mocknet/mocknet_test.go b/profiles/internal/testing/mocks/mocknet/mocknet_test.go
new file mode 100644
index 0000000..1e62ac7
--- /dev/null
+++ b/profiles/internal/testing/mocks/mocknet/mocknet_test.go
@@ -0,0 +1,214 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package mocknet_test
+
+import (
+ "errors"
+ "io"
+ "net"
+ "reflect"
+ "sync"
+ "testing"
+ "time"
+
+ "v.io/x/ref/profiles/internal/testing/mocks/mocknet"
+)
+
+//go:generate v23 test generate
+
+func newListener(t *testing.T, opts mocknet.Opts) net.Listener {
+ ln, err := mocknet.ListenerWithOpts(opts, "test", "127.0.0.1:0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ return ln
+}
+
+func TestTrace(t *testing.T) {
+ opts := mocknet.Opts{
+ Mode: mocknet.Trace,
+ Tx: make(chan int, 100),
+ Rx: make(chan int, 100),
+ }
+ ln := newListener(t, opts)
+ defer ln.Close()
+
+ var rxconn net.Conn
+ var rxerr error
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ rxconn, rxerr = ln.Accept()
+ wg.Done()
+ }()
+
+ txconn, err := mocknet.DialerWithOpts(opts, "test", ln.Addr().String(), time.Minute)
+ if err != nil {
+ t.Fatal(err)
+ }
+ wg.Wait()
+
+ rw := func(s string) {
+ b := make([]byte, len(s))
+ txconn.Write([]byte(s))
+ rxconn.Read(b[:])
+ if got, want := string(b), s; got != want {
+ t.Fatalf("got %v, want %v", got, want)
+ }
+ }
+
+ sizes := []int{}
+ for _, s := range []string{"hello", " ", "world"} {
+ rw(s)
+ sizes = append(sizes, len(s))
+ }
+ rxconn.Close()
+ close(opts.Tx)
+ close(opts.Rx)
+ sizes = append(sizes, -1)
+
+ drain := func(ch chan int) []int {
+ r := []int{}
+ for v := range ch {
+ r = append(r, v)
+ }
+ return r
+ }
+
+ if got, want := drain(opts.Rx), sizes; !reflect.DeepEqual(got, want) {
+ t.Fatalf("got %v, want %v", got, want)
+ }
+ if got, want := drain(opts.Tx), sizes; !reflect.DeepEqual(got, want) {
+ t.Fatalf("got %v, want %v", got, want)
+ }
+}
+
+func TestClose(t *testing.T) {
+ cases := []struct {
+ txClose, rxClose int
+ tx []string
+ rx []string
+ err error
+ }{
+ {6, 10, []string{"hello", "world"}, []string{"hello", "w"}, io.EOF},
+ {5, 10, []string{"hello", "world"}, []string{"hello", ""}, io.EOF},
+ {8, 6, []string{"hello", "world"}, []string{"hello", "w"}, io.EOF},
+ {8, 5, []string{"hello", "world"}, []string{"hello", ""}, errors.New("use of closed network connection")},
+ }
+
+ for ci, c := range cases {
+ opts := mocknet.Opts{
+ Mode: mocknet.Close,
+ TxCloseAt: c.txClose,
+ RxCloseAt: c.rxClose,
+ }
+
+ ln := newListener(t, opts)
+ defer ln.Close()
+
+ var rxconn net.Conn
+ var rxerr error
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ rxconn, rxerr = ln.Accept()
+ wg.Done()
+ }()
+
+ txconn, err := mocknet.DialerWithOpts(opts, "test", ln.Addr().String(), time.Minute)
+ if err != nil {
+ t.Fatal(err)
+ }
+ wg.Wait()
+
+ rw := func(s string) (int, int, string, error) {
+ b := make([]byte, len(s))
+ tx, _ := txconn.Write([]byte(s))
+ rx, err := rxconn.Read(b[:])
+ return tx, rx, string(b[0:rx]), err
+ }
+
+ txBytes := 0
+ rxBytes := 0
+ for i, m := range c.tx {
+ tx, rx, rxed, err := rw(m)
+ if got, want := rxed, c.rx[i]; got != want {
+ t.Fatalf("%d: got %q, want %q", ci, got, want)
+ }
+ txBytes += tx
+ rxBytes += rx
+ if err != nil {
+ if got, want := err.Error(), c.err.Error(); got != want {
+ t.Fatalf("%d: got %v, want %v", ci, got, want)
+ }
+ }
+ }
+ if got, want := txBytes, c.txClose; got != want {
+ t.Fatalf("%d: got %v, want %v", ci, got, want)
+ }
+ rxWant := c.rxClose
+ if rxWant > c.txClose {
+ rxWant = c.txClose
+ }
+ if got, want := rxBytes, rxWant; got != want {
+ t.Fatalf("%d: got %v, want %v", ci, got, want)
+
+ }
+ }
+}
+
+func TestDrop(t *testing.T) {
+ cases := []struct {
+ txDropAfter int
+ tx []string
+ rx []string
+ }{
+ {6, []string{"hello", "world"}, []string{"hello", "w"}},
+ {2, []string{"hello", "world"}, []string{"he", "wo"}},
+ {0, []string{"hello", "world"}, []string{"", ""}},
+ }
+
+ for ci, c := range cases {
+ opts := mocknet.Opts{
+ Mode: mocknet.Drop,
+ TxDropAfter: func() int { return c.txDropAfter },
+ }
+
+ ln := newListener(t, opts)
+ defer ln.Close()
+
+ var rxconn net.Conn
+ var rxerr error
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ rxconn, rxerr = ln.Accept()
+ wg.Done()
+ }()
+
+ txconn, err := mocknet.DialerWithOpts(opts, "test", ln.Addr().String(), time.Minute)
+ if err != nil {
+ t.Fatal(err)
+ }
+ wg.Wait()
+
+ rw := func(s string, l int) (int, int, string, error) {
+ b := make([]byte, l)
+ tx, _ := txconn.Write([]byte(s))
+ rx, err := rxconn.Read(b[:])
+ return tx, rx, string(b[0:rx]), err
+ }
+
+ for i, m := range c.tx {
+ tx, rx, rxed, _ := rw(m, len(c.rx[i]))
+ if got, want := rxed, c.rx[i]; got != want {
+ t.Fatalf("%d: got %q, want %q", ci, got, want)
+ }
+ if tx != rx {
+ t.Fatalf("%d: tx %d, rx %d", ci, tx, rx)
+ }
+ }
+ }
+}
diff --git a/profiles/internal/util.go b/profiles/internal/util.go
index a6c45c7..600ff0a 100644
--- a/profiles/internal/util.go
+++ b/profiles/internal/util.go
@@ -10,6 +10,7 @@
"strings"
"v.io/v23/rpc"
+ "v.io/v23/verror"
"v.io/x/lib/vlog"
"v.io/x/lib/netstate"
@@ -23,14 +24,13 @@
// profile can use or modify the flags as it pleases.
func ParseFlags(f *flags.Flags) error {
handle, err := exec.GetChildHandle()
- switch err {
- case exec.ErrNoVersion:
- // The process has not been started through the vanadium exec
- // library. No further action is needed.
- case nil:
+ if err == nil {
// The process has been started through the vanadium exec
// library.
- default:
+ } else if verror.ErrorID(err) == exec.ErrNoVersion.ID {
+ // The process has not been started through the vanadium exec
+ // library. No further action is needed.
+ } else {
return err
}
diff --git a/services/mgmt/device/impl/callback.go b/services/mgmt/device/impl/callback.go
index 6ffef42..4241e60 100644
--- a/services/mgmt/device/impl/callback.go
+++ b/services/mgmt/device/impl/callback.go
@@ -7,6 +7,7 @@
import (
"v.io/v23/context"
"v.io/v23/mgmt"
+ "v.io/v23/verror"
"v.io/x/lib/vlog"
"v.io/x/ref/lib/exec"
@@ -17,8 +18,7 @@
// is expected to be this device manager's object name).
func InvokeCallback(ctx *context.T, name string) {
handle, err := exec.GetChildHandle()
- switch err {
- case nil:
+ if err == nil {
// Device manager was started by self-update, notify the parent.
callbackName, err := handle.Config.Get(mgmt.ParentNameConfigKey)
if err != nil {
@@ -31,8 +31,7 @@
if err := client.Set(ctx, mgmt.ChildNameConfigKey, name); err != nil {
vlog.Fatalf("Set(%v, %v) failed: %v", mgmt.ChildNameConfigKey, name, err)
}
- case exec.ErrNoVersion:
- default:
+ } else if verror.ErrorID(err) != exec.ErrNoVersion.ID {
vlog.Fatalf("GetChildHandle() failed: %v", err)
}
}
diff --git a/services/mgmt/profile/impl/v23_internal_test.go b/services/mgmt/profile/impl/v23_internal_test.go
deleted file mode 100644
index f3c1179..0000000
--- a/services/mgmt/profile/impl/v23_internal_test.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// This file was auto-generated via go generate.
-// DO NOT UPDATE MANUALLY
-package impl
-
-import "testing"
-import "os"
-
-import "v.io/x/ref/test"
-
-func TestMain(m *testing.M) {
- test.Init()
- os.Exit(m.Run())
-}
diff --git a/services/mgmt/profile/impl/dispatcher.go b/services/mgmt/profile/profiled/dispatcher.go
similarity index 98%
rename from services/mgmt/profile/impl/dispatcher.go
rename to services/mgmt/profile/profiled/dispatcher.go
index 64dfa3e..4b43fc7 100644
--- a/services/mgmt/profile/impl/dispatcher.go
+++ b/services/mgmt/profile/profiled/dispatcher.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package impl
+package main
import (
"path/filepath"
diff --git a/services/mgmt/profile/impl/impl_test.go b/services/mgmt/profile/profiled/impl_test.go
similarity index 98%
rename from services/mgmt/profile/impl/impl_test.go
rename to services/mgmt/profile/profiled/impl_test.go
index 1f9583d..ba1c88b 100644
--- a/services/mgmt/profile/impl/impl_test.go
+++ b/services/mgmt/profile/profiled/impl_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package impl
+package main
import (
"io/ioutil"
@@ -14,7 +14,6 @@
"v.io/v23/naming"
"v.io/v23/services/mgmt/build"
- _ "v.io/x/ref/profiles"
"v.io/x/ref/services/mgmt/profile"
"v.io/x/ref/services/mgmt/repository"
"v.io/x/ref/test"
diff --git a/services/mgmt/profile/profiled/main.go b/services/mgmt/profile/profiled/main.go
index e8ef4d3..1c6b01f 100644
--- a/services/mgmt/profile/profiled/main.go
+++ b/services/mgmt/profile/profiled/main.go
@@ -13,7 +13,6 @@
"v.io/x/ref/lib/signals"
_ "v.io/x/ref/profiles/roaming"
vflag "v.io/x/ref/security/flag"
- "v.io/x/ref/services/mgmt/profile/impl"
)
var (
@@ -34,7 +33,7 @@
vlog.Fatalf("NewServer() failed: %v", err)
}
- dispatcher, err := impl.NewDispatcher(*store, vflag.NewAuthorizerOrDie())
+ dispatcher, err := NewDispatcher(*store, vflag.NewAuthorizerOrDie())
if err != nil {
vlog.Fatalf("NewDispatcher() failed: %v", err)
}
diff --git a/services/mgmt/profile/impl/service.go b/services/mgmt/profile/profiled/service.go
similarity index 99%
rename from services/mgmt/profile/impl/service.go
rename to services/mgmt/profile/profiled/service.go
index 2f33d08..49cef5f 100644
--- a/services/mgmt/profile/impl/service.go
+++ b/services/mgmt/profile/profiled/service.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package impl
+package main
import (
"errors"
diff --git a/services/security/discharger.vdl b/services/security/discharger.vdl
index d913b8d..040fe21 100644
--- a/services/security/discharger.vdl
+++ b/services/security/discharger.vdl
@@ -13,3 +13,8 @@
// this caveat.
Discharge(Caveat security.Caveat, Impetus security.DischargeImpetus) (Discharge security.WireDischarge | error)
}
+
+error (
+ // Indicates that the Caveat does not require a discharge
+ NotAThirdPartyCaveat(c security.Caveat) { "en": "discharges are not required for non-third-party caveats (id: {c.id})" }
+)
diff --git a/services/security/discharger.vdl.go b/services/security/discharger.vdl.go
index 825acae..5645357 100644
--- a/services/security/discharger.vdl.go
+++ b/services/security/discharger.vdl.go
@@ -11,12 +11,28 @@
// VDL system imports
"v.io/v23"
"v.io/v23/context"
+ "v.io/v23/i18n"
"v.io/v23/rpc"
+ "v.io/v23/verror"
// VDL user imports
"v.io/v23/security"
)
+var (
+ // Indicates that the Caveat does not require a discharge
+ ErrNotAThirdPartyCaveat = verror.Register("v.io/x/ref/services/security.NotAThirdPartyCaveat", verror.NoRetry, "{1:}{2:} discharges are not required for non-third-party caveats (id: {c.id})")
+)
+
+func init() {
+ i18n.Cat().SetWithBase(i18n.LangID("en"), i18n.MsgID(ErrNotAThirdPartyCaveat.ID), "{1:}{2:} discharges are not required for non-third-party caveats (id: {c.id})")
+}
+
+// NewErrNotAThirdPartyCaveat returns an error with the ErrNotAThirdPartyCaveat ID.
+func NewErrNotAThirdPartyCaveat(ctx *context.T, c security.Caveat) error {
+ return verror.New(ErrNotAThirdPartyCaveat, ctx, c)
+}
+
// DischargerClientMethods is the client interface
// containing Discharger methods.
//
diff --git a/services/security/discharger/discharger.go b/services/security/discharger/discharger.go
index e839380..30ad199 100644
--- a/services/security/discharger/discharger.go
+++ b/services/security/discharger/discharger.go
@@ -22,7 +22,7 @@
secCall := security.GetCall(ctx)
tp := caveat.ThirdPartyDetails()
if tp == nil {
- return security.Discharge{}, fmt.Errorf("Caveat %v does not represent a third party caveat", caveat)
+ return security.Discharge{}, services.NewErrNotAThirdPartyCaveat(call.Context(), caveat)
}
if err := tp.Dischargeable(ctx); err != nil {
return security.Discharge{}, fmt.Errorf("third-party caveat %v cannot be discharged for this context: %v", tp, err)
diff --git a/services/security/role.vdl b/services/security/role.vdl
new file mode 100644
index 0000000..26e970a
--- /dev/null
+++ b/services/security/role.vdl
@@ -0,0 +1,24 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package security
+
+import "v.io/v23/security"
+
+// Role is an interface to request blessings from a role account server. The
+// returned blessings are bound to the client's public key thereby authorizing
+// the client to acquire the role. The server may tie the returned blessings
+// with the client's presented blessing name in order to maintain audit
+// information in the blessing.
+//
+// In order to avoid granting role blessings to all delegates of a principal,
+// the role server requires that each authorized blessing presented by the
+// client have the string "_role" as suffix.
+type Role interface {
+ SeekBlessings() (security.WireBlessings | error)
+}
+
+// Role.SeekBlessings will return an error if the requestor does not present
+// blessings that end in this suffix.
+const RoleSuffix = "_role"
diff --git a/services/security/role.vdl.go b/services/security/role.vdl.go
new file mode 100644
index 0000000..b90bb48
--- /dev/null
+++ b/services/security/role.vdl.go
@@ -0,0 +1,157 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file was auto-generated by the vanadium vdl tool.
+// Source: role.vdl
+
+package security
+
+import (
+ // VDL system imports
+ "v.io/v23"
+ "v.io/v23/context"
+ "v.io/v23/rpc"
+
+ // VDL user imports
+ "v.io/v23/security"
+)
+
+// Role.SeekBlessings will return an error if the requestor does not present
+// blessings that end in this suffix.
+const RoleSuffix = "_role"
+
+// RoleClientMethods is the client interface
+// containing Role methods.
+//
+// Role is an interface to request blessings from a role account server. The
+// returned blessings are bound to the client's public key thereby authorizing
+// the client to acquire the role. The server may tie the returned blessings
+// with the client's presented blessing name in order to maintain audit
+// information in the blessing.
+//
+// In order to avoid granting role blessings to all delegates of a principal,
+// the role server requires that each authorized blessing presented by the
+// client have the string "_role" as suffix.
+type RoleClientMethods interface {
+ SeekBlessings(*context.T, ...rpc.CallOpt) (security.Blessings, error)
+}
+
+// RoleClientStub adds universal methods to RoleClientMethods.
+type RoleClientStub interface {
+ RoleClientMethods
+ rpc.UniversalServiceMethods
+}
+
+// RoleClient returns a client stub for Role.
+func RoleClient(name string, opts ...rpc.BindOpt) RoleClientStub {
+ var client rpc.Client
+ for _, opt := range opts {
+ if clientOpt, ok := opt.(rpc.Client); ok {
+ client = clientOpt
+ }
+ }
+ return implRoleClientStub{name, client}
+}
+
+type implRoleClientStub struct {
+ name string
+ client rpc.Client
+}
+
+func (c implRoleClientStub) c(ctx *context.T) rpc.Client {
+ if c.client != nil {
+ return c.client
+ }
+ return v23.GetClient(ctx)
+}
+
+func (c implRoleClientStub) SeekBlessings(ctx *context.T, opts ...rpc.CallOpt) (o0 security.Blessings, err error) {
+ var call rpc.ClientCall
+ if call, err = c.c(ctx).StartCall(ctx, c.name, "SeekBlessings", nil, opts...); err != nil {
+ return
+ }
+ err = call.Finish(&o0)
+ return
+}
+
+// RoleServerMethods is the interface a server writer
+// implements for Role.
+//
+// Role is an interface to request blessings from a role account server. The
+// returned blessings are bound to the client's public key thereby authorizing
+// the client to acquire the role. The server may tie the returned blessings
+// with the client's presented blessing name in order to maintain audit
+// information in the blessing.
+//
+// In order to avoid granting role blessings to all delegates of a principal,
+// the role server requires that each authorized blessing presented by the
+// client have the string "_role" as suffix.
+type RoleServerMethods interface {
+ SeekBlessings(rpc.ServerCall) (security.Blessings, error)
+}
+
+// RoleServerStubMethods is the server interface containing
+// Role methods, as expected by rpc.Server.
+// There is no difference between this interface and RoleServerMethods
+// since there are no streaming methods.
+type RoleServerStubMethods RoleServerMethods
+
+// RoleServerStub adds universal methods to RoleServerStubMethods.
+type RoleServerStub interface {
+ RoleServerStubMethods
+ // Describe the Role interfaces.
+ Describe__() []rpc.InterfaceDesc
+}
+
+// RoleServer returns a server stub for Role.
+// It converts an implementation of RoleServerMethods into
+// an object that may be used by rpc.Server.
+func RoleServer(impl RoleServerMethods) RoleServerStub {
+ stub := implRoleServerStub{
+ 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 := rpc.NewGlobState(stub); gs != nil {
+ stub.gs = gs
+ } else if gs := rpc.NewGlobState(impl); gs != nil {
+ stub.gs = gs
+ }
+ return stub
+}
+
+type implRoleServerStub struct {
+ impl RoleServerMethods
+ gs *rpc.GlobState
+}
+
+func (s implRoleServerStub) SeekBlessings(call rpc.ServerCall) (security.Blessings, error) {
+ return s.impl.SeekBlessings(call)
+}
+
+func (s implRoleServerStub) Globber() *rpc.GlobState {
+ return s.gs
+}
+
+func (s implRoleServerStub) Describe__() []rpc.InterfaceDesc {
+ return []rpc.InterfaceDesc{RoleDesc}
+}
+
+// RoleDesc describes the Role interface.
+var RoleDesc rpc.InterfaceDesc = descRole
+
+// descRole hides the desc to keep godoc clean.
+var descRole = rpc.InterfaceDesc{
+ Name: "Role",
+ PkgPath: "v.io/x/ref/services/security",
+ Doc: "// Role is an interface to request blessings from a role account server. The\n// returned blessings are bound to the client's public key thereby authorizing\n// the client to acquire the role. The server may tie the returned blessings\n// with the client's presented blessing name in order to maintain audit\n// information in the blessing.\n//\n// In order to avoid granting role blessings to all delegates of a principal,\n// the role server requires that each authorized blessing presented by the\n// client have the string \"_role\" as suffix.",
+ Methods: []rpc.MethodDesc{
+ {
+ Name: "SeekBlessings",
+ OutArgs: []rpc.ArgDesc{
+ {"", ``}, // security.Blessings
+ },
+ },
+ },
+}
diff --git a/services/security/roled/internal/caveats.vdl b/services/security/roled/internal/caveats.vdl
new file mode 100644
index 0000000..0ad318c
--- /dev/null
+++ b/services/security/roled/internal/caveats.vdl
@@ -0,0 +1,18 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package internal
+
+import (
+ "v.io/v23/security"
+ "v.io/v23/uniqueid"
+)
+
+const (
+ // LoggingCaveat is a caveat that will always validate but it logs the parameter on every attempt to validate it.
+ LoggingCaveat = security.CaveatDescriptor{
+ Id: uniqueid.Id{0xb0, 0x34, 0x1c, 0xed, 0xe2, 0xdf, 0x81, 0xbd, 0xed, 0x70, 0x97, 0xbb, 0x55, 0xad, 0x80, 0x0},
+ ParamType: typeobject([]string),
+ }
+)
diff --git a/services/security/roled/internal/caveats.vdl.go b/services/security/roled/internal/caveats.vdl.go
new file mode 100644
index 0000000..9a22ce7
--- /dev/null
+++ b/services/security/roled/internal/caveats.vdl.go
@@ -0,0 +1,40 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file was auto-generated by the vanadium vdl tool.
+// Source: caveats.vdl
+
+package internal
+
+import (
+ // VDL system imports
+ "v.io/v23/vdl"
+
+ // VDL user imports
+ "v.io/v23/security"
+ "v.io/v23/uniqueid"
+)
+
+// LoggingCaveat is a caveat that will always validate but it logs the parameter on every attempt to validate it.
+var LoggingCaveat = security.CaveatDescriptor{
+ Id: uniqueid.Id{
+ 176,
+ 52,
+ 28,
+ 237,
+ 226,
+ 223,
+ 129,
+ 189,
+ 237,
+ 112,
+ 151,
+ 187,
+ 85,
+ 173,
+ 128,
+ 0,
+ },
+ ParamType: vdl.TypeOf([]string(nil)),
+}
diff --git a/services/security/roled/internal/config.vdl b/services/security/roled/internal/config.vdl
new file mode 100644
index 0000000..cffe6b4
--- /dev/null
+++ b/services/security/roled/internal/config.vdl
@@ -0,0 +1,26 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package internal
+
+import "v.io/v23/security"
+
+// Config contains the attributes of the role, and the list of members who have
+// access to it.
+type Config struct {
+ // Blessings that match at least one of the patterns in this set are
+ // allowed to act on behalf of the role.
+ Members []security.BlessingPattern
+ // Indicates that the blessing name of the caller should be appended to
+ // the role blessing name.
+ Extend bool
+ // If Audit is true, each use of the role blessing will be reported to
+ // an auditing service and will be usable only if the report was
+ // successful.
+ Audit bool
+ // The amount of time for which the role blessing will be valid. It is a
+ // string representation of a time.Duration, e.g. "24h". An empty string
+ // indicates that the role blessing will not expire.
+ Expiry string
+}
diff --git a/services/security/roled/internal/config.vdl.go b/services/security/roled/internal/config.vdl.go
new file mode 100644
index 0000000..258fed3
--- /dev/null
+++ b/services/security/roled/internal/config.vdl.go
@@ -0,0 +1,44 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file was auto-generated by the vanadium vdl tool.
+// Source: config.vdl
+
+package internal
+
+import (
+ // VDL system imports
+ "v.io/v23/vdl"
+
+ // VDL user imports
+ "v.io/v23/security"
+)
+
+// Config contains the attributes of the role, and the list of members who have
+// access to it.
+type Config struct {
+ // Blessings that match at least one of the patterns in this set are
+ // allowed to act on behalf of the role.
+ Members []security.BlessingPattern
+ // Indicates that the blessing name of the caller should be appended to
+ // the role blessing name.
+ Extend bool
+ // If Audit is true, each use of the role blessing will be reported to
+ // an auditing service and will be usable only if the report was
+ // successful.
+ Audit bool
+ // The amount of time for which the role blessing will be valid. It is a
+ // string representation of a time.Duration, e.g. "24h". An empty string
+ // indicates that the role blessing will not expire.
+ Expiry string
+}
+
+func (Config) __VDLReflect(struct {
+ Name string "v.io/x/ref/services/security/roled/internal.Config"
+}) {
+}
+
+func init() {
+ vdl.Register((*Config)(nil))
+}
diff --git a/services/security/roled/internal/discharger.go b/services/security/roled/internal/discharger.go
new file mode 100644
index 0000000..8bcab33
--- /dev/null
+++ b/services/security/roled/internal/discharger.go
@@ -0,0 +1,60 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package internal
+
+import (
+ "time"
+
+ "v.io/v23"
+ "v.io/v23/context"
+ "v.io/v23/rpc"
+ "v.io/v23/security"
+ "v.io/v23/verror"
+
+ isecurity "v.io/x/ref/services/security"
+
+ "v.io/x/lib/vlog"
+)
+
+func init() {
+ security.RegisterCaveatValidator(LoggingCaveat, func(ctx *context.T, params []string) error {
+ vlog.Infof("Params: %#v", params)
+ return nil
+ })
+
+}
+
+type discharger struct{}
+
+func (discharger) Discharge(call rpc.ServerCall, caveat security.Caveat, impetus security.DischargeImpetus) (security.Discharge, error) {
+ details := caveat.ThirdPartyDetails()
+ if details == nil {
+ return security.Discharge{}, isecurity.NewErrNotAThirdPartyCaveat(call.Context(), caveat)
+ }
+ if err := details.Dischargeable(call.Context()); err != nil {
+ return security.Discharge{}, err
+ }
+ // TODO(rthellend,ashankar): Do proper logging when the API allows it.
+ vlog.Infof("Discharge() impetus: %#v", impetus)
+
+ expiry, err := security.ExpiryCaveat(time.Now().Add(5 * time.Minute))
+ if err != nil {
+ return security.Discharge{}, verror.Convert(verror.ErrInternal, call.Context(), err)
+ }
+ // Bind the discharge to precisely the purpose the requestor claims it will be used.
+ method, err := security.MethodCaveat(impetus.Method)
+ if err != nil {
+ return security.Discharge{}, verror.Convert(verror.ErrInternal, call.Context(), err)
+ }
+ peer, err := security.NewCaveat(security.PeerBlessingsCaveat, impetus.Server)
+ if err != nil {
+ return security.Discharge{}, verror.Convert(verror.ErrInternal, call.Context(), err)
+ }
+ discharge, err := v23.GetPrincipal(call.Context()).MintDischarge(caveat, expiry, method, peer)
+ if err != nil {
+ return security.Discharge{}, verror.Convert(verror.ErrInternal, call.Context(), err)
+ }
+ return discharge, nil
+}
diff --git a/services/security/roled/internal/dispatcher.go b/services/security/roled/internal/dispatcher.go
new file mode 100644
index 0000000..d01e8a2
--- /dev/null
+++ b/services/security/roled/internal/dispatcher.go
@@ -0,0 +1,102 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package internal
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "v.io/v23/context"
+ "v.io/v23/rpc"
+ "v.io/v23/security"
+ "v.io/v23/verror"
+
+ isecurity "v.io/x/ref/services/security"
+
+ "v.io/x/lib/vlog"
+)
+
+const requiredSuffix = security.ChainSeparator + isecurity.RoleSuffix
+
+// NewDispatcher returns a dispatcher object for a role service and its
+// associated discharger service.
+// The configRoot is the top level directory where the role configuration files
+// are stored.
+// The dischargerLocation is the object name or address of the discharger
+// service for the third-party caveats attached to the role blessings returned
+// by the role service.
+func NewDispatcher(configRoot, dischargerLocation string) rpc.Dispatcher {
+ return &dispatcher{configRoot, dischargerLocation}
+}
+
+type dispatcher struct {
+ configRoot string
+ dischargerLocation string
+}
+
+func (d *dispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
+ if len(suffix) == 0 {
+ return isecurity.DischargerServer(&discharger{}), &openAuthorizer{}, nil
+ }
+ fileName := filepath.Join(d.configRoot, filepath.FromSlash(suffix+".conf"))
+ if !strings.HasPrefix(fileName, d.configRoot) {
+ // Guard against ".." in the suffix that could be used to read
+ // files outside of the config root.
+ return nil, nil, verror.New(verror.ErrNoExistOrNoAccess, nil)
+ }
+ config, err := loadConfig(fileName)
+ if err != nil && !os.IsNotExist(err) {
+ // The config file exists, but we failed to read it for some
+ // reason. This is likely a server configuration error.
+ vlog.Errorf("loadConfig(%q): %v", fileName, err)
+ return nil, nil, verror.Convert(verror.ErrInternal, nil, err)
+ }
+ obj := &roleService{role: suffix, config: config, dischargerLocation: d.dischargerLocation}
+ return isecurity.RoleServer(obj), &authorizer{config}, nil
+}
+
+type openAuthorizer struct{}
+
+func (openAuthorizer) Authorize(*context.T) error {
+ return nil
+}
+
+type authorizer struct {
+ config *Config
+}
+
+func (a *authorizer) Authorize(ctx *context.T) error {
+ if a.config == nil {
+ return verror.New(verror.ErrNoExistOrNoAccess, ctx)
+ }
+ remoteBlessingNames, _ := security.RemoteBlessingNames(ctx)
+
+ for _, pattern := range a.config.Members {
+ if pattern.MatchedBy(remoteBlessingNames...) {
+ return nil
+ }
+ }
+ return verror.New(verror.ErrNoExistOrNoAccess, ctx)
+}
+
+func loadConfig(fileName string) (*Config, error) {
+ contents, err := ioutil.ReadFile(fileName)
+ if err != nil {
+ return nil, err
+ }
+ var c Config
+ if err := json.Unmarshal(contents, &c); err != nil {
+ return nil, err
+ }
+ for i, pattern := range c.Members {
+ if p := string(pattern); !strings.HasSuffix(p, requiredSuffix) {
+ c.Members[i] = security.BlessingPattern(p + requiredSuffix)
+ }
+ }
+ return &c, nil
+}
diff --git a/services/security/roled/internal/doc.go b/services/security/roled/internal/doc.go
new file mode 100644
index 0000000..118ee40
--- /dev/null
+++ b/services/security/roled/internal/doc.go
@@ -0,0 +1,6 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package internal implements the role service defined in v.io/x/ref/services/security
+package internal
diff --git a/services/security/roled/internal/role.go b/services/security/roled/internal/role.go
new file mode 100644
index 0000000..3941329
--- /dev/null
+++ b/services/security/roled/internal/role.go
@@ -0,0 +1,145 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package internal
+
+import (
+ "time"
+
+ "v.io/v23"
+ "v.io/v23/context"
+ "v.io/v23/rpc"
+ "v.io/v23/security"
+ "v.io/v23/verror"
+
+ "v.io/x/lib/vlog"
+)
+
+var (
+ errNoLocalBlessings = verror.Register("v.io/x/ref/services/security/roled/internal/noLocalBlessings", verror.NoRetry, "{1:}{2:} no local blessings")
+)
+
+type roleService struct {
+ role string
+ config *Config
+ dischargerLocation string
+}
+
+func (i *roleService) SeekBlessings(call rpc.ServerCall) (security.Blessings, error) {
+ ctx := call.Context()
+ remoteBlessingNames, _ := security.RemoteBlessingNames(ctx)
+ vlog.Infof("%q.SeekBlessings() called by %q", i.role, remoteBlessingNames)
+
+ members := i.filterNonMembers(remoteBlessingNames)
+ if len(members) == 0 {
+ // The Authorizer should already have caught that.
+ return security.Blessings{}, verror.New(verror.ErrNoAccess, ctx)
+ }
+
+ extensions := extensions(i.config, i.role, members)
+ caveats, err := caveats(ctx, i.config)
+ if err != nil {
+ return security.Blessings{}, err
+ }
+
+ return createBlessings(ctx, i.config, v23.GetPrincipal(ctx), extensions, caveats, i.dischargerLocation)
+}
+
+// filterNonMembers returns only the blessing names that are authorized members
+// for the role.
+func (i *roleService) filterNonMembers(blessingNames []string) []string {
+ var results []string
+ for _, name := range blessingNames {
+ // It is not enough to know if the pattern is matched by the
+ // blessings. We need to know exactly which names matched.
+ // These names will be used later to construct the role
+ // blessings.
+ for _, pattern := range i.config.Members {
+ if pattern.MatchedBy(name) {
+ results = append(results, name)
+ break
+ }
+ }
+ }
+ return results
+}
+
+func extensions(config *Config, role string, blessingNames []string) []string {
+ if !config.Extend {
+ return []string{role}
+ }
+ var extensions []string
+ for _, b := range blessingNames {
+ extensions = append(extensions, role+security.ChainSeparator+b)
+ }
+ return extensions
+}
+
+func caveats(ctx *context.T, config *Config) ([]security.Caveat, error) {
+ if config.Expiry == "" {
+ return nil, nil
+ }
+ d, err := time.ParseDuration(config.Expiry)
+ if err != nil {
+ return nil, verror.Convert(verror.ErrInternal, ctx, err)
+ }
+ expiry, err := security.ExpiryCaveat(time.Now().Add(d))
+ if err != nil {
+ return nil, verror.Convert(verror.ErrInternal, ctx, err)
+ }
+ return []security.Caveat{expiry}, nil
+}
+
+func createBlessings(ctx *context.T, config *Config, principal security.Principal, extensions []string, caveats []security.Caveat, dischargerLocation string) (security.Blessings, error) {
+ blessWith := security.GetCall(ctx).LocalBlessings()
+ blessWithNames := security.LocalBlessingNames(ctx)
+ publicKey := security.GetCall(ctx).RemoteBlessings().PublicKey()
+ if len(blessWithNames) == 0 {
+ return security.Blessings{}, verror.New(errNoLocalBlessings, ctx)
+ }
+
+ var ret security.Blessings
+ for _, ext := range extensions {
+ cav := caveats
+ if config.Audit {
+ // TODO(rthellend): This third-party caveat will only work with a single
+ // discharger service. We need a way to allow multiple instances of this
+ // service to be interchangeable.
+
+ fullNames := make([]string, len(blessWithNames))
+ for i, n := range blessWithNames {
+ fullNames[i] = n + security.ChainSeparator + ext
+ }
+ loggingCaveat, err := security.NewCaveat(LoggingCaveat, fullNames)
+ if err != nil {
+ return security.Blessings{}, verror.Convert(verror.ErrInternal, ctx, err)
+ }
+ thirdParty, err := security.NewPublicKeyCaveat(principal.PublicKey(), dischargerLocation, security.ThirdPartyRequirements{true, true, true}, loggingCaveat)
+ if err != nil {
+ return security.Blessings{}, verror.Convert(verror.ErrInternal, ctx, err)
+ }
+ cav = append(cav, thirdParty)
+ }
+ if len(cav) == 0 {
+ // TODO(rthellend,ashankar): the use of unconstrained
+ // use is concerning. We should figure out how to get
+ // rid of it.
+ // Some options:
+ // - have the seeker specify a set of caveats in the
+ // request (and forcefully insert a restrictive one
+ // or fail if the role server thinks that they are
+ // too loose or something).
+ // - have a set of caveats in the config of the role.
+ cav = []security.Caveat{security.UnconstrainedUse()}
+ }
+ b, err := principal.Bless(publicKey, blessWith, ext, cav[0], cav[1:]...)
+ if err != nil {
+ return security.Blessings{}, verror.Convert(verror.ErrInternal, ctx, err)
+ }
+ if ret, err = security.UnionOfBlessings(ret, b); err != nil {
+ verror.Convert(verror.ErrInternal, ctx, err)
+ }
+ }
+ return ret, nil
+}
diff --git a/services/security/roled/internal/role_test.go b/services/security/roled/internal/role_test.go
new file mode 100644
index 0000000..3ced6b1
--- /dev/null
+++ b/services/security/roled/internal/role_test.go
@@ -0,0 +1,205 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package internal_test
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "reflect"
+ "testing"
+
+ "v.io/v23"
+ "v.io/v23/context"
+ "v.io/v23/naming"
+ "v.io/v23/rpc"
+ "v.io/v23/security"
+ "v.io/v23/verror"
+
+ _ "v.io/x/ref/profiles"
+ vsecurity "v.io/x/ref/security"
+ isecurity "v.io/x/ref/services/security"
+ irole "v.io/x/ref/services/security/roled/internal"
+ "v.io/x/ref/test/testutil"
+)
+
+func TestSeekBlessings(t *testing.T) {
+ ctx, shutdown := v23.Init()
+ defer shutdown()
+
+ workdir, err := ioutil.TempDir("", "test-role-server-")
+ if err != nil {
+ t.Fatal("ioutil.TempDir failed: %v", err)
+ }
+ defer os.RemoveAll(workdir)
+
+ // Role A is a restricted role, i.e. it can be used in sensitive ACLs.
+ roleAConf := irole.Config{
+ Members: []security.BlessingPattern{
+ "root/users/user1/_role",
+ "root/users/user2/_role",
+ "root/users/user3", // _role/A implied
+ },
+ Extend: true,
+ }
+ writeConfig(t, roleAConf, filepath.Join(workdir, "A.conf"))
+
+ // Role B is an unrestricted role.
+ roleBConf := irole.Config{
+ Members: []security.BlessingPattern{
+ "root/users/user1/_role",
+ "root/users/user3/_role",
+ },
+ Audit: true,
+ Extend: false,
+ }
+ writeConfig(t, roleBConf, filepath.Join(workdir, "B.conf"))
+
+ root := testutil.NewIDProvider("root")
+
+ var (
+ user1 = newPrincipalContext(t, ctx, root, "users/user1")
+ user1R = newPrincipalContext(t, ctx, root, "users/user1/_role")
+ user2 = newPrincipalContext(t, ctx, root, "users/user2")
+ user2R = newPrincipalContext(t, ctx, root, "users/user2/_role")
+ user3 = newPrincipalContext(t, ctx, root, "users/user3")
+ user3R = newPrincipalContext(t, ctx, root, "users/user3", "users/user3/_role/foo", "users/user3/_role/bar")
+ )
+
+ testServerCtx := newPrincipalContext(t, ctx, root, "testserver")
+ server, testAddr := newServer(t, testServerCtx)
+ tDisp := &testDispatcher{}
+ if err := server.ServeDispatcher("", tDisp); err != nil {
+ t.Fatalf("server.ServeDispatcher failed: %v", err)
+ }
+
+ const noErr = ""
+ testcases := []struct {
+ ctx *context.T
+ role string
+ errID verror.ID
+ blessings []string
+ }{
+ {user1, "", verror.ErrNoExist.ID, nil},
+ {user1, "unknown", verror.ErrNoAccess.ID, nil},
+ {user2, "unknown", verror.ErrNoAccess.ID, nil},
+ {user3, "unknown", verror.ErrNoAccess.ID, nil},
+
+ {user1, "A", verror.ErrNoAccess.ID, nil},
+ {user1R, "A", noErr, []string{"root/roles/A/root/users/user1/_role"}},
+ {user2, "A", verror.ErrNoAccess.ID, nil},
+ {user2R, "A", noErr, []string{"root/roles/A/root/users/user2/_role"}},
+ {user3, "A", verror.ErrNoAccess.ID, nil},
+ {user3R, "A", noErr, []string{"root/roles/A/root/users/user3/_role/bar", "root/roles/A/root/users/user3/_role/foo"}},
+
+ {user1, "B", verror.ErrNoAccess.ID, nil},
+ {user1R, "B", noErr, []string{"root/roles/B"}},
+ {user2, "B", verror.ErrNoAccess.ID, nil},
+ {user2R, "B", verror.ErrNoAccess.ID, nil},
+ {user3, "B", verror.ErrNoAccess.ID, nil},
+ {user3R, "B", noErr, []string{"root/roles/B"}},
+ }
+ addr := newRoleServer(t, newPrincipalContext(t, ctx, root, "roles"), workdir)
+ for _, tc := range testcases {
+ user := v23.GetPrincipal(tc.ctx).BlessingStore().Default()
+ c := isecurity.RoleClient(naming.Join(addr, tc.role))
+ blessings, err := c.SeekBlessings(tc.ctx)
+ if verror.ErrorID(err) != tc.errID {
+ t.Errorf("unexpected error ID for (%q, %q). Got %#v, expected %#v", user, tc.role, verror.ErrorID(err), tc.errID)
+ }
+ if err == nil {
+ previousBlessings, _ := v23.GetPrincipal(tc.ctx).BlessingStore().Set(blessings, security.AllPrincipals)
+ blessingNames, rejected := callTest(t, tc.ctx, testAddr)
+ if !reflect.DeepEqual(blessingNames, tc.blessings) {
+ t.Errorf("unexpected blessings for (%q, %q). Got %q, expected %q", user, tc.role, blessingNames, tc.blessings)
+ }
+ if len(rejected) != 0 {
+ t.Errorf("unexpected rejected blessings for (%q, %q): %q", user, tc.role, rejected)
+ }
+ v23.GetPrincipal(tc.ctx).BlessingStore().Set(previousBlessings, security.AllPrincipals)
+ }
+ }
+}
+
+func newPrincipalContext(t *testing.T, ctx *context.T, root *testutil.IDProvider, names ...string) *context.T {
+ principal := testutil.NewPrincipal()
+ var blessings []security.Blessings
+ for _, n := range names {
+ blessing, err := root.NewBlessings(principal, n)
+ if err != nil {
+ t.Fatal("root.Bless failed for %q: %v", n, err)
+ }
+ blessings = append(blessings, blessing)
+ }
+ bUnion, err := security.UnionOfBlessings(blessings...)
+ if err != nil {
+ t.Fatal("security.UnionOfBlessings failed: %v", err)
+ }
+ vsecurity.SetDefaultBlessings(principal, bUnion)
+ ctx, err = v23.SetPrincipal(ctx, principal)
+ if err != nil {
+ t.Fatal("v23.SetPrincipal failed: %v", err)
+ }
+ return ctx
+}
+
+func newRoleServer(t *testing.T, ctx *context.T, dir string) string {
+ server, addr := newServer(t, ctx)
+ if err := server.ServeDispatcher("", irole.NewDispatcher(dir, addr)); err != nil {
+ t.Fatalf("ServeDispatcher failed: %v", err)
+ }
+ return addr
+}
+
+func newServer(t *testing.T, ctx *context.T) (rpc.Server, string) {
+ server, err := v23.NewServer(ctx)
+ if err != nil {
+ t.Fatalf("NewServer() failed: %v", err)
+ }
+ spec := rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
+ endpoints, err := server.Listen(spec)
+ if err != nil {
+ t.Fatalf("Listen(%v) failed: %v", spec, err)
+ }
+ return server, endpoints[0].Name()
+}
+
+func writeConfig(t *testing.T, config irole.Config, fileName string) {
+ mConf, err := json.Marshal(config)
+ if err != nil {
+ t.Fatal("json.MarshalIndent failed: %v", err)
+ }
+ if err := ioutil.WriteFile(fileName, mConf, 0644); err != nil {
+ t.Fatal("ioutil.WriteFile(%q, %q) failed: %v", fileName, string(mConf), err)
+ }
+}
+
+func callTest(t *testing.T, ctx *context.T, addr string) (blessingNames []string, rejected []security.RejectedBlessing) {
+ call, err := v23.GetClient(ctx).StartCall(ctx, addr, "Test", nil)
+ if err != nil {
+ t.Fatalf("StartCall failed: %v", err)
+ }
+ if err := call.Finish(&blessingNames, &rejected); err != nil {
+ t.Fatalf("Finish failed: %v", err)
+ }
+ return
+}
+
+type testDispatcher struct {
+}
+
+func (d *testDispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
+ return d, d, nil
+}
+
+func (d *testDispatcher) Authorize(*context.T) error {
+ return nil
+}
+
+func (d *testDispatcher) Test(call rpc.ServerCall) ([]string, []security.RejectedBlessing, error) {
+ blessings, rejected := security.RemoteBlessingNames(call.Context())
+ return blessings, rejected, nil
+}
diff --git a/services/security/roled/main.go b/services/security/roled/main.go
new file mode 100644
index 0000000..997358b
--- /dev/null
+++ b/services/security/roled/main.go
@@ -0,0 +1,57 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+
+ "v.io/v23"
+
+ "v.io/x/lib/vlog"
+ "v.io/x/ref/lib/signals"
+ _ "v.io/x/ref/profiles/static"
+ irole "v.io/x/ref/services/security/roled/internal"
+)
+
+var (
+ configDir = flag.String("config_dir", "", "The directory where the role configuration files are stored.")
+ name = flag.String("name", "", "The name to publish for this service.")
+)
+
+func main() {
+ ctx, shutdown := v23.Init()
+ defer shutdown()
+
+ if len(*configDir) == 0 {
+ fmt.Fprintf(os.Stderr, "--config_dir must be specified\n")
+ os.Exit(1)
+ }
+ if len(*name) == 0 {
+ fmt.Fprintf(os.Stderr, "--name must be specified\n")
+ os.Exit(1)
+ }
+ server, err := v23.NewServer(ctx)
+ if err != nil {
+ vlog.Fatalf("NewServer failed: %v", err)
+ }
+
+ listenSpec := v23.GetListenSpec(ctx)
+ eps, err := server.Listen(listenSpec)
+ if err != nil {
+ vlog.Fatalf("Listen(%v) failed: %v", listenSpec, err)
+ }
+ vlog.Infof("Listening on: %q", eps)
+ if err := server.ServeDispatcher(*name, irole.NewDispatcher(*configDir, *name)); err != nil {
+ vlog.Fatalf("ServeDispatcher(%q) failed: %v", *name, err)
+ }
+ if len(*name) > 0 {
+ fmt.Printf("NAME=%s\n", *name)
+ } else if len(eps) > 0 {
+ fmt.Printf("NAME=%s\n", eps[0].Name())
+ }
+ <-signals.ShutdownOnSignals(ctx)
+}
diff --git a/test/modules/exec.go b/test/modules/exec.go
index d9837c7..35c34e6 100644
--- a/test/modules/exec.go
+++ b/test/modules/exec.go
@@ -15,6 +15,7 @@
"time"
"v.io/v23/mgmt"
+ "v.io/v23/verror"
"v.io/x/lib/vlog"
vexec "v.io/x/ref/lib/exec"
"v.io/x/ref/test/expect"
@@ -248,7 +249,7 @@
// The child has exited already.
case <-time.After(eh.opts.ShutdownTimeout):
// Time out waiting for child to exit.
- procErr = vexec.ErrTimeout
+ procErr = verror.New(vexec.ErrTimeout, nil)
// Force close stdout to unblock any readers of stdout
// (including the drain loop started above).
eh.stdout.Close()
diff --git a/test/modules/modules_test.go b/test/modules/modules_test.go
index ced8fc8..b0745f0 100644
--- a/test/modules/modules_test.go
+++ b/test/modules/modules_test.go
@@ -21,6 +21,7 @@
"time"
"v.io/v23"
+ "v.io/v23/verror"
"v.io/x/ref/lib/exec"
execconsts "v.io/x/ref/lib/exec/consts"
@@ -469,8 +470,8 @@
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 := sh.Cleanup(&stdoutBuf, &stderrBuf); err == nil || verror.ErrorID(err) != exec.ErrTimeout.ID {
+ t.Errorf("unexpected error in Cleanup: got %v, want %v", err, exec.ErrTimeout.ID)
}
if err := syscall.Kill(h.Pid(), syscall.SIGINT); err != nil {
t.Errorf("Kill failed: %v", err)