Merge "veyron/tools/mgmt/vsh: introducing vsh, a security-agent shell wrapper"
diff --git a/runtimes/google/ipc/context.go b/runtimes/google/ipc/context.go
index c8d23a3..28dff36 100644
--- a/runtimes/google/ipc/context.go
+++ b/runtimes/google/ipc/context.go
@@ -8,11 +8,13 @@
"veyron.io/veyron/veyron2/context"
)
+const nilRuntimeMessage = "attempting to create a context with a nil runtime"
+
// InternalNewContext creates a new context.T. This function should only
// be called from within the runtime implementation.
func InternalNewContext(runtime veyron2.Runtime) context.T {
if runtime == nil {
- panic("attempting to create a context with a nil runtime")
+ panic(nilRuntimeMessage)
}
return rootContext{runtime}
}
@@ -133,9 +135,6 @@
return
}
-func (c *cancelContext) parent() context.T {
- return c.T
-}
// addChild sets child as a descendant cancellable context. This
// allows us to propagate cancellations through the context tree.
@@ -211,7 +210,7 @@
}
parent = c.parent()
}
- return nil, false
+ return nil, false // Unreachable.
}
func (c *cancelContext) Done() <-chan struct{} { return c.done }
diff --git a/runtimes/google/ipc/context_test.go b/runtimes/google/ipc/context_test.go
index 7b9137c..515ee26 100644
--- a/runtimes/google/ipc/context_test.go
+++ b/runtimes/google/ipc/context_test.go
@@ -49,9 +49,44 @@
}
}
+func TestRootContext(t *testing.T) {
+ r := &runtime.PanicRuntime{}
+ ctx := InternalNewContext(r)
+
+ if got := ctx.Runtime(); got != r {
+ t.Errorf("Expected runtime %v, but found %v", r, got)
+ }
+
+ if got := ctx.Err(); got != nil {
+ t.Errorf("Expected nil error, got: %v", got)
+ }
+
+ defer func() {
+ r := recover()
+ if r != nilRuntimeMessage {
+ t.Errorf("Unexpected recover value: %s", r)
+ }
+ }()
+ InternalNewContext(nil)
+}
+
func TestCancelContext(t *testing.T) {
ctx, cancel := testContext().WithCancel()
testCancel(t, ctx, cancel)
+
+ // Test cancelling a cancel context which is the child
+ // of a cancellable context.
+ parent, _ := testContext().WithCancel()
+ child, cancel := parent.WithCancel()
+ cancel()
+ <-child.Done()
+
+ // Test adding a cancellable child context after the parent is
+ // already cancelled.
+ parent, cancel = testContext().WithCancel()
+ cancel()
+ child, _ = parent.WithCancel()
+ <-child.Done() // The child should have been cancelled right away.
}
func TestMultiLevelCancelContext(t *testing.T) {
@@ -80,11 +115,14 @@
}
func TestCancelContextWithNonStandard(t *testing.T) {
- c0, c0Cancel := testContext().WithCancel()
- c1 := &nonStandardContext{c0}
+ // Test that cancellation flows properly through non-standard intermediates.
+ ctx := testContext()
+ c0 := &nonStandardContext{ctx}
+ c1, c1Cancel := c0.WithCancel()
c2 := &nonStandardContext{c1}
- c3, _ := c2.WithCancel()
- testCancel(t, c3, c0Cancel)
+ c3 := &nonStandardContext{c2}
+ c4, _ := c3.WithCancel()
+ testCancel(t, c4, c1Cancel)
}
func testDeadline(t *testing.T, ctx context.T, start time.Time, desiredTimeout time.Duration) {
@@ -99,12 +137,37 @@
func TestDeadlineContext(t *testing.T) {
cases := []time.Duration{
- 10 * time.Millisecond,
+ 3 * time.Millisecond,
0,
}
+ rootCtx := InternalNewContext(&runtime.PanicRuntime{})
+ cancelCtx, _ := rootCtx.WithCancel()
+ deadlineCtx, _ := rootCtx.WithDeadline(time.Now().Add(time.Hour))
+
for _, desiredTimeout := range cases {
+ // Test all the various ways of getting deadline contexts.
start := time.Now()
- ctx, _ := testContext().WithDeadline(start.Add(desiredTimeout))
+ ctx, _ := rootCtx.WithDeadline(start.Add(desiredTimeout))
+ testDeadline(t, ctx, start, desiredTimeout)
+
+ start = time.Now()
+ ctx, _ = cancelCtx.WithDeadline(start.Add(desiredTimeout))
+ testDeadline(t, ctx, start, desiredTimeout)
+
+ start = time.Now()
+ ctx, _ = deadlineCtx.WithDeadline(start.Add(desiredTimeout))
+ testDeadline(t, ctx, start, desiredTimeout)
+
+ start = time.Now()
+ ctx, _ = rootCtx.WithTimeout(desiredTimeout)
+ testDeadline(t, ctx, start, desiredTimeout)
+
+ start = time.Now()
+ ctx, _ = cancelCtx.WithTimeout(desiredTimeout)
+ testDeadline(t, ctx, start, desiredTimeout)
+
+ start = time.Now()
+ ctx, _ = deadlineCtx.WithTimeout(desiredTimeout)
testDeadline(t, ctx, start, desiredTimeout)
}
diff --git a/tools/mgmt/nminstall b/tools/mgmt/nminstall
index 858ade7..e6d71fd 100755
--- a/tools/mgmt/nminstall
+++ b/tools/mgmt/nminstall
@@ -163,8 +163,24 @@
local -r NM_ROOT="${INSTALL_DIR}/nmroot"
echo "Installing node manager under ${NM_ROOT} ..."
local -r PUBLISH=$(hostname)
- VEYRON_NM_CURRENT="${INSTALL_DIR}/curr" VEYRON_NM_ROOT="${NM_ROOT}" VEYRON_NM_HELPER="${SETUID_SCRIPT}" "${BIN_INSTALL}/noded" --install_self --name="${PUBLISH}"
+ VEYRON_NM_CURRENT="${INSTALL_DIR}/noded.curr" VEYRON_NM_ROOT="${NM_ROOT}" VEYRON_NM_HELPER="${SETUID_SCRIPT}" "${BIN_INSTALL}/noded" --install_self --name="${PUBLISH}"
echo "Node manager installed."
+
+ local -r SECURITY_DIR="${INSTALL_DIR}/security"
+ mkdir -m 700 "${SECURITY_DIR}"
+ local -r PRINCIPAL_DIR="${SECURITY_DIR}/principal"
+ mkdir -m 700 "${PRINCIPAL_DIR}"
+ local -r AGENT_KEY_DIR="${SECURITY_DIR}/keys"
+ mkdir -m 700 "${AGENT_KEY_DIR}"
+
+ # Run node manager under the security agent.
+ echo
+ echo "Running:"
+ echo "VEYRON_CREDENTIALS=\"${PRINCIPAL_DIR}\" \"${BIN_INSTALL}/agentd\" --additional_principals=\"${AGENT_KEY_DIR}\" \"${INSTALL_DIR}/noded.curr\""
+ echo
+ # NOTE: If you update the command below, please also update the command echoed
+ # above to keep the two in sync.
+ VEYRON_CREDENTIALS="${PRINCIPAL_DIR}" "${BIN_INSTALL}/agentd" --additional_principals="${AGENT_KEY_DIR}" "${INSTALL_DIR}/noded.curr"
}
main "$@"
diff --git a/tools/mgmt/nodex/acl_impl.go b/tools/mgmt/nodex/acl_impl.go
new file mode 100644
index 0000000..8348f5b
--- /dev/null
+++ b/tools/mgmt/nodex/acl_impl.go
@@ -0,0 +1,81 @@
+package main
+
+// Commands to get/set ACLs.
+
+import (
+ "fmt"
+ "sort"
+
+ "veyron.io/veyron/veyron2/rt"
+ "veyron.io/veyron/veyron2/services/mgmt/node"
+
+ "veyron.io/veyron/veyron/lib/cmdline"
+)
+
+var cmdGet = &cmdline.Command{
+ Run: runGet,
+ Name: "get",
+ Short: "Get ACLs for the given target.",
+ Long: "Get ACLs for the given target with friendly output. Also see getraw.",
+ ArgsName: "<node manager name>",
+ ArgsLong: `
+<node manager name> can be a Vanadium name for a node manager,
+application installation or instance.`,
+}
+
+type formattedACLEntry struct {
+ blessing string
+ inout string
+ label string
+}
+
+// Code to make formattedACLEntry sorted.
+type byBlessing []formattedACLEntry
+
+func (a byBlessing) Len() int { return len(a) }
+func (a byBlessing) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a byBlessing) Less(i, j int) bool { return a[i].blessing < a[j].blessing }
+
+func runGet(cmd *cmdline.Command, args []string) error {
+
+ if expected, got := 1, len(args); expected != got {
+ return cmd.UsageErrorf("install: incorrect number of arguments, expected %d, got %d", expected, got)
+ }
+
+ vanaName := args[0]
+ objACL, _, err := node.ApplicationClient(vanaName).GetACL(rt.R().NewContext())
+ if err != nil {
+ return fmt.Errorf("GetACL on %s failed: %v", vanaName, err)
+ }
+
+ // TODO(rjkroege): Update for custom labels.
+ output := make([]formattedACLEntry, 0)
+ for k, _ := range objACL.In {
+ output = append(output, formattedACLEntry{string(k), "in", objACL.In[k].String()})
+ }
+ for k, _ := range objACL.NotIn {
+
+ output = append(output, formattedACLEntry{string(k), "nin", objACL.NotIn[k].String()})
+ }
+
+ sort.Sort(byBlessing(output))
+
+ for _, e := range output {
+ fmt.Fprintf(cmd.Stdout(), "%s %s %s\n", e.blessing, e.inout, e.label)
+ }
+ return nil
+}
+
+// TODO(rjkroege): Implement the remaining sub-commands.
+// nodex acl set <target> ([!]<label> <blessing>)...
+
+func aclRoot() *cmdline.Command {
+ return &cmdline.Command{
+ Name: "acl",
+ Short: "Tool for creating associations between Vanadium blessings and a system account",
+ Long: `
+The associate tool facilitates managing blessing to system account associations.
+`,
+ Children: []*cmdline.Command{cmdGet},
+ }
+}
diff --git a/tools/mgmt/nodex/acl_test.go b/tools/mgmt/nodex/acl_test.go
new file mode 100644
index 0000000..fb629b9
--- /dev/null
+++ b/tools/mgmt/nodex/acl_test.go
@@ -0,0 +1,56 @@
+package main
+
+import (
+ "bytes"
+ "reflect"
+ "strings"
+ "testing"
+
+ "veyron.io/veyron/veyron2/naming"
+ "veyron.io/veyron/veyron2/rt"
+ "veyron.io/veyron/veyron2/security"
+)
+
+func TestACLGetCommand(t *testing.T) {
+ runtime := rt.Init()
+ tape := NewTape()
+ server, endpoint, err := startServer(t, runtime, tape)
+ if err != nil {
+ return
+ }
+ defer stopServer(t, server)
+
+ // Setup the command-line.
+ cmd := root()
+ var stdout, stderr bytes.Buffer
+ cmd.Init(nil, &stdout, &stderr)
+ nodeName := naming.JoinAddressName(endpoint.String(), "")
+
+ // Test the 'list' command.
+ tape.SetResponses([]interface{}{GetACLResponse{
+ acl: security.ACL{
+ In: map[security.BlessingPattern]security.LabelSet{
+ "root/self/...": security.AllLabels,
+ "root/other": security.LabelSet(security.ReadLabel),
+ },
+ NotIn: map[string]security.LabelSet{
+ "root/bob/...": security.LabelSet(security.WriteLabel),
+ },
+ },
+ etag: "anEtagForToday",
+ err: nil,
+ },
+ })
+
+ if err := cmd.Execute([]string{"acl", "get", nodeName}); err != nil {
+ t.Fatalf("%v, ouput: %v, error: %v", err)
+ }
+ if expected, got := "root/bob/... nin W\nroot/other in R\nroot/self/... in XRWADM", strings.TrimSpace(stdout.String()); got != expected {
+ t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
+ }
+ if got, expected := tape.Play(), []interface{}{"GetACL"}; !reflect.DeepEqual(expected, got) {
+ t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
+ }
+ tape.Rewind()
+ stdout.Reset()
+}
diff --git a/tools/mgmt/nodex/impl.go b/tools/mgmt/nodex/impl.go
index e4547fc..e062da2 100644
--- a/tools/mgmt/nodex/impl.go
+++ b/tools/mgmt/nodex/impl.go
@@ -107,8 +107,6 @@
Long: `
The nodex tool facilitates interaction with the veyron node manager.
`,
-
- // TODO(rjk): will have to fold these in...
- Children: []*cmdline.Command{cmdInstall, cmdStart, associateRoot(), cmdClaim, cmdStop, cmdSuspend, cmdResume},
+ Children: []*cmdline.Command{cmdInstall, cmdStart, associateRoot(), cmdClaim, cmdStop, cmdSuspend, cmdResume, aclRoot()},
}
}
diff --git a/tools/mgmt/nodex/impl_test.go b/tools/mgmt/nodex/impl_test.go
index f2111a0..031447d 100644
--- a/tools/mgmt/nodex/impl_test.go
+++ b/tools/mgmt/nodex/impl_test.go
@@ -83,21 +83,31 @@
return r.appId, r.err
}
-func (*mockNodeInvoker) Refresh(ipc.ServerContext) error { return nil }
-func (*mockNodeInvoker) Restart(ipc.ServerContext) error { return nil }
-func (*mockNodeInvoker) Resume(ipc.ServerContext) error { return nil }
-func (i *mockNodeInvoker) Revert(call ipc.ServerContext) error { return nil }
-func (*mockNodeInvoker) Start(ipc.ServerContext) ([]string, error) { return []string{}, nil }
-func (*mockNodeInvoker) Stop(ipc.ServerContext, uint32) error { return nil }
-func (*mockNodeInvoker) Suspend(ipc.ServerContext) error { return nil }
-func (*mockNodeInvoker) Uninstall(ipc.ServerContext) error { return nil }
-func (i *mockNodeInvoker) Update(ipc.ServerContext) error { return nil }
-func (*mockNodeInvoker) UpdateTo(ipc.ServerContext, string) error { return nil }
-func (i *mockNodeInvoker) SetACL(ipc.ServerContext, security.ACL, string) error { return nil }
-func (i *mockNodeInvoker) GetACL(ipc.ServerContext) (security.ACL, string, error) {
- return security.ACL{}, "", nil
+func (*mockNodeInvoker) Refresh(ipc.ServerContext) error { return nil }
+func (*mockNodeInvoker) Restart(ipc.ServerContext) error { return nil }
+func (*mockNodeInvoker) Resume(ipc.ServerContext) error { return nil }
+func (i *mockNodeInvoker) Revert(call ipc.ServerContext) error { return nil }
+func (*mockNodeInvoker) Start(ipc.ServerContext) ([]string, error) { return []string{}, nil }
+func (*mockNodeInvoker) Stop(ipc.ServerContext, uint32) error { return nil }
+func (*mockNodeInvoker) Suspend(ipc.ServerContext) error { return nil }
+func (*mockNodeInvoker) Uninstall(ipc.ServerContext) error { return nil }
+func (i *mockNodeInvoker) Update(ipc.ServerContext) error { return nil }
+func (*mockNodeInvoker) UpdateTo(ipc.ServerContext, string) error { return nil }
+
+// Mock ACL getting and setting
+type GetACLResponse struct {
+ acl security.ACL
+ etag string
+ err error
}
+func (mni *mockNodeInvoker) SetACL(ipc.ServerContext, security.ACL, string) error { return nil }
+func (mni *mockNodeInvoker) GetACL(ipc.ServerContext) (security.ACL, string, error) {
+ ir := mni.tape.Record("GetACL")
+ r := ir.(GetACLResponse)
+ return r.acl, r.etag, r.err
+}
+
type dispatcher struct {
tape *Tape
t *testing.T