Merge "veyron/lib/modules: Make modules parse flags using the veyron2.Init flag parsing mechanism."
diff --git a/services/mgmt/device/deviced/server.go b/services/mgmt/device/deviced/server.go
index 33d7919..7362b43 100644
--- a/services/mgmt/device/deviced/server.go
+++ b/services/mgmt/device/deviced/server.go
@@ -6,11 +6,13 @@
"v.io/lib/cmdline"
+ vexec "v.io/core/veyron/lib/exec"
"v.io/core/veyron/lib/signals"
_ "v.io/core/veyron/profiles/roaming"
"v.io/core/veyron/services/mgmt/device/config"
"v.io/core/veyron/services/mgmt/device/impl"
"v.io/core/veyron2"
+ "v.io/core/veyron2/mgmt"
"v.io/core/veyron2/vlog"
)
@@ -25,6 +27,17 @@
ctx, shutdown := veyron2.Init()
defer shutdown()
+ var testMode bool
+ // If this device manager was started by another device manager, it must
+ // be part of a self update to test that this binary works. In that
+ // case, we need to disable a lot of functionality.
+ if handle, err := vexec.GetChildHandle(); err == nil {
+ if _, err := handle.Config.Get(mgmt.ParentNameConfigKey); err == nil {
+ testMode = true
+ vlog.Infof("TEST MODE")
+ }
+ }
+
server, err := veyron2.NewServer(ctx)
if err != nil {
vlog.Errorf("NewServer() failed: %v", err)
@@ -50,16 +63,20 @@
// implementation detail).
var exitErr error
- dispatcher, err := impl.NewDispatcher(veyron2.GetPrincipal(ctx), configState, func() { exitErr = cmdline.ErrExitCode(*restartExitCode) })
+ dispatcher, err := impl.NewDispatcher(veyron2.GetPrincipal(ctx), configState, testMode, func() { exitErr = cmdline.ErrExitCode(*restartExitCode) })
if err != nil {
vlog.Errorf("Failed to create dispatcher: %v", err)
return err
}
- if err := server.ServeDispatcher(*publishAs, dispatcher); err != nil {
- vlog.Errorf("Serve(%v) failed: %v", *publishAs, err)
+ var publishName string
+ if testMode == false {
+ publishName = *publishAs
+ }
+ if err := server.ServeDispatcher(publishName, dispatcher); err != nil {
+ vlog.Errorf("Serve(%v) failed: %v", publishName, err)
return err
}
- vlog.VI(0).Infof("Device manager published as: %v", *publishAs)
+ vlog.VI(0).Infof("Device manager published as: %v", publishName)
impl.InvokeCallback(ctx, name)
// Wait until shutdown. Ignore duplicate signals (sent by agent and
diff --git a/services/mgmt/device/impl/device_service.go b/services/mgmt/device/impl/device_service.go
index 629a283..0ff8ead 100644
--- a/services/mgmt/device/impl/device_service.go
+++ b/services/mgmt/device/impl/device_service.go
@@ -43,15 +43,16 @@
"os/exec"
"path/filepath"
"reflect"
+ "strconv"
"strings"
"sync"
- "time"
"v.io/core/veyron2"
"v.io/core/veyron2/context"
"v.io/core/veyron2/ipc"
"v.io/core/veyron2/mgmt"
"v.io/core/veyron2/naming"
+ "v.io/core/veyron2/security"
"v.io/core/veyron2/services/mgmt/application"
"v.io/core/veyron2/services/mgmt/binary"
"v.io/core/veyron2/services/mgmt/device"
@@ -60,7 +61,9 @@
"v.io/core/veyron2/vlog"
vexec "v.io/core/veyron/lib/exec"
+ "v.io/core/veyron/lib/flags/consts"
"v.io/core/veyron/lib/netstate"
+ vsecurity "v.io/core/veyron/security"
"v.io/core/veyron/services/mgmt/device/config"
"v.io/core/veyron/services/mgmt/profile"
)
@@ -102,6 +105,7 @@
config *config.State
disp *dispatcher
uat BlessingSystemAssociationStore
+ securityAgent *securityAgentState
}
// managerInfo holds state about a running device manager.
@@ -216,6 +220,7 @@
func (s *deviceService) revertDeviceManager(ctx *context.T) error {
if err := updateLink(s.config.Previous, s.config.CurrentLink); err != nil {
+ vlog.Errorf("updateLink failed: %v", err)
return err
}
if s.restartHandler != nil {
@@ -278,6 +283,61 @@
cfg.Set(mgmt.ParentNameConfigKey, listener.name())
cfg.Set(mgmt.ProtocolConfigKey, "tcp")
cfg.Set(mgmt.AddressConfigKey, "127.0.0.1:0")
+
+ var p security.Principal
+ var agentHandle []byte
+ if s.securityAgent != nil {
+ // TODO(rthellend): Cleanup principal
+ handle, conn, err := s.securityAgent.keyMgrAgent.NewPrincipal(ctx, false)
+ if err != nil {
+ vlog.Errorf("NewPrincipal() failed %v", err)
+ return verror2.Make(ErrOperationFailed, nil)
+ }
+ agentHandle = handle
+ var cancel func()
+ if p, cancel, err = agentPrincipal(ctx, conn); err != nil {
+ vlog.Errorf("agentPrincipal failed: %v", err)
+ return verror2.Make(ErrOperationFailed, nil)
+ }
+ defer cancel()
+
+ } else {
+ credentialsDir := filepath.Join(workspace, "credentials")
+ var err error
+ if p, err = vsecurity.CreatePersistentPrincipal(credentialsDir, nil); err != nil {
+ vlog.Errorf("CreatePersistentPrincipal(%v, nil) failed: %v", credentialsDir, err)
+ return verror2.Make(ErrOperationFailed, nil)
+ }
+ cmd.Env = append(cmd.Env, consts.VeyronCredentials+"="+credentialsDir)
+ }
+ dmPrincipal := veyron2.GetPrincipal(ctx)
+ dmBlessings, err := dmPrincipal.Bless(p.PublicKey(), dmPrincipal.BlessingStore().Default(), "testdm", security.UnconstrainedUse())
+ if err := p.BlessingStore().SetDefault(dmBlessings); err != nil {
+ vlog.Errorf("BlessingStore.SetDefault() failed: %v", err)
+ return verror2.Make(ErrOperationFailed, nil)
+ }
+ if _, err := p.BlessingStore().Set(dmBlessings, security.AllPrincipals); err != nil {
+ vlog.Errorf("BlessingStore.Set() failed: %v", err)
+ return verror2.Make(ErrOperationFailed, nil)
+ }
+ if err := p.AddToRoots(dmBlessings); err != nil {
+ vlog.Errorf("AddToRoots() failed: %v", err)
+ return verror2.Make(ErrOperationFailed, nil)
+ }
+
+ if s.securityAgent != nil {
+ file, err := s.securityAgent.keyMgrAgent.NewConnection(agentHandle)
+ if err != nil {
+ vlog.Errorf("NewConnection(%v) failed: %v", agentHandle, err)
+ return err
+ }
+ defer file.Close()
+
+ fd := len(cmd.ExtraFiles) + vexec.FileOffset
+ cmd.ExtraFiles = append(cmd.ExtraFiles, file)
+ cfg.Set(mgmt.SecurityAgentFDConfigKey, strconv.Itoa(fd))
+ }
+
handle := vexec.NewParentHandle(cmd, vexec.ConfigOpt{cfg})
// Start the child process.
if err := handle.Start(); err != nil {
@@ -296,35 +356,14 @@
}
childName, err := listener.waitForValue(childReadyTimeout)
if err != nil {
+ vlog.Errorf("waitForValue(%v) failed: %v", childReadyTimeout, err)
return verror2.Make(ErrOperationFailed, ctx)
}
- // Check that invoking Revert() succeeds.
+ // Check that invoking Stop() succeeds.
childName = naming.Join(childName, "device")
dmClient := device.DeviceClient(childName)
- linkOld, pathOld, err := s.getCurrentFileInfo()
- if err != nil {
- return verror2.Make(ErrOperationFailed, ctx)
- }
- // Since the resolution of mtime for files is seconds, the test sleeps
- // for a second to make sure it can check whether the current symlink is
- // updated.
- time.Sleep(time.Second)
- if err := dmClient.Revert(ctx); err != nil {
- return verror2.Make(ErrOperationFailed, ctx)
- }
- linkNew, pathNew, err := s.getCurrentFileInfo()
- if err != nil {
- return verror2.Make(ErrOperationFailed, ctx)
- }
- // Check that the new device manager updated the current symbolic link.
- if !linkOld.ModTime().Before(linkNew.ModTime()) {
- vlog.Errorf("New device manager test failed")
- return verror2.Make(ErrOperationFailed, ctx)
- }
- // Ensure that the current symbolic link points to the same script.
- if pathNew != pathOld {
- updateLink(pathOld, s.config.CurrentLink)
- vlog.Errorf("New device manager test failed")
+ if err := dmClient.Stop(ctx, 0); err != nil {
+ vlog.Errorf("Stop() failed: %v", err)
return verror2.Make(ErrOperationFailed, ctx)
}
if err := handle.Wait(childWaitTimeout); err != nil {
@@ -432,11 +471,10 @@
return err
}
- // TODO(rthellend): testDeviceManager always fails due to https://github.com/veyron/release-issues/issues/714
- // Uncomment when the bug is fixed.
- //if err := s.testDeviceManager(ctx, workspace, envelope); err != nil {
- // return err
- //}
+ if err := s.testDeviceManager(ctx, workspace, envelope); err != nil {
+ vlog.Errorf("testDeviceManager failed: %v", err)
+ return err
+ }
if err := updateLink(filepath.Join(workspace, "deviced.sh"), s.config.CurrentLink); err != nil {
return err
@@ -470,10 +508,12 @@
func (s *deviceService) Revert(call ipc.ServerContext) error {
if s.config.Previous == "" {
+ vlog.Errorf("Revert failed: no previous version")
return verror2.Make(ErrUpdateNoOp, call.Context())
}
updatingState := s.updating
if updatingState.testAndSetUpdating() {
+ vlog.Errorf("Revert failed: already in progress")
return verror2.Make(ErrOperationInProgress, call.Context())
}
err := s.revertDeviceManager(call.Context())
diff --git a/services/mgmt/device/impl/dispatcher.go b/services/mgmt/device/impl/dispatcher.go
index 5632858..7dc317e 100644
--- a/services/mgmt/device/impl/dispatcher.go
+++ b/services/mgmt/device/impl/dispatcher.go
@@ -34,6 +34,7 @@
updating *updatingState
securityAgent *securityAgentState
restartHandler func()
+ testMode bool
}
// dispatcher holds the state of the device manager dispatcher.
@@ -72,7 +73,7 @@
)
// NewDispatcher is the device manager dispatcher factory.
-func NewDispatcher(principal security.Principal, config *config.State, restartHandler func()) (*dispatcher, error) {
+func NewDispatcher(principal security.Principal, config *config.State, testMode bool, restartHandler func()) (ipc.Dispatcher, error) {
if err := config.Validate(); err != nil {
return nil, fmt.Errorf("invalid config %v: %v", config, err)
}
@@ -95,6 +96,7 @@
callback: newCallbackState(config.Name),
updating: newUpdatingState(),
restartHandler: restartHandler,
+ testMode: testMode,
},
config: config,
uat: uat,
@@ -121,6 +123,9 @@
}
}
}
+ if testMode {
+ return &testModeDispatcher{d}, nil
+ }
return d, nil
}
@@ -166,8 +171,17 @@
// TODO(rjkroege): Consider refactoring authorizer implementations to
// be shareable with other components.
-func newAuthorizer(principal security.Principal, dir string, locks *acls.Locks) (security.Authorizer, error) {
- rootTam, _, err := locks.GetPathACL(principal, dir)
+func (d *dispatcher) newAuthorizer() (security.Authorizer, error) {
+ if d.internal.testMode {
+ // In test mode, the device manager will not be able to read
+ // the ACLs, because they were signed with the key of the real
+ // device manager. It's not a problem because the
+ // testModeDispatcher overrides the authorizer anyway.
+ return nil, nil
+ }
+
+ dir := d.getACLDir()
+ rootTam, _, err := d.locks.GetPathACL(d.principal, dir)
if err != nil && os.IsNotExist(err) {
vlog.VI(1).Infof("GetPathACL(%s) failed: %v", dir, err)
@@ -194,7 +208,8 @@
i--
}
}
- auth, err := newAuthorizer(d.principal, d.getACLDir(), d.locks)
+
+ auth, err := d.newAuthorizer()
if err != nil {
return nil, nil, err
}
@@ -214,6 +229,7 @@
config: d.config,
disp: d,
uat: d.uat,
+ securityAgent: d.internal.securityAgent,
})
return receiver, auth, nil
case appsSuffix:
@@ -252,9 +268,6 @@
return invoker, auth, nil
}
}
- if err != nil {
- return nil, nil, err
- }
receiver := device.ApplicationServer(&appService{
callback: d.internal.callback,
config: d.config,
@@ -289,6 +302,27 @@
}
}
+// testModeDispatcher is a wrapper around the real dispatcher. It returns the
+// exact same object as the real dispatcher, but the authorizer only allows
+// calls to "device".Stop().
+type testModeDispatcher struct {
+ realDispatcher ipc.Dispatcher
+}
+
+func (d *testModeDispatcher) Lookup(suffix string) (interface{}, security.Authorizer, error) {
+ obj, _, err := d.realDispatcher.Lookup(suffix)
+ return obj, d, err
+}
+
+func (testModeDispatcher) Authorize(ctx security.Context) error {
+ if ctx.Suffix() == deviceSuffix && ctx.Method() == "Stop" {
+ vlog.Infof("testModeDispatcher.Authorize: Allow %q.%s()", ctx.Suffix(), ctx.Method())
+ return nil
+ }
+ vlog.Infof("testModeDispatcher.Authorize: Reject %q.%s()", ctx.Suffix(), ctx.Method())
+ return verror.Make(ErrInvalidSuffix, nil)
+}
+
func newAppSpecificAuthorizer(sec security.Authorizer, config *config.State, suffix []string) (security.Authorizer, error) {
// TODO(rjkroege): This does not support <appname>.Start() to start all
// instances. Correct this.
diff --git a/services/mgmt/device/impl/impl_test.go b/services/mgmt/device/impl/impl_test.go
index 9a7e600..79957c6 100644
--- a/services/mgmt/device/impl/impl_test.go
+++ b/services/mgmt/device/impl/impl_test.go
@@ -164,11 +164,13 @@
// script prepared by a previous version of the device manager.
if len(args) > 0 {
if want, got := 4, len(args); want != got {
- vlog.Fatalf("expected %d additional arguments, got %d instead", want, got)
+ vlog.Fatalf("expected %d additional arguments, got %d instead: %q", want, got, args)
}
configState.Root, configState.Helper, configState.Origin, configState.CurrentLink = args[0], args[1], args[2], args[3]
}
- dispatcher, err := impl.NewDispatcher(veyron2.GetPrincipal(ctx), configState, func() { fmt.Println("restart handler") })
+ blessings := fmt.Sprint(veyron2.GetPrincipal(ctx).BlessingStore().Default())
+ testMode := strings.HasSuffix(blessings, "/testdm")
+ dispatcher, err := impl.NewDispatcher(veyron2.GetPrincipal(ctx), configState, testMode, func() { fmt.Println("restart handler") })
if err != nil {
vlog.Fatalf("Failed to create device manager dispatcher: %v", err)
}
@@ -184,7 +186,7 @@
if val, present := env["PAUSE_BEFORE_STOP"]; present && val == "1" {
modules.WaitForEOF(stdin)
}
- if dispatcher.Leaking() {
+ if impl.DispatcherLeaking(dispatcher) {
vlog.Fatalf("device manager leaking resources")
}
return nil
@@ -366,9 +368,7 @@
// Set up a second version of the device manager. The information in the
// envelope will be used by the device manager to stage the next
// version.
- crDir, crEnv := mgmttest.CredentialsForChild(ctx, "child")
- defer os.RemoveAll(crDir)
- *envelope = envelopeFromShell(sh, crEnv, deviceManagerCmd, application.DeviceManagerTitle, "v2DM")
+ *envelope = envelopeFromShell(sh, nil, deviceManagerCmd, application.DeviceManagerTitle, "v2DM")
updateDevice(t, ctx, "factoryDM")
// Current link should have been updated to point to v2.
@@ -410,9 +410,7 @@
}
// Create a third version of the device manager and issue an update.
- crDir, crEnv = mgmttest.CredentialsForChild(ctx, "child")
- defer os.RemoveAll(crDir)
- *envelope = envelopeFromShell(sh, crEnv, deviceManagerCmd, application.DeviceManagerTitle, "v3DM")
+ *envelope = envelopeFromShell(sh, nil, deviceManagerCmd, application.DeviceManagerTitle, "v3DM")
updateDevice(t, ctx, "v2DM")
scriptPathV3 := evalLink()
diff --git a/services/mgmt/device/impl/only_for_test.go b/services/mgmt/device/impl/only_for_test.go
index 082fc25..6f92afc 100644
--- a/services/mgmt/device/impl/only_for_test.go
+++ b/services/mgmt/device/impl/only_for_test.go
@@ -2,9 +2,11 @@
import (
"flag"
+ "fmt"
"os"
"path/filepath"
+ "v.io/core/veyron2/ipc"
"v.io/core/veyron2/services/mgmt/device"
"v.io/core/veyron2/vlog"
)
@@ -20,8 +22,15 @@
return len(c.channels) > 0
}
-func (d *dispatcher) Leaking() bool {
- return d.internal.callback.leaking()
+func DispatcherLeaking(d ipc.Dispatcher) bool {
+ switch obj := d.(type) {
+ case *dispatcher:
+ return obj.internal.callback.leaking()
+ case *testModeDispatcher:
+ return obj.realDispatcher.(*dispatcher).internal.callback.leaking()
+ default:
+ panic(fmt.Sprintf("unexpected type: %T", d))
+ }
}
func init() {
diff --git a/services/mgmt/device/impl/util.go b/services/mgmt/device/impl/util.go
index 596ef71..54d90d2 100644
--- a/services/mgmt/device/impl/util.go
+++ b/services/mgmt/device/impl/util.go
@@ -29,7 +29,7 @@
vlog.Errorf("Download(%v) failed: %v", name, err)
return verror2.Make(ErrOperationFailed, nil)
}
- path, perm := filepath.Join(workspace, fileName), os.FileMode(755)
+ path, perm := filepath.Join(workspace, fileName), os.FileMode(0755)
if err := ioutil.WriteFile(path, data, perm); err != nil {
vlog.Errorf("WriteFile(%v, %v) failed: %v", path, perm, err)
return verror2.Make(ErrOperationFailed, nil)
diff --git a/tools/mgmt/test.sh b/tools/mgmt/test.sh
index 7a28463..7cf1f9e 100755
--- a/tools/mgmt/test.sh
+++ b/tools/mgmt/test.sh
@@ -116,14 +116,15 @@
local -r APPLICATIOND_NAME="applicationd"
local -r DEVICED_APP_NAME="${APPLICATIOND_NAME}/deviced/test"
- BIN_STAGING_DIR=$(shell::tmp_dir)
+ BIN_STAGING_DIR="${WORKDIR}/bin"
+ mkdir -p "${BIN_STAGING_DIR}"
cp "${AGENTD_BIN}" "${SUIDHELPER_BIN}" "${INITHELPER_BIN}" "${DEVICEMANAGER_BIN}" "${BIN_STAGING_DIR}"
shell_test::setup_server_test
# Install and start device manager.
- DM_INSTALL_DIR=$(shell::tmp_dir)
+ DM_INSTALL_DIR="${WORKDIR}/dm"
- export VANADIUM_DEVICE_DIR="${DM_INSTALL_DIR}/dm"
+ export VANADIUM_DEVICE_DIR="${DM_INSTALL_DIR}"
if [[ "${WITH_SUID}" == "--with_suid" ]]; then
"${DEVICE_SCRIPT}" install "${BIN_STAGING_DIR}" --origin="${DEVICED_APP_NAME}" -- --veyron.tcp.address=127.0.0.1:0
@@ -170,7 +171,7 @@
# the device ("alice/myworkstation") can talk to it.
local -r BINARYD_NAME="binaryd"
shell_test::start_server "${VRUN}" --name=myworkstation/binaryd "${BINARYD_BIN}" --name="${BINARYD_NAME}" \
- --root_dir="$(shell::tmp_dir)/binstore" --veyron.tcp.address=127.0.0.1:0 --http=127.0.0.1:0 \
+ --root_dir="${WORKDIR}/binstore" --veyron.tcp.address=127.0.0.1:0 --http=127.0.0.1:0 \
|| shell_test::fail "line ${LINENO} failed to start binaryd"
# Upload a binary to the binary server. The binary we upload is binaryd
@@ -185,8 +186,9 @@
# Start an application server under the blessing "alice/myworkstation/applicationd" so that
# the device ("alice/myworkstation") can talk to it.
+ mkdir -p "${WORKDIR}/appstore"
shell_test::start_server "${VRUN}" --name=myworkstation/applicationd "${APPLICATIOND_BIN}" --name="${APPLICATIOND_NAME}" \
- --store="$(shell::tmp_dir)" --veyron.tcp.address=127.0.0.1:0 \
+ --store="${WORKDIR}/appstore" --veyron.tcp.address=127.0.0.1:0 \
|| shell_test::fail "line ${LINENO} failed to start applicationd"
# Upload an envelope for our test app.