Merge "veyron/runtimes/google/ipc,veyron/runtimes/google/rt: expose stats for security."
diff --git a/lib/testutil/glob.go b/lib/testutil/glob.go
new file mode 100644
index 0000000..ecd078f
--- /dev/null
+++ b/lib/testutil/glob.go
@@ -0,0 +1,37 @@
+package testutil
+
+import (
+ "io"
+ "sort"
+
+ "veyron.io/veyron/veyron2/ipc"
+ "veyron.io/veyron/veyron2/rt"
+ "veyron.io/veyron/veyron2/services/mounttable/types"
+)
+
+// GlobName calls __Glob on the given object with the given pattern and returns
+// a sorted list of matching object names, or an error.
+func GlobName(name, pattern string) ([]string, error) {
+ call, err := rt.R().Client().StartCall(rt.R().NewContext(), name, ipc.GlobMethod, []interface{}{pattern})
+ if err != nil {
+ return nil, err
+ }
+ results := []string{}
+Loop:
+ for {
+ var me types.MountEntry
+ switch err := call.Recv(&me); err {
+ case nil:
+ results = append(results, me.Name)
+ case io.EOF:
+ break Loop
+ default:
+ return nil, err
+ }
+ }
+ sort.Strings(results)
+ if ferr := call.Finish(&err); ferr != nil {
+ err = ferr
+ }
+ return results, err
+}
diff --git a/profiles/roaming/roaming.go b/profiles/roaming/roaming.go
index 4aa190f..33a1c20 100644
--- a/profiles/roaming/roaming.go
+++ b/profiles/roaming/roaming.go
@@ -72,7 +72,7 @@
func (p *profile) Init(rt veyron2.Runtime, publisher *config.Publisher) error {
log := rt.Logger()
- rt.ConfigureReservedName(debug.NewDispatcher(log.LogDir(), sflag.NewAuthorizerOrDie()))
+ rt.ConfigureReservedName(debug.NewDispatcher(log.LogDir(), sflag.NewAuthorizerOrDie(), rt.VtraceStore()))
lf := commonFlags.ListenFlags()
ListenSpec = ipc.ListenSpec{
diff --git a/profiles/static/static.go b/profiles/static/static.go
index 87b1780..7ff75b1 100644
--- a/profiles/static/static.go
+++ b/profiles/static/static.go
@@ -58,7 +58,7 @@
func (p *static) Init(rt veyron2.Runtime, _ *config.Publisher) error {
log := rt.Logger()
- rt.ConfigureReservedName(debug.NewDispatcher(log.LogDir(), sflag.NewAuthorizerOrDie()))
+ rt.ConfigureReservedName(debug.NewDispatcher(log.LogDir(), sflag.NewAuthorizerOrDie(), rt.VtraceStore()))
lf := commonFlags.ListenFlags()
ListenSpec = ipc.ListenSpec{
diff --git a/runtimes/google/ipc/debug_test.go b/runtimes/google/ipc/debug_test.go
index 054696d..c537fec 100644
--- a/runtimes/google/ipc/debug_test.go
+++ b/runtimes/google/ipc/debug_test.go
@@ -16,6 +16,7 @@
"veyron.io/veyron/veyron/runtimes/google/ipc/stream/sectest"
"veyron.io/veyron/veyron/runtimes/google/ipc/stream/vc"
tnaming "veyron.io/veyron/veyron/runtimes/google/testing/mocks/naming"
+ "veyron.io/veyron/veyron/runtimes/google/vtrace"
"veyron.io/veyron/veyron/services/mgmt/debug"
)
@@ -30,7 +31,8 @@
pclient.AddToRoots(bclient) // Client recognizes "server" as a root of blessings.
pclient.BlessingStore().Set(bclient, "server") // Client presents bclient to server
- debugDisp := debug.NewDispatcher(vlog.Log.LogDir(), nil)
+ store := vtrace.NewStore(10)
+ debugDisp := debug.NewDispatcher(vlog.Log.LogDir(), nil, store)
sm := manager.InternalNew(naming.FixedRoutingID(0x555555555))
defer sm.Shutdown()
@@ -94,12 +96,12 @@
}{
{"", "*", []string{}},
{"", "__*", []string{"__debug"}},
- {"", "__*/*", []string{"__debug/logs", "__debug/pprof", "__debug/stats"}},
- {"__debug", "*", []string{"logs", "pprof", "stats"}},
+ {"", "__*/*", []string{"__debug/logs", "__debug/pprof", "__debug/stats", "__debug/vtrace"}},
+ {"__debug", "*", []string{"logs", "pprof", "stats", "vtrace"}},
}
for _, tc := range testcases {
addr := naming.JoinAddressName(ep.String(), "//"+tc.name)
- call, err := client.StartCall(ctx, addr, "Glob", []interface{}{tc.pattern})
+ call, err := client.StartCall(ctx, addr, ipc.GlobMethod, []interface{}{tc.pattern})
if err != nil {
t.Fatalf("client.StartCall failed: %v", err)
}
diff --git a/runtimes/google/ipc/glob_test.go b/runtimes/google/ipc/glob_test.go
index 654709d..44b7720 100644
--- a/runtimes/google/ipc/glob_test.go
+++ b/runtimes/google/ipc/glob_test.go
@@ -3,7 +3,6 @@
import (
"fmt"
"reflect"
- "sort"
"strings"
"testing"
@@ -12,10 +11,10 @@
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/security"
- "veyron.io/veyron/veyron2/services/mounttable"
"veyron.io/veyron/veyron2/services/mounttable/types"
"veyron.io/veyron/veyron/lib/glob"
+ "veyron.io/veyron/veyron/lib/testutil"
"veyron.io/veyron/veyron/profiles"
)
@@ -159,27 +158,15 @@
}},
}
for _, tc := range testcases {
- c := mounttable.GlobbableClient(naming.JoinAddressName(ep, tc.name))
-
- stream, err := c.Glob(runtime.NewContext(), tc.pattern)
+ name := naming.JoinAddressName(ep, tc.name)
+ results, err := testutil.GlobName(name, tc.pattern)
if err != nil {
- t.Fatalf("Glob failed: %v", err)
+ t.Errorf("unexpected Glob error for (%q, %q): %v", tc.name, tc.pattern, err)
+ continue
}
- results := []string{}
- iterator := stream.RecvStream()
- for iterator.Advance() {
- results = append(results, iterator.Value().Name)
- }
- sort.Strings(results)
if !reflect.DeepEqual(results, tc.expected) {
t.Errorf("unexpected result for (%q, %q). Got %q, want %q", tc.name, tc.pattern, results, tc.expected)
}
- if err := iterator.Err(); err != nil {
- t.Errorf("unexpected stream error for %q: %v", tc.name, err)
- }
- if err := stream.Finish(); err != nil {
- t.Errorf("Finish failed for %q: %v", tc.name, err)
- }
}
}
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index d149ee6..c30c097 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -839,9 +839,7 @@
// value may be modified to match the actual name and method to use.
func (fs *flowServer) lookup(name, method *string) (ipc.Invoker, security.Authorizer, verror.E) {
*name = strings.TrimLeft(*name, "/")
- // TODO(rthellend): Remove "Glob" from the condition below after
- // everything has transitioned to the new name.
- if *method == "Glob" || *method == ipc.GlobMethod {
+ if *method == ipc.GlobMethod {
*method = "Glob"
return ipc.ReflectInvoker(&globInternal{fs, *name}), &acceptAllAuthorizer{}, nil
}
diff --git a/runtimes/google/naming/namespace/glob.go b/runtimes/google/naming/namespace/glob.go
index 4083bb3..ed6cda2 100644
--- a/runtimes/google/naming/namespace/glob.go
+++ b/runtimes/google/naming/namespace/glob.go
@@ -8,6 +8,7 @@
"veyron.io/veyron/veyron/lib/glob"
"veyron.io/veyron/veyron2/context"
+ "veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/options"
"veyron.io/veyron/veyron2/services/mounttable/types"
@@ -60,7 +61,7 @@
// Don't further resolve s.Server.
callCtx, _ := ctx.WithTimeout(callTimeout)
client := ns.rt.Client()
- call, err := client.StartCall(callCtx, s.Server, "Glob", []interface{}{pstr}, options.NoResolve(true))
+ call, err := client.StartCall(callCtx, s.Server, ipc.GlobMethod, []interface{}{pstr}, options.NoResolve(true))
if err != nil {
lastErr = err
continue // try another instance
diff --git a/runtimes/google/rt/rt.go b/runtimes/google/rt/rt.go
index 2cff75d..4b02edf 100644
--- a/runtimes/google/rt/rt.go
+++ b/runtimes/google/rt/rt.go
@@ -158,6 +158,10 @@
copy(rt.reservedOpts, opts)
}
+func (rt *vrt) VtraceStore() vtrace.Store {
+ return rt.traceStore
+}
+
func (rt *vrt) Cleanup() {
if rt.flags.Vtrace.DumpOnShutdown {
vtrace.FormatTraces(os.Stderr, rt.traceStore.TraceRecords(), nil)
diff --git a/runtimes/google/testing/mocks/runtime/panic_runtime.go b/runtimes/google/testing/mocks/runtime/panic_runtime.go
index f5c7c2c..1c28e1d 100644
--- a/runtimes/google/testing/mocks/runtime/panic_runtime.go
+++ b/runtimes/google/testing/mocks/runtime/panic_runtime.go
@@ -48,4 +48,5 @@
func (*PanicRuntime) ConfigureReservedName(ipc.Dispatcher, ...ipc.ServerOpt) {
panic(badRuntime)
}
-func (*PanicRuntime) Cleanup() { panic(badRuntime) }
+func (*PanicRuntime) VtraceStore() vtrace.Store { panic(badRuntime) }
+func (*PanicRuntime) Cleanup() { panic(badRuntime) }
diff --git a/runtimes/google/vtrace/collector.go b/runtimes/google/vtrace/collector.go
index 6c883cc..fc06731 100644
--- a/runtimes/google/vtrace/collector.go
+++ b/runtimes/google/vtrace/collector.go
@@ -27,10 +27,9 @@
type collector struct {
traceID uniqueid.ID
store *Store
-
- mu sync.Mutex
- method vtrace.TraceMethod // GUARDED_BY(mu)
- spans map[uniqueid.ID]*vtrace.SpanRecord // GUARDED_BY(mu)
+ mu sync.Mutex
+ method vtrace.TraceMethod // GUARDED_BY(mu)
+ spans map[uniqueid.ID]*vtrace.SpanRecord // GUARDED_BY(mu)
}
// newCollector returns a new collector for the given traceID.
diff --git a/runtimes/google/vtrace/store.go b/runtimes/google/vtrace/store.go
index 182dea5..f70dc2a 100644
--- a/runtimes/google/vtrace/store.go
+++ b/runtimes/google/vtrace/store.go
@@ -58,20 +58,20 @@
}
// TraceRecords returns TraceRecords for all traces saved in the store.
-func (s *Store) TraceRecords() []*vtrace.TraceRecord {
+func (s *Store) TraceRecords() []vtrace.TraceRecord {
s.mu.Lock()
defer s.mu.Unlock()
- out := make([]*vtrace.TraceRecord, s.size)
+ out := make([]vtrace.TraceRecord, s.size)
i := 0
for _, ts := range s.traces {
- out[i] = ts.traceRecord()
+ ts.traceRecord(&out[i])
i++
}
return out
}
-// TraceRecord returns a TraceRecord for a given uniqueid. Returns
+// TraceRecord returns a TraceRecord for a given ID. Returns
// nil if the given id is not present.
func (s *Store) TraceRecord(id uniqueid.ID) *vtrace.TraceRecord {
s.mu.Lock()
@@ -80,7 +80,9 @@
if ts == nil {
return nil
}
- return ts.traceRecord()
+ out := vtrace.TraceRecord{}
+ ts.traceRecord(&out)
+ return &out
}
// trimLocked removes elements from the store LRU first until we are
@@ -135,9 +137,7 @@
panic("unreachable")
}
-func (ts *traceSet) traceRecord() *vtrace.TraceRecord {
- var out vtrace.TraceRecord
-
+func (ts *traceSet) traceRecord(out *vtrace.TraceRecord) {
// It is possible to have duplicate copies of spans. Consider the
// case where a server calls itself (even indirectly) we'll have one
// Trace in the set for the parent call and one Trace in the set for
@@ -156,5 +156,4 @@
out.Spans = append(out.Spans, span)
}
}
- return &out
}
diff --git a/runtimes/google/vtrace/store_test.go b/runtimes/google/vtrace/store_test.go
index 9fdc06b..910dd07 100644
--- a/runtimes/google/vtrace/store_test.go
+++ b/runtimes/google/vtrace/store_test.go
@@ -27,7 +27,7 @@
return traces
}
-func recordids(records ...*vtrace.TraceRecord) map[uniqueid.ID]bool {
+func recordids(records ...vtrace.TraceRecord) map[uniqueid.ID]bool {
out := make(map[uniqueid.ID]bool)
for _, trace := range records {
out[trace.ID] = true
diff --git a/security/blessingroots.go b/security/blessingroots.go
index 5f314d8..9883122 100644
--- a/security/blessingroots.go
+++ b/security/blessingroots.go
@@ -11,17 +11,12 @@
"veyron.io/veyron/veyron2/security"
)
-const (
- blessingRootsDataFile = "blessingroots.data"
- blessingRootsSigFile = "blessingroots.sig"
-)
-
// blessingRoots implements security.BlessingRoots.
type blessingRoots struct {
- dir string
- signer serialization.Signer
- mu sync.RWMutex
- store map[string][]security.BlessingPattern // GUARDED_BY(mu)
+ persistedData SerializerReaderWriter
+ signer serialization.Signer
+ mu sync.RWMutex
+ store map[string][]security.BlessingPattern // GUARDED_BY(mu)
}
func storeMapKey(root security.PublicKey) (string, error) {
@@ -93,10 +88,14 @@
}
func (br *blessingRoots) save() error {
- if (br.signer == nil) && (br.dir == "") {
+ if (br.signer == nil) && (br.persistedData == nil) {
return nil
}
- return encodeAndStore(br.store, br.dir, blessingRootsDataFile, blessingRootsSigFile, br.signer)
+ data, signature, err := br.persistedData.Writers()
+ if err != nil {
+ return err
+ }
+ return encodeAndStore(br.store, data, signature, br.signer)
}
// newInMemoryBlessingRoots returns an in-memory security.BlessingRoots.
@@ -108,27 +107,26 @@
}
}
-// newPersistingBlessingRoots returns a security.BlessingRoots that signs
-// and persists all updates to the provided directory. Signing is carried
-// out using the provided signer.
-//
-// The returned BlessingRoots is initialized from the existing data present
-// in the directory. The data is verified to have been written by a persisting
-// BlessingRoots object constructed from the same signer.
-//
-// Any errors obtained in reading or verifying the data are returned.
-func newPersistingBlessingRoots(directory string, signer serialization.Signer) (security.BlessingRoots, error) {
- if directory == "" || signer == nil {
- return nil, errors.New("directory or signer is not specified")
+// newPersistingBlessingRoots returns a security.BlessingRoots for a principal
+// that is initialized with the persisted data. The returned security.BlessingRoots
+// also persists any updates to its state.
+func newPersistingBlessingRoots(persistedData SerializerReaderWriter, signer serialization.Signer) (security.BlessingRoots, error) {
+ if persistedData == nil || signer == nil {
+ return nil, errors.New("persisted data or signer is not specified")
}
br := &blessingRoots{
- store: make(map[string][]security.BlessingPattern),
- dir: directory,
- signer: signer,
+ store: make(map[string][]security.BlessingPattern),
+ persistedData: persistedData,
+ signer: signer,
}
-
- if err := decodeFromStorage(&br.store, br.dir, blessingRootsDataFile, blessingRootsSigFile, br.signer.PublicKey()); err != nil {
+ data, signature, err := br.persistedData.Readers()
+ if err != nil {
return nil, err
}
+ if (data != nil) && (signature != nil) {
+ if err := decodeFromStorage(&br.store, data, signature, br.signer.PublicKey()); err != nil {
+ return nil, err
+ }
+ }
return br, nil
}
diff --git a/security/blessingstore.go b/security/blessingstore.go
index b135580..2a03456 100644
--- a/security/blessingstore.go
+++ b/security/blessingstore.go
@@ -13,11 +13,6 @@
"veyron.io/veyron/veyron2/vlog"
)
-const (
- blessingStoreDataFile = "blessingstore.data"
- blessingStoreSigFile = "blessingstore.sig"
-)
-
var errStoreAddMismatch = errors.New("blessing's public key does not match store's public key")
type persistentState struct {
@@ -33,45 +28,45 @@
// blessingStore implements security.BlessingStore.
type blessingStore struct {
- publicKey security.PublicKey
- dir string
- signer serialization.Signer
- mu sync.RWMutex
- state persistentState // GUARDED_BY(mu)
+ publicKey security.PublicKey
+ persistedData SerializerReaderWriter
+ signer serialization.Signer
+ mu sync.RWMutex
+ state persistentState // GUARDED_BY(mu)
}
-func (s *blessingStore) Set(blessings security.Blessings, forPeers security.BlessingPattern) (security.Blessings, error) {
+func (bs *blessingStore) Set(blessings security.Blessings, forPeers security.BlessingPattern) (security.Blessings, error) {
if !forPeers.IsValid() {
return nil, fmt.Errorf("%q is an invalid BlessingPattern", forPeers)
}
- if blessings != nil && !reflect.DeepEqual(blessings.PublicKey(), s.publicKey) {
+ if blessings != nil && !reflect.DeepEqual(blessings.PublicKey(), bs.publicKey) {
return nil, errStoreAddMismatch
}
- s.mu.Lock()
- defer s.mu.Unlock()
- old, hadold := s.state.Store[forPeers]
+ bs.mu.Lock()
+ defer bs.mu.Unlock()
+ old, hadold := bs.state.Store[forPeers]
if blessings != nil {
- s.state.Store[forPeers] = blessings
+ bs.state.Store[forPeers] = blessings
} else {
- delete(s.state.Store, forPeers)
+ delete(bs.state.Store, forPeers)
}
- if err := s.save(); err != nil {
+ if err := bs.save(); err != nil {
if hadold {
- s.state.Store[forPeers] = old
+ bs.state.Store[forPeers] = old
} else {
- delete(s.state.Store, forPeers)
+ delete(bs.state.Store, forPeers)
}
return nil, err
}
return old, nil
}
-func (s *blessingStore) ForPeer(peerBlessings ...string) security.Blessings {
- s.mu.RLock()
- defer s.mu.RUnlock()
+func (bs *blessingStore) ForPeer(peerBlessings ...string) security.Blessings {
+ bs.mu.RLock()
+ defer bs.mu.RUnlock()
var ret security.Blessings
- for pattern, blessings := range s.state.Store {
+ for pattern, blessings := range bs.state.Store {
if pattern.MatchedBy(peerBlessings...) {
if union, err := security.UnionOfBlessings(ret, blessings); err != nil {
vlog.Errorf("UnionOfBlessings(%v, %v) failed: %v, dropping the latter from BlessingStore.ForPeers(%v)", ret, blessings, err, peerBlessings)
@@ -83,35 +78,35 @@
return ret
}
-func (s *blessingStore) Default() security.Blessings {
- s.mu.RLock()
- defer s.mu.RUnlock()
- if s.state.Default != nil {
- return s.state.Default
+func (bs *blessingStore) Default() security.Blessings {
+ bs.mu.RLock()
+ defer bs.mu.RUnlock()
+ if bs.state.Default != nil {
+ return bs.state.Default
}
- return s.ForPeer()
+ return bs.ForPeer()
}
-func (s *blessingStore) SetDefault(blessings security.Blessings) error {
- s.mu.Lock()
- defer s.mu.Unlock()
- if !reflect.DeepEqual(blessings.PublicKey(), s.publicKey) {
+func (bs *blessingStore) SetDefault(blessings security.Blessings) error {
+ bs.mu.Lock()
+ defer bs.mu.Unlock()
+ if !reflect.DeepEqual(blessings.PublicKey(), bs.publicKey) {
return errStoreAddMismatch
}
- oldDefault := s.state.Default
- s.state.Default = blessings
- if err := s.save(); err != nil {
- s.state.Default = oldDefault
+ oldDefault := bs.state.Default
+ bs.state.Default = blessings
+ if err := bs.save(); err != nil {
+ bs.state.Default = oldDefault
}
return nil
}
-func (s *blessingStore) PublicKey() security.PublicKey {
- return s.publicKey
+func (bs *blessingStore) PublicKey() security.PublicKey {
+ return bs.publicKey
}
-func (s *blessingStore) String() string {
- return fmt.Sprintf("{state: %v, publicKey: %v, dir: %v}", s.state, s.publicKey, s.dir)
+func (bs *blessingStore) String() string {
+ return fmt.Sprintf("{state: %v, publicKey: %v}", bs.state, bs.publicKey)
}
// DebugString return a human-readable string encoding of the store
@@ -122,22 +117,26 @@
// <pattern> : <blessings>
// ...
// <pattern> : <blessings>
-func (br *blessingStore) DebugString() string {
+func (bs *blessingStore) DebugString() string {
const format = "%-30s : %s\n"
- b := bytes.NewBufferString(fmt.Sprintf("Default blessings: %v\n", br.state.Default))
+ b := bytes.NewBufferString(fmt.Sprintf("Default blessings: %v\n", bs.state.Default))
b.WriteString(fmt.Sprintf(format, "Peer pattern", "Blessings"))
- for pattern, blessings := range br.state.Store {
+ for pattern, blessings := range bs.state.Store {
b.WriteString(fmt.Sprintf(format, pattern, blessings))
}
return b.String()
}
-func (s *blessingStore) save() error {
- if (s.signer == nil) && (s.dir == "") {
+func (bs *blessingStore) save() error {
+ if (bs.signer == nil) && (bs.persistedData == nil) {
return nil
}
- return encodeAndStore(s.state, s.dir, blessingStoreDataFile, blessingStoreSigFile, s.signer)
+ data, signature, err := bs.persistedData.Writers()
+ if err != nil {
+ return err
+ }
+ return encodeAndStore(bs.state, data, signature, bs.signer)
}
// newInMemoryBlessingStore returns an in-memory security.BlessingStore for a
@@ -152,33 +151,31 @@
}
// newPersistingBlessingStore returns a security.BlessingStore for a principal
-// that persists all updates to the specified directory and uses the provided
-// signer to ensure integrity of data read from the filesystem.
-//
-// The returned BlessingStore is initialized from the existing data present in
-// the directory. The data is verified to have been written by a persisting
-// BlessingStore object constructed from the same signer.
-//
-// Any errors obtained in reading or verifying the data are returned.
-func newPersistingBlessingStore(directory string, signer serialization.Signer) (security.BlessingStore, error) {
- if directory == "" || signer == nil {
- return nil, errors.New("directory or signer is not specified")
+// that is initialized with the persisted data. The returned security.BlessingStore
+// also persists any updates to its state.
+func newPersistingBlessingStore(persistedData SerializerReaderWriter, signer serialization.Signer) (security.BlessingStore, error) {
+ if persistedData == nil || signer == nil {
+ return nil, errors.New("persisted data or signer is not specified")
}
- s := &blessingStore{
- publicKey: signer.PublicKey(),
- state: persistentState{Store: make(map[security.BlessingPattern]security.Blessings)},
- dir: directory,
- signer: signer,
+ bs := &blessingStore{
+ publicKey: signer.PublicKey(),
+ state: persistentState{Store: make(map[security.BlessingPattern]security.Blessings)},
+ persistedData: persistedData,
+ signer: signer,
}
-
- if err := decodeFromStorage(&s.state, s.dir, blessingStoreDataFile, blessingStoreSigFile, s.signer.PublicKey()); err != nil {
+ data, signature, err := bs.persistedData.Readers()
+ if err != nil {
return nil, err
}
-
- for _, b := range s.state.Store {
- if !reflect.DeepEqual(b.PublicKey(), s.publicKey) {
- return nil, fmt.Errorf("directory contains Blessings: %v that are not for the provided PublicKey: %v", b, s.publicKey)
+ if data != nil && signature != nil {
+ if err := decodeFromStorage(&bs.state, data, signature, bs.signer.PublicKey()); err != nil {
+ return nil, err
}
}
- return s, nil
+ for _, b := range bs.state.Store {
+ if !reflect.DeepEqual(b.PublicKey(), bs.publicKey) {
+ return nil, fmt.Errorf("directory contains Blessings: %v that are not for the provided PublicKey: %v", b, bs.publicKey)
+ }
+ }
+ return bs, nil
}
diff --git a/security/principal.go b/security/principal.go
index f0dba02..7247434 100644
--- a/security/principal.go
+++ b/security/principal.go
@@ -9,7 +9,15 @@
"veyron.io/veyron/veyron2/security"
)
-const privateKeyFile = "privatekey.pem"
+const (
+ blessingStoreDataFile = "blessingstore.data"
+ blessingStoreSigFile = "blessingstore.sig"
+
+ blessingRootsDataFile = "blessingroots.data"
+ blessingRootsSigFile = "blessingroots.sig"
+
+ privateKeyFile = "privatekey.pem"
+)
// NewPrincipal mints a new private key and generates a principal based on
// this key, storing its BlessingRoots and BlessingStore in memory.
@@ -27,7 +35,7 @@
return security.CreatePrincipal(signer, newInMemoryBlessingStore(signer.PublicKey()), newInMemoryBlessingRoots())
}
-// NewPersistentPrincipalForSigner creates a new principal using the provided Signer and a
+// NewPersistentPrincipalFromSigner creates a new principal using the provided Signer and a
// partial state (i.e., BlessingRoots, BlessingStore) that is read from the provided directory 'dir'.
// Changes to the partial state are persisted and commited to the same directory; the provided
// signer isn't persisted: the caller is expected to persist it separately or use the
@@ -100,11 +108,22 @@
if err != nil {
return nil, fmt.Errorf("failed to create serialization.Signer: %v", err)
}
- roots, err := newPersistingBlessingRoots(dir, serializationSigner)
+ dataFile := path.Join(dir, blessingRootsDataFile)
+ signatureFile := path.Join(dir, blessingRootsSigFile)
+ fs, err := NewFileSerializer(dataFile, signatureFile)
+ if err != nil {
+ return nil, err
+ }
+ roots, err := newPersistingBlessingRoots(fs, serializationSigner)
if err != nil {
return nil, fmt.Errorf("failed to load BlessingRoots from %q: %v", dir, err)
}
- store, err := newPersistingBlessingStore(dir, serializationSigner)
+ dataFile = path.Join(dir, blessingStoreDataFile)
+ signatureFile = path.Join(dir, blessingStoreSigFile)
+ if fs, err = NewFileSerializer(dataFile, signatureFile); err != nil {
+ return nil, err
+ }
+ store, err := newPersistingBlessingStore(fs, serializationSigner)
if err != nil {
return nil, fmt.Errorf("failed to load BlessingStore from %q: %v", dir, err)
}
diff --git a/security/serializer_reader_writer.go b/security/serializer_reader_writer.go
index 55248e4..633456d 100644
--- a/security/serializer_reader_writer.go
+++ b/security/serializer_reader_writer.go
@@ -2,6 +2,7 @@
import (
"io"
+ "os"
)
// SerializerReaderWriter is a factory for managing the readers and writers used for
@@ -14,3 +15,51 @@
// integrity signature.
Writers() (data io.WriteCloser, signature io.WriteCloser, err error)
}
+
+// FileSerializer implements SerializerReaderWriter that persists state to files.
+type FileSerializer struct {
+ data *os.File
+ signature *os.File
+
+ dataFilePath string
+ signatureFilePath string
+}
+
+// NewFileSerializer creates a FileSerializer with the given data and signature files.
+func NewFileSerializer(dataFilePath, signatureFilePath string) (*FileSerializer, error) {
+ data, err := os.Open(dataFilePath)
+ if err != nil && !os.IsNotExist(err) {
+ return nil, err
+ }
+ signature, err := os.Open(signatureFilePath)
+ if err != nil && !os.IsNotExist(err) {
+ return nil, err
+ }
+ return &FileSerializer{
+ data: data,
+ signature: signature,
+ dataFilePath: dataFilePath,
+ signatureFilePath: signatureFilePath,
+ }, nil
+}
+
+func (fs *FileSerializer) Readers() (io.ReadCloser, io.ReadCloser, error) {
+ if fs.data == nil || fs.signature == nil {
+ return nil, nil, nil
+ }
+ return fs.data, fs.signature, nil
+}
+
+func (fs *FileSerializer) Writers() (io.WriteCloser, io.WriteCloser, error) {
+ // Remove previous version of the files
+ os.Remove(fs.dataFilePath)
+ os.Remove(fs.signatureFilePath)
+ var err error
+ if fs.data, err = os.Create(fs.dataFilePath); err != nil {
+ return nil, nil, err
+ }
+ if fs.signature, err = os.Create(fs.signatureFilePath); err != nil {
+ return nil, nil, err
+ }
+ return fs.data, fs.signature, nil
+}
diff --git a/security/storage.go b/security/storage.go
index 0dd755b..2fdd8cd 100644
--- a/security/storage.go
+++ b/security/storage.go
@@ -1,32 +1,19 @@
package security
import (
- "io/ioutil"
- "os"
- "path"
+ "fmt"
+ "io"
"veyron.io/veyron/veyron/security/serialization"
-
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/vom"
)
-func encodeAndStore(obj interface{}, dir, dataFile, sigFile string, signer serialization.Signer) error {
- // Save the object to temporary data and signature files, and then move
- // those files to the actual data and signature file. This reduces the
- // risk of loosing all saved data on disk in the event of a Write failure.
- data, err := ioutil.TempFile(dir, "data")
- if err != nil {
- return err
+func encodeAndStore(obj interface{}, data, signature io.WriteCloser, signer serialization.Signer) error {
+ if data == nil || signature == nil {
+ return fmt.Errorf("invalid data/signature handles data:%v sig:%v", data, signature)
}
- defer os.Remove(data.Name())
- sig, err := ioutil.TempFile(dir, "sig")
- if err != nil {
- return err
- }
- defer os.Remove(sig.Name())
-
- swc, err := serialization.NewSigningWriteCloser(data, sig, signer, nil)
+ swc, err := serialization.NewSigningWriteCloser(data, signature, signer, nil)
if err != nil {
return err
}
@@ -34,32 +21,16 @@
swc.Close()
return err
}
- if err := swc.Close(); err != nil {
- return err
- }
-
- if err := os.Rename(data.Name(), path.Join(dir, dataFile)); err != nil {
- return err
- }
- return os.Rename(sig.Name(), path.Join(dir, sigFile))
+ return swc.Close()
}
-func decodeFromStorage(obj interface{}, dir, dataFile, sigFile string, publicKey security.PublicKey) error {
- data, dataErr := os.Open(path.Join(dir, dataFile))
- defer data.Close()
- sig, sigErr := os.Open(path.Join(dir, sigFile))
- defer sig.Close()
-
- switch {
- case os.IsNotExist(dataErr) && os.IsNotExist(sigErr):
- return nil
- case dataErr != nil:
- return dataErr
- case sigErr != nil:
- return sigErr
+func decodeFromStorage(obj interface{}, data, signature io.ReadCloser, publicKey security.PublicKey) error {
+ if data == nil || signature == nil {
+ return fmt.Errorf("invalid data/signature handles data:%v sig:%v", data, signature)
}
-
- vr, err := serialization.NewVerifyingReader(data, sig, publicKey)
+ defer data.Close()
+ defer signature.Close()
+ vr, err := serialization.NewVerifyingReader(data, signature, publicKey)
if err != nil {
return err
}
diff --git a/services/mgmt/debug/dispatcher.go b/services/mgmt/debug/dispatcher.go
index 30a8750..89154c5 100644
--- a/services/mgmt/debug/dispatcher.go
+++ b/services/mgmt/debug/dispatcher.go
@@ -6,22 +6,25 @@
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/vtrace"
logreaderimpl "veyron.io/veyron/veyron/services/mgmt/logreader/impl"
pprofimpl "veyron.io/veyron/veyron/services/mgmt/pprof/impl"
statsimpl "veyron.io/veyron/veyron/services/mgmt/stats/impl"
+ vtraceimpl "veyron.io/veyron/veyron/services/mgmt/vtrace/impl"
)
// dispatcher holds the state of the debug dispatcher.
type dispatcher struct {
logsDir string // The root of the logs directory.
auth security.Authorizer
+ store vtrace.Store
}
var _ ipc.Dispatcher = (*dispatcher)(nil)
-func NewDispatcher(logsDir string, authorizer security.Authorizer) *dispatcher {
- return &dispatcher{logsDir, authorizer}
+func NewDispatcher(logsDir string, authorizer security.Authorizer, store vtrace.Store) *dispatcher {
+ return &dispatcher{logsDir, authorizer, store}
}
// The first part of the names of the objects served by this dispatcher.
@@ -41,7 +44,7 @@
return NewSignatureInvoker(suffix), d.auth, nil
}
if suffix == "" {
- return ipc.VChildrenGlobberInvoker("logs", "pprof", "stats"), d.auth, nil
+ return ipc.VChildrenGlobberInvoker("logs", "pprof", "stats", "vtrace"), d.auth, nil
}
parts := strings.SplitN(suffix, "/", 2)
if len(parts) == 2 {
@@ -59,6 +62,8 @@
return pprofimpl.NewInvoker(), d.auth, nil
case "stats":
return statsimpl.NewStatsInvoker(suffix, 10*time.Second), d.auth, nil
+ case "vtrace":
+ return vtraceimpl.NewVtraceService(d.store), d.auth, nil
}
return nil, d.auth, nil
}
diff --git a/services/mgmt/debug/dispatcher_test.go b/services/mgmt/debug/dispatcher_test.go
index 4d6f6b9..2a19ee3 100644
--- a/services/mgmt/debug/dispatcher_test.go
+++ b/services/mgmt/debug/dispatcher_test.go
@@ -2,6 +2,7 @@
import (
"fmt"
+ "io"
"io/ioutil"
"os"
"path/filepath"
@@ -17,10 +18,11 @@
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/services/mgmt/logreader"
"veyron.io/veyron/veyron2/services/mgmt/stats"
- "veyron.io/veyron/veyron2/services/mounttable"
+ "veyron.io/veyron/veyron2/services/mgmt/vtrace"
"veyron.io/veyron/veyron2/verror"
libstats "veyron.io/veyron/veyron/lib/stats"
+ "veyron.io/veyron/veyron/lib/testutil"
"veyron.io/veyron/veyron/profiles"
)
@@ -29,7 +31,7 @@
if len(logsDir) == 0 {
return "", nil, fmt.Errorf("logs directory missing")
}
- disp := NewDispatcher(logsDir, nil)
+ disp := NewDispatcher(logsDir, nil, rt.VtraceStore())
server, err := rt.NewServer()
if err != nil {
return "", nil, fmt.Errorf("failed to start debug server: %v", err)
@@ -66,44 +68,23 @@
// Access a logs directory that exists.
{
- ld := mounttable.GlobbableClient(naming.JoinAddressName(endpoint, "debug/logs"))
- stream, err := ld.Glob(runtime.NewContext(), "*")
+ results, err := testutil.GlobName(naming.JoinAddressName(endpoint, "debug/logs"), "*")
if err != nil {
t.Errorf("Glob failed: %v", err)
}
- results := []string{}
- iterator := stream.RecvStream()
- for count := 0; iterator.Advance(); count++ {
- results = append(results, iterator.Value().Name)
- }
if len(results) != 1 || results[0] != "test.INFO" {
t.Errorf("unexpected result. Got %v, want 'test.INFO'", results)
}
- if err := iterator.Err(); err != nil {
- t.Errorf("unexpected stream error: %v", iterator.Err())
- }
- if err := stream.Finish(); err != nil {
- t.Errorf("Finish failed: %v", err)
- }
}
// Access a logs directory that doesn't exist.
{
- ld := mounttable.GlobbableClient(naming.JoinAddressName(endpoint, "debug/logs/nowheretobefound"))
- stream, err := ld.Glob(runtime.NewContext(), "*")
- if err != nil {
- t.Errorf("Glob failed: %v", err)
- }
- results := []string{}
- iterator := stream.RecvStream()
- for count := 0; iterator.Advance(); count++ {
- results = append(results, iterator.Value().Name)
- }
+ results, err := testutil.GlobName(naming.JoinAddressName(endpoint, "debug/logs/nowheretobefound"), "*")
if len(results) != 0 {
t.Errorf("unexpected result. Got %v, want ''", results)
}
- if expected, got := verror.NoExist, stream.Finish(); !verror.Is(got, expected) {
- t.Errorf("unexpected error value, got %v, want: %v", got, expected)
+ if expected := verror.NoExist; !verror.Is(err, expected) {
+ t.Errorf("unexpected error value, got %v, want: %v", err, expected)
}
}
@@ -152,6 +133,27 @@
}
}
+ // Access vtrace.
+ {
+ vt := vtrace.StoreClient(naming.JoinAddressName(endpoint, "debug/vtrace"))
+ call, err := vt.AllTraces(runtime.NewContext())
+ if err != nil {
+ t.Errorf("AllTraces failed: %v", err)
+ }
+ ntraces := 0
+ stream := call.RecvStream()
+ for stream.Advance() {
+ stream.Value()
+ ntraces++
+ }
+ if err = stream.Err(); err != nil && err != io.EOF {
+ t.Fatalf("Unexpected error reading trace stream: %s", err)
+ }
+ if ntraces < 1 {
+ t.Errorf("We expected at least one trace, got: %d", ntraces)
+ }
+ }
+
// Glob from the root.
{
ns := rt.R().Namespace()
@@ -204,6 +206,7 @@
"logs",
"pprof",
"stats",
+ "vtrace",
}
if !reflect.DeepEqual(expected, results) {
t.Errorf("unexpected result. Got %v, want %v", results, expected)
@@ -229,6 +232,7 @@
"pprof",
"stats",
"stats/testing/foo",
+ "vtrace",
}
if !reflect.DeepEqual(expected, results) {
t.Errorf("unexpected result. Got %v, want %v", results, expected)
diff --git a/services/mgmt/node/impl/impl_test.go b/services/mgmt/node/impl/impl_test.go
index 7c8c79f..8367a64 100644
--- a/services/mgmt/node/impl/impl_test.go
+++ b/services/mgmt/node/impl/impl_test.go
@@ -36,7 +36,6 @@
"veyron.io/veyron/veyron2/services/mgmt/node"
"veyron.io/veyron/veyron2/services/mgmt/pprof"
"veyron.io/veyron/veyron2/services/mgmt/stats"
- "veyron.io/veyron/veyron2/services/mounttable"
"veyron.io/veyron/veyron2/services/mounttable/types"
"veyron.io/veyron/veyron2/verror"
"veyron.io/veyron/veyron2/vlog"
@@ -1020,7 +1019,11 @@
logFileRemoveErrorFatalWarningRE := regexp.MustCompile("(ERROR|FATAL|WARNING)")
statsTrimRE := regexp.MustCompile("/stats/(ipc|system(/start-time.*)?)$")
for _, tc := range testcases {
- results := doGlob(t, tc.name, tc.pattern)
+ results, err := testutil.GlobName(tc.name, tc.pattern)
+ if err != nil {
+ t.Errorf("unexpected glob error for (%q, %q): %v", tc.name, tc.pattern, err)
+ continue
+ }
filteredResults := []string{}
for _, name := range results {
// Keep only the stats object names that match this RE.
@@ -1044,7 +1047,10 @@
}
// Call Size() on the log file objects.
- files := doGlob(t, "nm", "apps/google naps/"+installID+"/"+instance1ID+"/logs/*")
+ files, err := testutil.GlobName("nm", "apps/google naps/"+installID+"/"+instance1ID+"/logs/*")
+ if err != nil {
+ t.Errorf("unexpected glob error: %v", err)
+ }
if want, got := 4, len(files); got < want {
t.Errorf("Unexpected number of matches. Got %d, want at least %d", got, want)
}
@@ -1057,7 +1063,10 @@
}
// Call Value() on some of the stats objects.
- objects := doGlob(t, "nm", "apps/google naps/"+installID+"/"+instance1ID+"/stats/system/start-time*")
+ objects, err := testutil.GlobName("nm", "apps/google naps/"+installID+"/"+instance1ID+"/stats/system/start-time*")
+ if err != nil {
+ t.Errorf("unexpected glob error: %v", err)
+ }
if want, got := 2, len(objects); got != want {
t.Errorf("Unexpected number of matches. Got %d, want %d", got, want)
}
@@ -1086,26 +1095,6 @@
}
}
-func doGlob(t *testing.T, name, pattern string) []string {
- c := mounttable.GlobbableClient(name)
- stream, err := c.Glob(rt.R().NewContext(), pattern)
- if err != nil {
- t.Errorf("Glob failed: %v", err)
- }
- results := []string{}
- iterator := stream.RecvStream()
- for iterator.Advance() {
- results = append(results, iterator.Value().Name)
- }
- if err := iterator.Err(); err != nil {
- t.Errorf("unexpected stream error: %v", err)
- }
- if err := stream.Finish(); err != nil {
- t.Errorf("Finish failed: %v", err)
- }
- return results
-}
-
func listAndVerifyAssociations(t *testing.T, stub node.NodeClientMethods, run veyron2.Runtime, expected []node.Association) {
assocs, err := stub.ListAssociations(run.NewContext())
if err != nil {
diff --git a/services/mgmt/vtrace/impl/vtrace_invoker.go b/services/mgmt/vtrace/impl/vtrace_invoker.go
new file mode 100644
index 0000000..d9cbe0a
--- /dev/null
+++ b/services/mgmt/vtrace/impl/vtrace_invoker.go
@@ -0,0 +1,40 @@
+package impl
+
+import (
+ "veyron.io/veyron/veyron2/ipc"
+ "veyron.io/veyron/veyron2/uniqueid"
+ "veyron.io/veyron/veyron2/verror2"
+ "veyron.io/veyron/veyron2/vtrace"
+)
+
+type vtraceServer struct {
+ store vtrace.Store
+}
+
+func (v *vtraceServer) Trace(call ipc.ServerCall, id uniqueid.ID) (vtrace.TraceRecord, error) {
+ tr := v.store.TraceRecord(id)
+ if tr == nil {
+ return vtrace.TraceRecord{}, verror2.Make(verror2.NoExist, call, "No trace with id %x", id)
+ }
+ return *tr, nil
+}
+
+func (v *vtraceServer) AllTraces(call ipc.ServerCall) error {
+ // TODO(mattr): Consider changing the store to allow us to iterate through traces
+ // when there are many.
+ traces := v.store.TraceRecords()
+ for i := range traces {
+ if err := call.Send(traces[i]); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func NewVtraceInvoker(store vtrace.Store) ipc.Invoker {
+ return ipc.ReflectInvoker(&vtraceServer{store})
+}
+
+func NewVtraceService(store vtrace.Store) interface{} {
+ return &vtraceServer{store}
+}
diff --git a/services/mgmt/vtrace/impl/vtrace_invoker_test.go b/services/mgmt/vtrace/impl/vtrace_invoker_test.go
new file mode 100644
index 0000000..c967d79
--- /dev/null
+++ b/services/mgmt/vtrace/impl/vtrace_invoker_test.go
@@ -0,0 +1,90 @@
+package impl_test
+
+import (
+ "io"
+ "testing"
+
+ "veyron.io/veyron/veyron2"
+ "veyron.io/veyron/veyron2/ipc"
+ "veyron.io/veyron/veyron2/naming"
+ "veyron.io/veyron/veyron2/rt"
+ service "veyron.io/veyron/veyron2/services/mgmt/vtrace"
+ "veyron.io/veyron/veyron2/vtrace"
+
+ "veyron.io/veyron/veyron/profiles"
+ "veyron.io/veyron/veyron/services/mgmt/vtrace/impl"
+)
+
+func setup(t *testing.T) (string, ipc.Server, veyron2.Runtime) {
+ runtime, err := rt.New()
+ if err != nil {
+ t.Fatalf("Could not create runtime: %s", err)
+ }
+
+ server, err := runtime.NewServer()
+ if err != nil {
+ t.Fatalf("Could not create server: %s", err)
+ }
+ endpoint, err := server.Listen(profiles.LocalListenSpec)
+ if err != nil {
+ t.Fatalf("Listen failed: %s", err)
+ }
+ if err := server.Serve("", impl.NewVtraceService(runtime.VtraceStore()), nil); err != nil {
+ t.Fatalf("Serve failed: %s", err)
+ }
+ return endpoint.String(), server, runtime
+}
+
+func TestVtraceInvoker(t *testing.T) {
+ endpoint, server, runtime := setup(t)
+ defer server.Stop()
+
+ sctx := runtime.NewContext()
+ sctx, span := runtime.WithNewSpan(sctx, "The Span")
+ span.Trace().ForceCollect()
+ span.Finish()
+ id := span.Trace().ID()
+
+ client := service.StoreClient(naming.JoinAddressName(endpoint, ""))
+
+ trace, err := client.Trace(runtime.NewContext(), id)
+ if err != nil {
+ t.Fatalf("Unexpected error getting trace: %s", err)
+ }
+ if len(trace.Spans) != 1 {
+ t.Errorf("Returned trace should have 1 span, found %#v", trace)
+ }
+ if trace.Spans[0].Name != "The Span" {
+ t.Errorf("Returned span has wrong name: %#v", trace)
+ }
+
+ call, err := client.AllTraces(runtime.NewContext())
+ if err != nil {
+ t.Fatalf("Unexpected error getting traces: %s", err)
+ }
+ ntraces := 0
+ stream := call.RecvStream()
+ var tr *vtrace.TraceRecord
+ for stream.Advance() {
+ trace := stream.Value()
+ if trace.ID == id {
+ tr = &trace
+ }
+ ntraces++
+ }
+ if err = stream.Err(); err != nil && err != io.EOF {
+ t.Fatalf("Unexpected error reading trace stream: %s", err)
+ }
+ if ntraces < 2 {
+ t.Fatalf("Expected at least 2 traces, got %#v", ntraces)
+ }
+ if tr == nil {
+ t.Fatalf("Desired trace %x not found.", id)
+ }
+ if len(tr.Spans) != 1 {
+ t.Errorf("Returned trace should have 1 span, found %#v", tr)
+ }
+ if tr.Spans[0].Name != "The Span" {
+ t.Fatalf("Returned span has wrong name: %#v", tr)
+ }
+}
diff --git a/tools/debug/test.sh b/tools/debug/test.sh
index afb886e..8c38207 100755
--- a/tools/debug/test.sh
+++ b/tools/debug/test.sh
@@ -39,7 +39,8 @@
|| (dumplogs "${DBGLOG}"; shell_test::fail "line ${LINENO}: failed to run debug")
WANT="${EP}/__debug/logs
${EP}/__debug/pprof
-${EP}/__debug/stats"
+${EP}/__debug/stats
+${EP}/__debug/vtrace"
shell_test::assert_eq "${GOT}" "${WANT}" "${LINENO}"
# Test logs glob.