veyron/services/mgmt/node: Hook up the logreader
This change hooks up the logreader implementation to the node manager
apps objects.
Change-Id: I8ccca706a34b02fea32e31ca5a3ca1696fbca15f
diff --git a/services/mgmt/node/impl/app_invoker.go b/services/mgmt/node/impl/app_invoker.go
index e26b2bb..c359a2a 100644
--- a/services/mgmt/node/impl/app_invoker.go
+++ b/services/mgmt/node/impl/app_invoker.go
@@ -581,17 +581,22 @@
}
// instanceDir returns the path to the directory containing the app instance
+// referred to by the given suffix relative to the given root directory.
+func instanceDir(root string, suffix []string) (string, error) {
+ if nComponents := len(suffix); nComponents != 3 {
+ return "", errInvalidSuffix
+ }
+ app, installation, instance := suffix[0], suffix[1], suffix[2]
+ instancesDir := filepath.Join(root, applicationDirName(app), installationDirName(installation), "instances")
+ instanceDir := filepath.Join(instancesDir, instanceDirName(instance))
+ return instanceDir, nil
+}
+
+// instanceDir returns the path to the directory containing the app instance
// referred to by the invoker's suffix, as well as the corresponding stopped
// instance dir. Returns an error if the suffix does not name an instance.
func (i *appInvoker) instanceDir() (string, error) {
- components := i.suffix
- if nComponents := len(components); nComponents != 3 {
- return "", errInvalidSuffix
- }
- app, installation, instance := components[0], components[1], components[2]
- instancesDir := filepath.Join(i.config.Root, applicationDirName(app), installationDirName(installation), "instances")
- instanceDir := filepath.Join(instancesDir, instanceDirName(instance))
- return instanceDir, nil
+ return instanceDir(i.config.Root, i.suffix)
}
func (i *appInvoker) Resume(call ipc.ServerContext) error {
@@ -856,7 +861,8 @@
return nil
}
for _, path := range instances {
- if _, err := loadInstanceInfo(filepath.Dir(path)); err != nil {
+ instanceDir := filepath.Dir(path)
+ if _, err := loadInstanceInfo(instanceDir); err != nil {
continue
}
relpath, _ := filepath.Rel(i.config.Root, path)
@@ -869,12 +875,25 @@
installID := strings.TrimPrefix(elems[1], "installation-")
instanceID := strings.TrimPrefix(elems[3], "instance-")
if title, ok := appIDMap[appID]; ok {
- tree.find([]string{title, installID, instanceID}, true)
+ n := tree.find([]string{title, installID, instanceID, "logs"}, true)
+ i.addLogFiles(n, filepath.Join(instanceDir, "logs"))
}
}
return tree
}
+func (i *appInvoker) addLogFiles(n *treeNode, dir string) {
+ filepath.Walk(dir, func(path string, _ os.FileInfo, _ error) error {
+ if path == dir {
+ // Skip the logs directory itself.
+ return nil
+ }
+ relpath, _ := filepath.Rel(dir, path)
+ n.find(strings.Split(relpath, string(filepath.Separator)), true)
+ return nil
+ })
+}
+
func (i *appInvoker) Glob(ctx ipc.ServerContext, pattern string, stream mounttable.GlobbableServiceGlobStream) error {
g, err := glob.Parse(pattern)
if err != nil {
diff --git a/services/mgmt/node/impl/dispatcher.go b/services/mgmt/node/impl/dispatcher.go
index ffc2cf6..655e758 100644
--- a/services/mgmt/node/impl/dispatcher.go
+++ b/services/mgmt/node/impl/dispatcher.go
@@ -15,10 +15,12 @@
vflag "veyron.io/veyron/veyron/security/flag"
"veyron.io/veyron/veyron/security/serialization"
"veyron.io/veyron/veyron/services/mgmt/lib/toplevelglob"
+ logsimpl "veyron.io/veyron/veyron/services/mgmt/logreader/impl"
inode "veyron.io/veyron/veyron/services/mgmt/node"
"veyron.io/veyron/veyron/services/mgmt/node/config"
"veyron.io/veyron/veyron2/ipc"
+ "veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/services/mgmt/node"
@@ -245,6 +247,15 @@
})
return ipc.ReflectInvoker(receiver), d.auth, nil
case appsSuffix:
+ if method != "Glob" && len(components) >= 5 && components[4] == "logs" {
+ appInstanceDir, err := instanceDir(d.config.Root, components[1:4])
+ if err != nil {
+ return nil, nil, err
+ }
+ logsDir := filepath.Join(appInstanceDir, "logs")
+ suffix := naming.Join(components[5:]...)
+ return logsimpl.NewLogFileInvoker(logsDir, suffix), d.auth, nil
+ }
receiver := node.NewServerApplication(&appInvoker{
callback: d.internal.callback,
config: d.config,
diff --git a/services/mgmt/node/impl/impl_test.go b/services/mgmt/node/impl/impl_test.go
index fdd2c32..579e5c2 100644
--- a/services/mgmt/node/impl/impl_test.go
+++ b/services/mgmt/node/impl/impl_test.go
@@ -17,6 +17,7 @@
"path"
"path/filepath"
"reflect"
+ "regexp"
"sort"
"strconv"
"strings"
@@ -39,6 +40,7 @@
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/services/mgmt/application"
+ "veyron.io/veyron/veyron2/services/mgmt/logreader"
"veyron.io/veyron/veyron2/services/mgmt/node"
"veyron.io/veyron/veyron2/services/mounttable"
"veyron.io/veyron/veyron2/verror"
@@ -962,7 +964,7 @@
runNM.Cleanup()
}
-func TestNodeManagerGlob(t *testing.T) {
+func TestNodeManagerGlobAndLogs(t *testing.T) {
// Set up mount table, application, and binary repositories.
defer setupLocalNamespace(t)()
envelope, cleanup := startApplicationRepository()
@@ -1013,37 +1015,64 @@
"apps/google naps",
"apps/google naps/" + installID,
"apps/google naps/" + installID + "/" + instance1ID,
+ "apps/google naps/" + installID + "/" + instance1ID + "/logs",
+ "apps/google naps/" + installID + "/" + instance1ID + "/logs/STDERR-<timestamp>",
+ "apps/google naps/" + installID + "/" + instance1ID + "/logs/STDOUT-<timestamp>",
"nm",
}},
{"nm/apps", "*", []string{"google naps"}},
{"nm/apps/google naps", "*", []string{installID}},
+ {"nm/apps/google naps/" + installID, "*", []string{instance1ID}},
+ {"nm/apps/google naps/" + installID + "/" + instance1ID, "*", []string{"logs"}},
+ {"nm/apps/google naps/" + installID + "/" + instance1ID + "/logs", "*", []string{"STDERR-<timestamp>", "STDOUT-<timestamp>"}},
}
+ re := regexp.MustCompile("(STDOUT|STDERR)-[0-9]+$")
for _, tc := range testcases {
- c, err := mounttable.BindGlobbable(tc.name)
- if err != nil {
- t.Fatalf("BindGlobbable failed: %v", err)
+ results := doGlob(t, tc.name, tc.pattern)
+ for i, name := range results {
+ results[i] = re.ReplaceAllString(name, "$1-<timestamp>")
}
-
- stream, err := c.Glob(rt.R().NewContext(), tc.pattern)
- if err != nil {
- t.Errorf("Glob failed: %v", err)
- }
- 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. Got %q, want %q", results, tc.expected)
- }
- 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)
+ t.Errorf("unexpected result for (%q, %q). Got %q, want %q", tc.name, tc.pattern, results, tc.expected)
}
}
+
+ // Call Size() on the log file objects
+ files := doGlob(t, "nm", "apps/google naps/"+installID+"/"+instance1ID+"/logs/*")
+ for _, file := range files {
+ name := naming.Join("nm", file)
+ c, err := logreader.BindLogFile(name)
+ if err != nil {
+ t.Fatalf("BindLogFile failed: %v", err)
+ }
+ if _, err := c.Size(rt.R().NewContext()); err != nil {
+ t.Errorf("Size(%q) failed: %v", name, err)
+ }
+ }
+}
+
+func doGlob(t *testing.T, name, pattern string) []string {
+ c, err := mounttable.BindGlobbable(name)
+ if err != nil {
+ t.Fatalf("BindGlobbable failed: %v", err)
+ }
+ 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)
+ }
+ sort.Strings(results)
+ return results
}
func listAndVerifyAssociations(t *testing.T, stub node.Node, run veyron2.Runtime, expected []node.Association) {
diff --git a/services/mgmt/node/impl/node_invoker.go b/services/mgmt/node/impl/node_invoker.go
index a751d11..aae24f4 100644
--- a/services/mgmt/node/impl/node_invoker.go
+++ b/services/mgmt/node/impl/node_invoker.go
@@ -393,7 +393,6 @@
}
func (i *nodeInvoker) Glob(ctx ipc.ServerContext, pattern string, stream mounttable.GlobbableServiceGlobStream) error {
- // TODO(rthellend): Finish implementing Glob
g, err := glob.Parse(pattern)
if err != nil {
return err