Merge "veyron2/vdl: New API for generated Go interfaces."
diff --git a/services/mgmt/node/impl/app_invoker.go b/services/mgmt/node/impl/app_invoker.go
index 247d7d0..6ff19f7 100644
--- a/services/mgmt/node/impl/app_invoker.go
+++ b/services/mgmt/node/impl/app_invoker.go
@@ -300,7 +300,7 @@
installationID := generateID()
installationDir := filepath.Join(i.config.Root, applicationDirName(envelope.Title), installationDirName(installationID))
deferrer := func() {
- cleanupDir(installationDir)
+ cleanupDir(installationDir, "")
}
defer func() {
if deferrer != nil {
@@ -476,18 +476,17 @@
if err := mkdir(logDir); err != nil {
return nil, err
}
+ cmd.Args = append(cmd.Args, "--logdir", logDir)
timestamp := time.Now().UnixNano()
+
stdoutLog := filepath.Join(logDir, fmt.Sprintf("STDOUT-%d", timestamp))
if cmd.Stdout, err = openWriteFile(stdoutLog); err != nil {
return nil, err
}
- cmd.Args = append(cmd.Args, "--stdoutlog", stdoutLog)
-
stderrLog := filepath.Join(logDir, fmt.Sprintf("STDERR-%d", timestamp))
if cmd.Stderr, err = openWriteFile(stderrLog); err != nil {
return nil, err
}
- cmd.Args = append(cmd.Args, "--stderrlog", stderrLog)
cmd.Args = append(cmd.Args, "--run", binPath)
cmd.Args = append(cmd.Args, "--")
@@ -559,25 +558,26 @@
}
func (i *appInvoker) Start(call ipc.ServerContext) ([]string, error) {
+ helper := i.config.Helper
instanceDir, instanceID, err := i.newInstance()
if err != nil {
- cleanupDir(instanceDir)
+ cleanupDir(instanceDir, helper)
return nil, err
}
- systemName, err := systemAccountForHelper(i.config.Helper, call.RemoteBlessings().ForContext(call), i.uat)
+ systemName, err := systemAccountForHelper(helper, call.RemoteBlessings().ForContext(call), i.uat)
if err != nil {
- cleanupDir(instanceDir)
+ cleanupDir(instanceDir, helper)
return nil, err
}
if err := saveSystemNameForInstance(instanceDir, systemName); err != nil {
- cleanupDir(instanceDir)
+ cleanupDir(instanceDir, helper)
return nil, err
}
if err = i.run(instanceDir, systemName); err != nil {
- cleanupDir(instanceDir)
+ cleanupDir(instanceDir, helper)
return nil, err
}
return []string{instanceID}, nil
@@ -743,7 +743,7 @@
}
versionDir, err := newVersion(installationDir, newEnvelope, oldVersionDir)
if err != nil {
- cleanupDir(versionDir)
+ cleanupDir(versionDir, "")
return err
}
return nil
diff --git a/services/mgmt/node/impl/impl_helper_test.go b/services/mgmt/node/impl/impl_helper_test.go
new file mode 100644
index 0000000..1eaaeda
--- /dev/null
+++ b/services/mgmt/node/impl/impl_helper_test.go
@@ -0,0 +1,45 @@
+package impl_test
+
+// Separate from impl_test to avoid contributing further to impl_test bloat.
+// TODO(rjkroege): Move all helper-related tests to here.
+
+import (
+ "io/ioutil"
+ "os"
+ "path"
+ "testing"
+
+ "veyron.io/veyron/veyron/services/mgmt/node/impl"
+)
+
+func TestBaseCleanupDir(t *testing.T) {
+ dir, err := ioutil.TempDir("", "impl_helper_test")
+ if err != nil {
+ t.Fatalf("ioutil.TempDir() failed: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ // Setup some files to delete.
+ helperTarget := path.Join(dir, "helper_target")
+ if err := os.MkdirAll(helperTarget, os.FileMode(0700)); err != nil {
+ t.Fatalf("os.MkdirAll(%s) failed: %v", helperTarget, err)
+ }
+
+ nohelperTarget := path.Join(dir, "nohelper_target")
+ if err := os.MkdirAll(nohelperTarget, os.FileMode(0700)); err != nil {
+ t.Fatalf("os.MkdirAll(%s) failed: %v", nohelperTarget, err)
+ }
+
+ // Setup a helper.
+ helper := generateSuidHelperScript(t, dir)
+
+ impl.WrapBaseCleanupDir(helperTarget, helper)
+ if _, err := os.Stat(helperTarget); err == nil || os.IsExist(err) {
+ t.Fatalf("%s should be missing but isn't", helperTarget)
+ }
+
+ impl.WrapBaseCleanupDir(nohelperTarget, "")
+ if _, err := os.Stat(nohelperTarget); err == nil || os.IsExist(err) {
+ t.Fatalf("%s should be missing but isn't", nohelperTarget)
+ }
+}
diff --git a/services/mgmt/node/impl/impl_test.go b/services/mgmt/node/impl/impl_test.go
index f35721c..8f44059 100644
--- a/services/mgmt/node/impl/impl_test.go
+++ b/services/mgmt/node/impl/impl_test.go
@@ -276,30 +276,7 @@
return path
}
-// generateSuidHelperScript builds a script to execute the test target as
-// a suidhelper instance and installs it in <root>/helper.
-func generateSuidHelperScript(t *testing.T, root string) string {
- output := "#!/bin/bash\n"
- output += "VEYRON_SUIDHELPER_TEST=1"
- output += " "
- output += "exec" + " " + os.Args[0] + " " + "-minuid=1" + " " + "-test.run=TestSuidHelper $*"
- output += "\n"
-
- vlog.VI(1).Infof("script\n%s", output)
-
- if err := os.MkdirAll(root, 0755); err != nil {
- t.Fatalf("MkdirAll failed: %v", err)
- }
- // Helper does not need to live under the node manager's root dir, but
- // we put it there for convenience.
- path := filepath.Join(root, "helper.sh")
- if err := ioutil.WriteFile(path, []byte(output), 0755); err != nil {
- t.Fatalf("WriteFile(%v) failed: %v", path, err)
- }
- return path
-}
-
-// readPID waits for the "ready:<PID>" line from the child and parses out the
+/// readPID waits for the "ready:<PID>" line from the child and parses out the
// PID of the child.
func readPID(t *testing.T, s *expect.Session) int {
m := s.ExpectRE("ready:([0-9]+)", -1)
diff --git a/services/mgmt/node/impl/node_invoker.go b/services/mgmt/node/impl/node_invoker.go
index b2952f9..0e50fdd 100644
--- a/services/mgmt/node/impl/node_invoker.go
+++ b/services/mgmt/node/impl/node_invoker.go
@@ -339,7 +339,7 @@
}
deferrer := func() {
- cleanupDir(workspace)
+ cleanupDir(workspace, "")
}
defer func() {
if deferrer != nil {
diff --git a/services/mgmt/node/impl/only_for_test.go b/services/mgmt/node/impl/only_for_test.go
index 3261c42..b110699 100644
--- a/services/mgmt/node/impl/only_for_test.go
+++ b/services/mgmt/node/impl/only_for_test.go
@@ -24,9 +24,14 @@
}
func init() {
- cleanupDir = func(dir string) {
+ cleanupDir = func(dir, helper string) {
parentDir, base := filepath.Dir(dir), filepath.Base(dir)
- renamed := filepath.Join(parentDir, "deleted_"+base)
+ var renamed string
+ if helper != "" {
+ renamed = filepath.Join(parentDir, "helper_deleted_"+base)
+ } else {
+ renamed = filepath.Join(parentDir, "deleted_"+base)
+ }
if err := os.Rename(dir, renamed); err != nil {
vlog.Errorf("Rename(%v, %v) failed: %v", dir, renamed, err)
}
@@ -38,3 +43,7 @@
vlog.VI(2).Infof("Mock isSetuid is reporting: %v", *mockIsSetuid)
return *mockIsSetuid
}
+
+func WrapBaseCleanupDir(path, helper string) {
+ baseCleanupDir(path, helper)
+}
diff --git a/services/mgmt/node/impl/util.go b/services/mgmt/node/impl/util.go
index d8cca9d..29ca3da 100644
--- a/services/mgmt/node/impl/util.go
+++ b/services/mgmt/node/impl/util.go
@@ -3,6 +3,7 @@
import (
"io/ioutil"
"os"
+ "os/exec"
"path/filepath"
"time"
@@ -76,10 +77,24 @@
return nil
}
-// cleanupDir is defined like this so we can override its implementation for
-// tests.
-var cleanupDir = func(dir string) {
- if err := os.RemoveAll(dir); err != nil {
- vlog.Errorf("RemoveAll(%v) failed: %v", dir, err)
+func baseCleanupDir(path, helper string) {
+ if helper != "" {
+ out, err := exec.Command(helper, "--rm", path).CombinedOutput()
+ if err != nil {
+ vlog.Errorf("exec.Command(%s %s %s).CombinedOutput() failed: %v", helper, "--rm", path, err)
+ return
+ }
+ if len(out) != 0 {
+ vlog.Errorf("exec.Command(%s %s %s).CombinedOutput() generated output: %v", helper, "--rm", path, string(out))
+ }
+ } else {
+ if err := os.RemoveAll(path); err != nil {
+ vlog.Errorf("RemoveAll(%v) failed: %v", path, err)
+ }
}
}
+
+// cleanupDir is defined like this so we can override its implementation for
+// tests. cleanupDir will use the helper to delete application state possibly
+// owned by different accounts if helper is provided.
+var cleanupDir = baseCleanupDir
diff --git a/services/mgmt/node/impl/util_test.go b/services/mgmt/node/impl/util_test.go
index fce2764..00762a5 100644
--- a/services/mgmt/node/impl/util_test.go
+++ b/services/mgmt/node/impl/util_test.go
@@ -330,3 +330,26 @@
t.Fatalf("ListAssociations() got %v, expected %v", got, expected)
}
}
+
+// generateSuidHelperScript builds a script to execute the test target as
+// a suidhelper instance and returns the path to the script.
+func generateSuidHelperScript(t *testing.T, root string) string {
+ output := "#!/bin/bash\n"
+ output += "VEYRON_SUIDHELPER_TEST=1"
+ output += " "
+ output += "exec" + " " + os.Args[0] + " " + "-minuid=1" + " " + "-test.run=TestSuidHelper $*"
+ output += "\n"
+
+ vlog.VI(1).Infof("script\n%s", output)
+
+ if err := os.MkdirAll(root, 0755); err != nil {
+ t.Fatalf("MkdirAll failed: %v", err)
+ }
+ // Helper does not need to live under the node manager's root dir, but
+ // we put it there for convenience.
+ path := filepath.Join(root, "helper.sh")
+ if err := ioutil.WriteFile(path, []byte(output), 0755); err != nil {
+ t.Fatalf("WriteFile(%v) failed: %v", path, err)
+ }
+ return path
+}
diff --git a/services/mgmt/suidhelper/impl/args.go b/services/mgmt/suidhelper/impl/args.go
index 2cc26ae..7ab9574 100644
--- a/services/mgmt/suidhelper/impl/args.go
+++ b/services/mgmt/suidhelper/impl/args.go
@@ -16,8 +16,7 @@
uid int
gid int
workspace string
- stderrLog string
- stdoutLog string
+ logDir string
argv0 string
argv []string
envv []string
@@ -26,17 +25,16 @@
}
type ArgsSavedForTest struct {
- Uname string
- Workpace string
- Run string
- StdoutLog string
- StderrLog string
+ Uname string
+ Workpace string
+ Run string
+ LogDir string
}
const SavedArgs = "VEYRON_SAVED_ARGS"
var (
- flagUsername, flagWorkspace, flagStdoutLog, flagStderrLog, flagRun *string
+ flagUsername, flagWorkspace, flagLogDir, flagRun *string
flagMinimumUid *int64
flagRemove *bool
)
@@ -51,8 +49,7 @@
}
flagUsername = sflag.Username
flagWorkspace = sflag.Workspace
- flagStdoutLog = sflag.StdoutLog
- flagStderrLog = sflag.StderrLog
+ flagLogDir = sflag.LogDir
flagRun = sflag.Run
flagMinimumUid = sflag.MinimumUid
flagRemove = sflag.Remove
@@ -97,11 +94,10 @@
b := new(bytes.Buffer)
enc := json.NewEncoder(b)
enc.Encode(ArgsSavedForTest{
- Uname: *flagUsername,
- Workpace: *flagWorkspace,
- Run: *flagRun,
- StdoutLog: *flagStdoutLog,
- StderrLog: *flagStderrLog,
+ Uname: *flagUsername,
+ Workpace: *flagWorkspace,
+ Run: *flagRun,
+ LogDir: *flagLogDir,
})
env = append(env, SavedArgs+"="+b.String())
wp.dryrun = true
@@ -111,8 +107,7 @@
wp.gid = int(gid)
wp.workspace = *flagWorkspace
wp.argv0 = *flagRun
- wp.stdoutLog = *flagStdoutLog
- wp.stderrLog = *flagStderrLog
+ wp.logDir = *flagLogDir
wp.argv = append([]string{wp.argv0}, fs.Args()...)
// TODO(rjkroege): Reduce the environment to the absolute minimum needed.
wp.envv = env
diff --git a/services/mgmt/suidhelper/impl/args_test.go b/services/mgmt/suidhelper/impl/args_test.go
index a9f0cde..9358e9f 100644
--- a/services/mgmt/suidhelper/impl/args_test.go
+++ b/services/mgmt/suidhelper/impl/args_test.go
@@ -30,8 +30,7 @@
uid: testUid,
gid: testGid,
workspace: "",
- stderrLog: "",
- stdoutLog: "",
+ logDir: "",
argv0: "",
argv: []string{""},
envv: []string{"A=B"},
@@ -42,15 +41,14 @@
{
[]string{"setuidhelper", "--minuid", "1", "--username", testUserName, "--workspace", "/hello",
- "--stdoutlog", "/stdout", "--stderrlog", "/stderr", "--run", "/bin/veyron", "--", "one", "two"},
+ "--logdir", "/logging", "--run", "/bin/veyron", "--", "one", "two"},
[]string{"A=B"},
nil,
WorkParameters{
uid: testUid,
gid: testGid,
workspace: "/hello",
- stderrLog: "/stderr",
- stdoutLog: "/stdout",
+ logDir: "/logging",
argv0: "/bin/veyron",
argv: []string{"/bin/veyron", "one", "two"},
envv: []string{"A=B"},
@@ -73,8 +71,7 @@
uid: 0,
gid: 0,
workspace: "",
- stderrLog: "",
- stdoutLog: "",
+ logDir: "",
argv0: "",
argv: []string{"hello", "vanadium"},
envv: nil,
diff --git a/services/mgmt/suidhelper/impl/flag/flag.go b/services/mgmt/suidhelper/impl/flag/flag.go
index 674227e..6a4cdb3 100644
--- a/services/mgmt/suidhelper/impl/flag/flag.go
+++ b/services/mgmt/suidhelper/impl/flag/flag.go
@@ -13,7 +13,7 @@
import "flag"
var (
- Username, Workspace, StdoutLog, StderrLog, Run *string
+ Username, Workspace, LogDir, Run *string
MinimumUid *int64
Remove *bool
)
@@ -25,8 +25,7 @@
func SetupFlags(fs *flag.FlagSet) {
Username = fs.String("username", "", "The UNIX user name used for the other functions of this tool.")
Workspace = fs.String("workspace", "", "Path to the application's workspace directory.")
- StdoutLog = fs.String("stdoutlog", "", "Path to the stdout log file.")
- StderrLog = fs.String("stderrlog", "", "Path to the stdin log file.")
+ LogDir = fs.String("logdir", "", "Path to the log directory.")
Run = fs.String("run", "", "Path to the application to exec.")
MinimumUid = fs.Int64("minuid", uidThreshold, "UIDs cannot be less than this number.")
Remove = fs.Bool("rm", false, "Remove the file trees given as command-line arguments.")
diff --git a/services/mgmt/suidhelper/impl/system.go b/services/mgmt/suidhelper/impl/system.go
index 9df0f9b..2032f60 100644
--- a/services/mgmt/suidhelper/impl/system.go
+++ b/services/mgmt/suidhelper/impl/system.go
@@ -6,13 +6,17 @@
"fmt"
"log"
"os"
+ "path/filepath"
"syscall"
)
// Chown is only availabe on UNIX platforms so this file has a build
// restriction.
func (hw *WorkParameters) Chown() error {
- chown := func(path string) error {
+ chown := func(path string, _ os.FileInfo, inerr error) error {
+ if inerr != nil {
+ return inerr
+ }
if hw.dryrun {
log.Printf("[dryrun] os.Chown(%s, %d, %d)", path, hw.uid, hw.gid)
return nil
@@ -20,9 +24,9 @@
return os.Chown(path, hw.uid, hw.gid)
}
- for _, p := range []string{hw.workspace, hw.stdoutLog, hw.stderrLog} {
+ for _, p := range []string{hw.workspace, hw.logDir} {
// TODO(rjkroege): Ensure that the nodemanager can read log entries.
- if err := chown(p); err != nil {
+ if err := filepath.Walk(p, chown); err != nil {
return fmt.Errorf("os.Chown(%s, %d, %d) failed: %v", p, hw.uid, hw.gid, err)
}
}
diff --git a/services/mgmt/suidhelper/impl/system_test.go b/services/mgmt/suidhelper/impl/system_test.go
new file mode 100644
index 0000000..d8040f2
--- /dev/null
+++ b/services/mgmt/suidhelper/impl/system_test.go
@@ -0,0 +1,70 @@
+package impl
+
+import (
+ "bytes"
+ "io/ioutil"
+ "log"
+ "os"
+ "path"
+ "testing"
+)
+
+func TestChown(t *testing.T) {
+ dir, err := ioutil.TempDir("", "chown_test")
+ if err != nil {
+ t.Fatalf("ioutil.TempDir() failed: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ for _, p := range []string{"a/b/c", "c/d"} {
+ fp := path.Join(dir, p)
+ if err := os.MkdirAll(fp, os.FileMode(0700)); err != nil {
+ t.Fatalf("os.MkdirAll(%s) failed: %v", fp, err)
+ }
+ }
+
+ wp := &WorkParameters{
+ uid: 42,
+ gid: 7,
+ logDir: path.Join(dir, "a"),
+ workspace: path.Join(dir, "c"),
+
+ dryrun: true,
+ }
+
+ // Collect the log entries.
+ b := new(bytes.Buffer)
+ log.SetOutput(b)
+ log.SetFlags(0)
+ defer log.SetOutput(os.Stderr)
+ defer log.SetFlags(log.LstdFlags)
+
+ // Mock-chown the tree.
+ if err := wp.Chown(); err != nil {
+ t.Fatalf("wp.Chown() wrongly failed: %v", err)
+ }
+
+ // Edit the log buffer to remove the invocation dependent output.
+ pb := bytes.TrimSpace(bytes.Replace(b.Bytes(), []byte(dir), []byte("$PATH"), -1))
+
+ cmds := bytes.Split(pb, []byte{'\n'})
+ for i, _ := range cmds {
+ cmds[i] = bytes.TrimSpace(cmds[i])
+ }
+
+ expected := []string{
+ "[dryrun] os.Chown($PATH/c, 42, 7)",
+ "[dryrun] os.Chown($PATH/c/d, 42, 7)",
+ "[dryrun] os.Chown($PATH/a, 42, 7)",
+ "[dryrun] os.Chown($PATH/a/b, 42, 7)",
+ "[dryrun] os.Chown($PATH/a/b/c, 42, 7)",
+ }
+ if got, expected := len(cmds), len(expected); got != expected {
+ t.Fatalf("bad length. got: %d, expected %d", got, expected)
+ }
+ for i, _ := range expected {
+ if expected, got := expected[i], string(cmds[i]); expected != got {
+ t.Fatalf("wp.Chown output %d: got %v, expected %v", i, got, expected)
+ }
+ }
+}