veyron/lib/modules: adding an option to pass Config to a child process

This CL extends the modules package with the ability to pass a
configuration to the processes spawned by the veyron exec library.

This mechanism is then used to replace the hardcoded server address in
the management runtime component, with values specified in the
configuration.

Change-Id: I3df226d1550bb9194bf240943683313f603f6600
diff --git a/lib/modules/exec.go b/lib/modules/exec.go
index 26c8b61..ab893d8 100644
--- a/lib/modules/exec.go
+++ b/lib/modules/exec.go
@@ -134,7 +134,7 @@
 		return nil, err
 	}
 
-	handle := vexec.NewParentHandle(cmd)
+	handle := vexec.NewParentHandle(cmd, vexec.ConfigOpt{Config: sh.Config})
 	eh.stdout = stdout
 	eh.stderr = stderr
 	eh.stdin = stdin
diff --git a/lib/modules/shell.go b/lib/modules/shell.go
index 1f2a016..7f3c194 100644
--- a/lib/modules/shell.go
+++ b/lib/modules/shell.go
@@ -49,6 +49,7 @@
 	"sync"
 	"time"
 
+	"veyron.io/veyron/veyron/lib/exec"
 	"veyron.io/veyron/veyron/lib/flags/consts"
 	"veyron.io/veyron/veyron2/vlog"
 )
@@ -61,6 +62,7 @@
 	handles      map[Handle]struct{}
 	credDir      string
 	startTimeout time.Duration
+	Config       exec.Config
 }
 
 type commandDesc struct {
@@ -96,6 +98,7 @@
 		cmds:         make(map[string]*commandDesc),
 		handles:      make(map[Handle]struct{}),
 		startTimeout: time.Minute,
+		Config:       exec.NewConfig(),
 	}
 	if flag.Lookup("test.run") != nil && os.Getenv(consts.VeyronCredentials) == "" {
 		if err := sh.CreateAndUseNewCredentials(); err != nil {
diff --git a/lib/signals/signals_test.go b/lib/signals/signals_test.go
index abe2e27..a34c0db 100644
--- a/lib/signals/signals_test.go
+++ b/lib/signals/signals_test.go
@@ -340,7 +340,9 @@
 	configServer, configServiceName, ch := createConfigServer(t)
 	defer configServer.Stop()
 	sh.SetVar(consts.VeyronCredentials, childcreds)
-	sh.SetVar(mgmt.ParentNodeManagerConfigKey, configServiceName)
+	sh.Config.Set(mgmt.ParentNameConfigKey, configServiceName)
+	sh.Config.Set(mgmt.ProtocolConfigKey, "tcp")
+	sh.Config.Set(mgmt.AddressConfigKey, "127.0.0.1:0")
 	h, err := sh.Start("handleDefaults", nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
diff --git a/runtimes/google/rt/mgmt.go b/runtimes/google/rt/mgmt.go
index 103d804..772fdfa 100644
--- a/runtimes/google/rt/mgmt.go
+++ b/runtimes/google/rt/mgmt.go
@@ -25,35 +25,48 @@
 	server       ipc.Server // Serves AppCycle service.
 }
 
-// parentName returns the object name for the Config service on which we should
-// communicate the object name of the app cycle service.  Currently, this can
-// be configured either via env vars or via the exec config passed from parent.
-func parentName() (name string) {
-	name = os.Getenv(mgmt.ParentNodeManagerConfigKey)
-	if len(name) > 0 {
-		return
+func getListenSpec(handle *exec.ChildHandle) (*ipc.ListenSpec, error) {
+	protocol, err := handle.Config.Get(mgmt.ProtocolConfigKey)
+	if err != nil {
+		return nil, err
 	}
-	handle, _ := exec.GetChildHandle()
-	if handle == nil {
-		return
+	if protocol == "" {
+		return nil, fmt.Errorf("%v is not set", mgmt.ProtocolConfigKey)
 	}
-	name, _ = handle.Config.Get(mgmt.ParentNodeManagerConfigKey)
-	return
+
+	address, err := handle.Config.Get(mgmt.AddressConfigKey)
+	if err != nil {
+		return nil, err
+	}
+	if address == "" {
+		return nil, fmt.Errorf("%v is not set", mgmt.AddressConfigKey)
+	}
+	return &ipc.ListenSpec{Protocol: protocol, Address: address}, nil
 }
 
-func (m *mgmtImpl) initMgmt(rt *vrt, listenSpec ipc.ListenSpec) error {
-	m.rt = rt
-	parentName := parentName()
-	if len(parentName) == 0 {
+func (m *mgmtImpl) initMgmt(rt *vrt) error {
+	// Do not initialize the mgmt runtime if the process has not
+	// been started through the veyron exec library by a node
+	// manager.
+	handle, err := exec.GetChildHandle()
+	if err != nil {
 		return nil
 	}
-	var err error
-	if m.server, err = rt.NewServer(); err != nil {
+	parentName, err := handle.Config.Get(mgmt.ParentNameConfigKey)
+	if err != nil {
+		return nil
+	}
+	listenSpec, err := getListenSpec(handle)
+	if err != nil {
 		return err
 	}
-	// TODO(caprita): We should pick the address to listen on from config.
-	var ep naming.Endpoint
-	if ep, err = m.server.Listen(listenSpec); err != nil {
+	m.rt = rt
+	m.server, err = rt.NewServer()
+	if err != nil {
+		return err
+	}
+	ep, err := m.server.Listen(*listenSpec)
+	if err != nil {
 		return err
 	}
 	if err := m.server.Serve("", ipc.LeafDispatcher(appcycle.NewServerAppCycle(m), nil)); err != nil {
diff --git a/runtimes/google/rt/mgmt_test.go b/runtimes/google/rt/mgmt_test.go
index 4eea73d..866585e 100644
--- a/runtimes/google/rt/mgmt_test.go
+++ b/runtimes/google/rt/mgmt_test.go
@@ -291,7 +291,9 @@
 	configServer, configServiceName, ch := createConfigServer(t, r)
 	sh := modules.NewShell(appCmd)
 	sh.SetVar(consts.VeyronCredentials, childcreds)
-	sh.SetVar(mgmt.ParentNodeManagerConfigKey, configServiceName)
+	sh.Config.Set(mgmt.ParentNameConfigKey, configServiceName)
+	sh.Config.Set(mgmt.ProtocolConfigKey, "tcp")
+	sh.Config.Set(mgmt.AddressConfigKey, "127.0.0.1:0")
 	h, err := sh.Start("app", nil)
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
diff --git a/runtimes/google/rt/rt.go b/runtimes/google/rt/rt.go
index f766be3..b8ff3d0 100644
--- a/runtimes/google/rt/rt.go
+++ b/runtimes/google/rt/rt.go
@@ -126,10 +126,7 @@
 		return nil, err
 	}
 
-	// TODO(caprita, cnicolaou): how is this to be configured?
-	// Can it ever be anything other than a localhost/loopback address?
-	listenSpec := ipc.ListenSpec{Protocol: "tcp", Address: "127.0.0.1:0"}
-	if err := rt.mgmt.initMgmt(rt, listenSpec); err != nil {
+	if err := rt.mgmt.initMgmt(rt); err != nil {
 		return nil, err
 	}
 
diff --git a/services/mgmt/node/impl/app_invoker.go b/services/mgmt/node/impl/app_invoker.go
index 4de936a..5832b36 100644
--- a/services/mgmt/node/impl/app_invoker.go
+++ b/services/mgmt/node/impl/app_invoker.go
@@ -503,7 +503,9 @@
 	listener := callbackState.listenFor(mgmt.AppCycleManagerConfigKey)
 	defer listener.cleanup()
 	cfg := vexec.NewConfig()
-	cfg.Set(mgmt.ParentNodeManagerConfigKey, listener.name())
+	cfg.Set(mgmt.ParentNameConfigKey, listener.name())
+	cfg.Set(mgmt.ProtocolConfigKey, "tcp")
+	cfg.Set(mgmt.AddressConfigKey, "127.0.0.1:0")
 	handle := vexec.NewParentHandle(cmd, vexec.ConfigOpt{cfg})
 	defer func() {
 		if handle != nil {
diff --git a/services/mgmt/node/impl/callback.go b/services/mgmt/node/impl/callback.go
index 514e183..7865bc6 100644
--- a/services/mgmt/node/impl/callback.go
+++ b/services/mgmt/node/impl/callback.go
@@ -18,7 +18,7 @@
 	switch err {
 	case nil:
 		// Node manager was started by self-update, notify the parent.
-		callbackName, err := handle.Config.Get(mgmt.ParentNodeManagerConfigKey)
+		callbackName, err := handle.Config.Get(mgmt.ParentNameConfigKey)
 		if err != nil {
 			// Node manager was not started by self-update, return silently.
 			return
@@ -29,8 +29,8 @@
 		}
 		ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
 		defer cancel()
-		if err := nmClient.Set(ctx, mgmt.ChildNodeManagerConfigKey, name); err != nil {
-			vlog.Fatalf("Set(%v, %v) failed: %v", mgmt.ChildNodeManagerConfigKey, name, err)
+		if err := nmClient.Set(ctx, mgmt.ChildNameConfigKey, name); err != nil {
+			vlog.Fatalf("Set(%v, %v) failed: %v", mgmt.ChildNameConfigKey, name, err)
 		}
 	case exec.ErrNoVersion:
 	default:
diff --git a/services/mgmt/node/impl/node_invoker.go b/services/mgmt/node/impl/node_invoker.go
index 1712a0a..7374abd 100644
--- a/services/mgmt/node/impl/node_invoker.go
+++ b/services/mgmt/node/impl/node_invoker.go
@@ -216,11 +216,13 @@
 
 	// Setup up the child process callback.
 	callbackState := i.callback
-	listener := callbackState.listenFor(mgmt.ChildNodeManagerConfigKey)
+	listener := callbackState.listenFor(mgmt.ChildNameConfigKey)
 	defer listener.cleanup()
 	cfg := vexec.NewConfig()
 
-	cfg.Set(mgmt.ParentNodeManagerConfigKey, listener.name())
+	cfg.Set(mgmt.ParentNameConfigKey, listener.name())
+	cfg.Set(mgmt.ProtocolConfigKey, "tcp")
+	cfg.Set(mgmt.AddressConfigKey, "127.0.0.1:0")
 	handle := vexec.NewParentHandle(cmd, vexec.ConfigOpt{cfg})
 	// Start the child process.
 	if err := handle.Start(); err != nil {