veyron2: veyron2.Init function for new API.
The veyron2.Init function will do the following.
(1) Construct an empty context.
(2) Calls a profileInitFunc that returns a RuntimeX instace and an initialized context.
(3) Returns the *context.T and a context.CancelFunc.
Change-Id: I4130499bba7955c57291654ca1aa7504bdb78a2d
diff --git a/runtimes/google/rt/mgmt.go b/runtimes/google/rt/mgmt.go
index e617410..6d3b521 100644
--- a/runtimes/google/rt/mgmt.go
+++ b/runtimes/google/rt/mgmt.go
@@ -1,7 +1,6 @@
package rt
import (
- "fmt"
"time"
"v.io/core/veyron2"
@@ -58,25 +57,6 @@
return server, nil
}
-func getListenSpec(handle *exec.ChildHandle) (*ipc.ListenSpec, error) {
- protocol, err := handle.Config.Get(mgmt.ProtocolConfigKey)
- if err != nil {
- return nil, err
- }
- if protocol == "" {
- return nil, fmt.Errorf("%v is not set", mgmt.ProtocolConfigKey)
- }
-
- 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{Addrs: ipc.ListenAddrs{{protocol, address}}}, nil
-}
-
func (rt *vrt) callbackToParent(parentName, myName string) error {
ctx, _ := context.WithTimeout(rt.NewContext(), 10*time.Second)
call, err := rt.Client().StartCall(ctx, parentName, "Set", []interface{}{mgmt.AppCycleManagerConfigKey, myName})
diff --git a/runtimes/google/rt/mgmtx.go b/runtimes/google/rt/mgmtx.go
new file mode 100644
index 0000000..cef0cb7
--- /dev/null
+++ b/runtimes/google/rt/mgmtx.go
@@ -0,0 +1,97 @@
+package rt
+
+import (
+ "fmt"
+ "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/options"
+
+ "v.io/core/veyron/lib/exec"
+)
+
+func initMgmt(ctx *context.T, appCycle veyron2.AppCycle, handle *exec.ChildHandle) error {
+ // Do not initialize the mgmt runtime if the process has not
+ // been started through the veyron exec library by a device
+ // manager.
+ if handle == nil {
+ return nil
+ }
+ parentName, err := handle.Config.Get(mgmt.ParentNameConfigKey)
+ if err != nil {
+ return nil
+ }
+ listenSpec, err := getListenSpec(handle)
+ if err != nil {
+ return err
+ }
+ var serverOpts []ipc.ServerOpt
+ parentPeerPattern, err := handle.Config.Get(mgmt.ParentBlessingConfigKey)
+ if err == nil && parentPeerPattern != "" {
+ // Grab the blessing from our blessing store that the parent
+ // told us to use so they can talk to us.
+ serverBlessing := veyron2.GetPrincipal(ctx).BlessingStore().ForPeer(parentPeerPattern)
+ serverOpts = append(serverOpts, options.ServerBlessings{serverBlessing})
+ }
+ server, err := veyron2.NewServer(ctx, serverOpts...)
+ if err != nil {
+ return err
+ }
+ eps, err := server.Listen(*listenSpec)
+ if err != nil {
+ return err
+ }
+ if err := server.Serve("", appCycle.Remote(), nil); err != nil {
+ server.Stop()
+ return err
+ }
+ err = callbackToParent(ctx, parentName, naming.JoinAddressName(eps[0].String(), ""))
+ if err != nil {
+ server.Stop()
+ return err
+ }
+
+ if done := ctx.Done(); done != nil {
+ go func() {
+ <-done
+ server.Stop()
+ }()
+ }
+
+ return nil
+}
+
+func getListenSpec(handle *exec.ChildHandle) (*ipc.ListenSpec, error) {
+ protocol, err := handle.Config.Get(mgmt.ProtocolConfigKey)
+ if err != nil {
+ return nil, err
+ }
+ if protocol == "" {
+ return nil, fmt.Errorf("%v is not set", mgmt.ProtocolConfigKey)
+ }
+
+ 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{Addrs: ipc.ListenAddrs{{protocol, address}}}, nil
+}
+
+func callbackToParent(ctx *context.T, parentName, myName string) error {
+ ctx, _ = context.WithTimeout(ctx, 10*time.Second)
+ call, err := veyron2.GetClient(ctx).StartCall(ctx, parentName, "Set", []interface{}{mgmt.AppCycleManagerConfigKey, myName})
+ if err != nil {
+ return err
+ }
+ if ierr := call.Finish(&err); ierr != nil {
+ return ierr
+ }
+ return err
+}
diff --git a/runtimes/google/rt/rt.go b/runtimes/google/rt/rt.go
index f38deb6..77f1258 100644
--- a/runtimes/google/rt/rt.go
+++ b/runtimes/google/rt/rt.go
@@ -1,7 +1,6 @@
package rt
import (
- "flag"
"fmt"
"os"
"path/filepath"
@@ -55,12 +54,8 @@
var _ veyron2.Runtime = (*vrt)(nil)
-var flagsOnce sync.Once
-var runtimeFlags *flags.Flags
-
func init() {
rt.RegisterRuntime(veyron2.GoogleRuntimeName, New)
- runtimeFlags = flags.CreateAndRegister(flag.CommandLine, flags.Runtime)
}
// Implements veyron2/rt.New
diff --git a/runtimes/google/rt/rt_test.go b/runtimes/google/rt/rt_test.go
index c8e803d..44cf67f 100644
--- a/runtimes/google/rt/rt_test.go
+++ b/runtimes/google/rt/rt_test.go
@@ -3,7 +3,6 @@
import (
"fmt"
"io"
- "io/ioutil"
"os"
"reflect"
"regexp"
@@ -13,7 +12,6 @@
"v.io/core/veyron2"
"v.io/core/veyron2/options"
"v.io/core/veyron2/rt"
- "v.io/core/veyron2/security"
"v.io/core/veyron2/vlog"
"v.io/core/veyron/lib/expect"
@@ -104,33 +102,6 @@
h.Shutdown(os.Stderr, os.Stderr)
}
-func validatePrincipal(p security.Principal) error {
- if p == nil {
- return fmt.Errorf("nil principal")
- }
- blessings := p.BlessingStore().Default()
- if blessings == nil {
- return fmt.Errorf("rt.Principal().BlessingStore().Default() returned nil")
- }
- ctx := security.NewContext(&security.ContextParams{LocalPrincipal: p})
- if n := len(blessings.ForContext(ctx)); n != 1 {
- fmt.Errorf("rt.Principal().BlessingStore().Default() returned Blessing %v with %d recognized blessings, want exactly one recognized blessing", blessings, n)
- }
- return nil
-}
-
-func defaultBlessing(p security.Principal) string {
- return p.BlessingStore().Default().ForContext(security.NewContext(&security.ContextParams{LocalPrincipal: p}))[0]
-}
-
-func tmpDir(t *testing.T) string {
- dir, err := ioutil.TempDir("", "rt_test_dir")
- if err != nil {
- t.Fatalf("unexpected error: %s", err)
- }
- return dir
-}
-
func principal(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
r, err := rt.New()
if err != nil {
@@ -173,16 +144,6 @@
return nil
}
-func createCredentialsInDir(t *testing.T, dir string, blessing string) {
- principal, err := vsecurity.CreatePersistentPrincipal(dir, nil)
- if err != nil {
- t.Fatalf("unexpected error: %s", err)
- }
- if err := vsecurity.InitDefaultBlessings(principal, blessing); err != nil {
- t.Fatalf("unexpected error: %s", err)
- }
-}
-
func TestPrincipalInheritance(t *testing.T) {
sh, err := modules.NewShell(nil)
if err != nil {
diff --git a/runtimes/google/rt/rtx_test.go b/runtimes/google/rt/rtx_test.go
new file mode 100644
index 0000000..212075d
--- /dev/null
+++ b/runtimes/google/rt/rtx_test.go
@@ -0,0 +1,283 @@
+package rt_test
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "regexp"
+ "testing"
+ "time"
+
+ "v.io/core/veyron2"
+ "v.io/core/veyron2/context"
+ "v.io/core/veyron2/security"
+ "v.io/core/veyron2/vlog"
+
+ "v.io/core/veyron/lib/expect"
+ "v.io/core/veyron/lib/flags/consts"
+ "v.io/core/veyron/lib/modules"
+ "v.io/core/veyron/lib/testutil"
+ "v.io/core/veyron/runtimes/google/rt"
+ vsecurity "v.io/core/veyron/security"
+)
+
+func init() {
+ testutil.Init()
+ modules.RegisterChild("childX", "", childX)
+ modules.RegisterChild("principalX", "", principalX)
+ modules.RegisterChild("runnerX", "", runnerX)
+}
+
+// initForTest creates a context for use in a test.
+func initForTest() (*context.T, context.CancelFunc) {
+ r := &rt.RuntimeX{}
+ var ctx *context.T
+ var cancel context.CancelFunc
+ var err error
+ ctx, cancel = context.WithCancel(ctx)
+ ctx, err = r.Init(ctx, nil)
+ if err != nil {
+ panic(err)
+ }
+ return ctx, cancel
+}
+
+func TestHelperProcessX(t *testing.T) {
+ modules.DispatchInTest()
+}
+
+func TestInitX(t *testing.T) {
+ ctx, cancel := initForTest()
+ defer cancel()
+
+ l := veyron2.GetLogger(ctx)
+ fmt.Println(l)
+ args := fmt.Sprintf("%s", l)
+ expected := regexp.MustCompile("name=veyron logdirs=\\[/tmp\\] logtostderr=true|false alsologtostderr=false|true max_stack_buf_size=4292608 v=[0-9] stderrthreshold=2 vmodule= log_backtrace_at=:0")
+ if !expected.MatchString(args) {
+ t.Errorf("unexpected default args: %s", args)
+ }
+ p := veyron2.GetPrincipal(ctx)
+ if p == nil {
+ t.Fatalf("A new principal should have been created")
+ }
+ if p.BlessingStore() == nil {
+ t.Fatalf("The principal must have a BlessingStore")
+ }
+ if p.BlessingStore().Default() == nil {
+ t.Errorf("Principal().BlessingStore().Default() should not be nil")
+ }
+ if p.BlessingStore().ForPeer() == nil {
+ t.Errorf("Principal().BlessingStore().ForPeer() should not be nil")
+ }
+}
+
+func childX(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+ ctx, cancel := veyron2.Init()
+ defer cancel()
+
+ logger := veyron2.GetLogger(ctx)
+ vlog.Infof("%s\n", logger)
+ fmt.Fprintf(stdout, "%s\n", logger)
+ modules.WaitForEOF(stdin)
+ fmt.Fprintf(stdout, "done\n")
+ return nil
+}
+
+func TestInitArgsX(t *testing.T) {
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ defer sh.Cleanup(os.Stderr, os.Stderr)
+ h, err := sh.Start("child", nil, "--logtostderr=true", "--vmodule=*=3", "--", "foobar")
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ s := expect.NewSession(t, h.Stdout(), time.Minute)
+ s.Expect(fmt.Sprintf("name=veyron "+
+ "logdirs=[%s] "+
+ "logtostderr=true "+
+ "alsologtostderr=true "+
+ "max_stack_buf_size=4292608 "+
+ "v=0 "+
+ "stderrthreshold=2 "+
+ "vmodule=*=3 "+
+ "log_backtrace_at=:0",
+ os.TempDir()))
+ h.CloseStdin()
+ s.Expect("done")
+ s.ExpectEOF()
+ h.Shutdown(os.Stderr, os.Stderr)
+}
+
+func validatePrincipal(p security.Principal) error {
+ if p == nil {
+ return fmt.Errorf("nil principal")
+ }
+ blessings := p.BlessingStore().Default()
+ if blessings == nil {
+ return fmt.Errorf("rt.Principal().BlessingStore().Default() returned nil")
+ }
+ ctx := security.NewContext(&security.ContextParams{LocalPrincipal: p})
+ if n := len(blessings.ForContext(ctx)); n != 1 {
+ fmt.Errorf("rt.Principal().BlessingStore().Default() returned Blessing %v with %d recognized blessings, want exactly one recognized blessing", blessings, n)
+ }
+ return nil
+}
+
+func defaultBlessing(p security.Principal) string {
+ return p.BlessingStore().Default().ForContext(security.NewContext(&security.ContextParams{LocalPrincipal: p}))[0]
+}
+
+func tmpDir(t *testing.T) string {
+ dir, err := ioutil.TempDir("", "rt_test_dir")
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ return dir
+}
+
+func principalX(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+ ctx, cancel := veyron2.Init()
+ defer cancel()
+
+ p := veyron2.GetPrincipal(ctx)
+ if err := validatePrincipal(p); err != nil {
+ return err
+ }
+ fmt.Fprintf(stdout, "DEFAULT_BLESSING=%s\n", defaultBlessing(p))
+ return nil
+}
+
+// Runner runs a principal as a subprocess and reports back with its
+// own security info and it's childs.
+func runnerX(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+ ctx, cancel := veyron2.Init()
+ defer cancel()
+
+ p := veyron2.GetPrincipal(ctx)
+ if err := validatePrincipal(p); err != nil {
+ return err
+ }
+ fmt.Fprintf(stdout, "RUNNER_DEFAULT_BLESSING=%v\n", defaultBlessing(p))
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ return err
+ }
+ if _, err := sh.Start("principal", nil, args[1:]...); err != nil {
+ return err
+ }
+ // Cleanup copies the output of sh to these Writers.
+ sh.Cleanup(stdout, stderr)
+ return nil
+}
+
+func createCredentialsInDir(t *testing.T, dir string, blessing string) {
+ principal, err := vsecurity.CreatePersistentPrincipal(dir, nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ if err := vsecurity.InitDefaultBlessings(principal, blessing); err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+}
+
+func TestPrincipalInheritanceX(t *testing.T) {
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ defer func() {
+ sh.Cleanup(os.Stdout, os.Stderr)
+ }()
+
+ // Test that the child inherits from the parent's credentials correctly.
+ // The running test process may or may not have a credentials directory set
+ // up so we have to use a 'runner' process to ensure the correct setup.
+ cdir := tmpDir(t)
+ defer os.RemoveAll(cdir)
+
+ createCredentialsInDir(t, cdir, "test")
+
+ // directory supplied by the environment.
+ credEnv := []string{consts.VeyronCredentials + "=" + cdir}
+
+ h, err := sh.Start("runner", credEnv)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+
+ s := expect.NewSession(t, h.Stdout(), time.Minute)
+ runnerBlessing := s.ExpectVar("RUNNER_DEFAULT_BLESSING")
+ principalBlessing := s.ExpectVar("DEFAULT_BLESSING")
+ if err := s.Error(); err != nil {
+ t.Fatalf("failed to read input from children: %s", err)
+ }
+ h.Shutdown(os.Stdout, os.Stderr)
+
+ wantRunnerBlessing := "test"
+ wantPrincipalBlessing := "test/child"
+ if runnerBlessing != wantRunnerBlessing || principalBlessing != wantPrincipalBlessing {
+ t.Fatalf("unexpected default blessing: got runner %s, principal %s, want runner %s, principal %s", runnerBlessing, principalBlessing, wantRunnerBlessing, wantPrincipalBlessing)
+ }
+
+}
+
+func TestPrincipalInitX(t *testing.T) {
+ // Collect the process' public key and error status
+ collect := func(sh *modules.Shell, env []string, args ...string) string {
+ h, err := sh.Start("principal", env, args...)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ s := expect.NewSession(t, h.Stdout(), time.Minute)
+ s.SetVerbosity(testing.Verbose())
+ return s.ExpectVar("DEFAULT_BLESSING")
+ }
+
+ // A credentials directory may, or may, not have been already specified.
+ // Either way, we want to use our own, so we set it aside and use our own.
+ origCredentialsDir := os.Getenv(consts.VeyronCredentials)
+ defer os.Setenv(consts.VeyronCredentials, origCredentialsDir)
+
+ // Test that with VEYRON_CREDENTIALS unset the runtime's Principal
+ // is correctly initialized.
+ if err := os.Setenv(consts.VeyronCredentials, ""); err != nil {
+ t.Fatal(err)
+ }
+
+ sh, err := modules.NewShell(nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ defer sh.Cleanup(os.Stderr, os.Stderr)
+
+ blessing := collect(sh, nil)
+ if len(blessing) == 0 {
+ t.Fatalf("child returned an empty default blessings set")
+ }
+
+ // Test specifying credentials via VEYRON_CREDENTIALS environment.
+ cdir1 := tmpDir(t)
+ defer os.RemoveAll(cdir1)
+ createCredentialsInDir(t, cdir1, "test_env")
+ credEnv := []string{consts.VeyronCredentials + "=" + cdir1}
+
+ blessing = collect(sh, credEnv)
+ if got, want := blessing, "test_env"; got != want {
+ t.Errorf("got default blessings: %q, want %q", got, want)
+ }
+
+ // Test specifying credentials via the command line and that the
+ // comand line overrides the environment
+ cdir2 := tmpDir(t)
+ defer os.RemoveAll(cdir2)
+ createCredentialsInDir(t, cdir2, "test_cmd")
+
+ blessing = collect(sh, credEnv, "--veyron.credentials="+cdir2)
+ if got, want := blessing, "test_cmd"; got != want {
+ t.Errorf("got %q, want %q", got, want)
+ }
+}
diff --git a/runtimes/google/rt/runtimex.go b/runtimes/google/rt/runtimex.go
index c50f693..ef249cc 100644
--- a/runtimes/google/rt/runtimex.go
+++ b/runtimes/google/rt/runtimex.go
@@ -1,26 +1,38 @@
package rt
import (
+ "flag"
"fmt"
+ "os"
+ "os/signal"
+ "path/filepath"
+ "strings"
+ "sync"
+ "syscall"
"time"
- _ "v.io/core/veyron/lib/stats/sysstats"
"v.io/core/veyron2"
"v.io/core/veyron2/config"
"v.io/core/veyron2/context"
+ "v.io/core/veyron2/i18n"
"v.io/core/veyron2/ipc"
"v.io/core/veyron2/ipc/stream"
"v.io/core/veyron2/naming"
"v.io/core/veyron2/options"
"v.io/core/veyron2/security"
+ "v.io/core/veyron2/verror2"
"v.io/core/veyron2/vlog"
+ "v.io/core/veyron2/vtrace"
- //iipc "v.io/core/veyron/runtimes/google/ipc"
+ "v.io/core/veyron/lib/exec"
+ "v.io/core/veyron/lib/flags"
+ _ "v.io/core/veyron/lib/stats/sysstats"
iipc "v.io/core/veyron/runtimes/google/ipc"
imanager "v.io/core/veyron/runtimes/google/ipc/stream/manager"
"v.io/core/veyron/runtimes/google/ipc/stream/vc"
inaming "v.io/core/veyron/runtimes/google/naming"
"v.io/core/veyron/runtimes/google/naming/namespace"
+ ivtrace "v.io/core/veyron/runtimes/google/vtrace"
)
type contextKey int
@@ -39,8 +51,14 @@
publisherKey
)
+// TODO(suharshs,mattr): Panic instead of flagsOnce after the transition to veyron.Init is completed.
+var flagsOnce sync.Once
+var runtimeFlags *flags.Flags
+var signals chan os.Signal
+
func init() {
veyron2.RegisterRuntime("google", &RuntimeX{})
+ runtimeFlags = flags.CreateAndRegister(flag.CommandLine, flags.Runtime)
}
// initRuntimeXContext provides compatibility between Runtime and RuntimeX.
@@ -68,18 +86,140 @@
// individiual methods.
type RuntimeX struct{}
-// TODO(mattr): This function isn't used yet. We'll implement it later
-// in the transition.
-func (*RuntimeX) Init(ctx *context.T, protocols []string) *context.T {
- // TODO(mattr): Here we need to do a bunch of one time init, like parsing flags
- // and reading the credentials, init logging and verror, start an appcycle manager.
- // TODO(mattr): Here we need to arrange for a long of one time cleanup
- // when cancel is called. Dump vtrace, shotdown signalhandling, shutdownlogging,
- // shutdown the appcyclemanager.
+func (r *RuntimeX) Init(ctx *context.T, protocols []string) (*context.T, error) {
+ handle, err := exec.GetChildHandle()
+ switch err {
+ case exec.ErrNoVersion:
+ // The process has not been started through the veyron exec
+ // library. No further action is needed.
+ case nil:
+ // The process has been started through the veyron exec
+ // library.
+ default:
+ return nil, err
+ }
+
+ r.initLogging(ctx)
+ ctx = context.WithValue(ctx, loggerKey, vlog.Log)
+
+ // Set the preferred protocols.
if len(protocols) > 0 {
ctx = context.WithValue(ctx, protocolsKey, protocols)
}
- return ctx
+
+ // Parse runtime flags.
+ flagsOnce.Do(func() {
+ var config map[string]string
+ if handle != nil {
+ config = handle.Config.Dump()
+ }
+ runtimeFlags.Parse(os.Args[1:], config)
+ })
+ flags := runtimeFlags.RuntimeFlags()
+
+ // Setup i18n.
+ ctx = i18n.ContextWithLangID(ctx, i18n.LangIDFromEnv())
+ if len(flags.I18nCatalogue) != 0 {
+ cat := i18n.Cat()
+ for _, filename := range strings.Split(flags.I18nCatalogue, ",") {
+ err := cat.MergeFromFile(filename)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s: i18n: error reading i18n catalogue file %q: %s\n", os.Args[0], filename, err)
+ }
+ }
+ }
+
+ // Setup the program name.
+ ctx = verror2.ContextWithComponentName(ctx, filepath.Base(os.Args[0]))
+
+ // Setup the initial trace.
+ ctx, err = ivtrace.Init(ctx, flags.Vtrace)
+ if err != nil {
+ return nil, err
+ }
+ ctx, _ = vtrace.SetNewTrace(ctx)
+
+ // Enable signal handling.
+ initSignalHandling(ctx)
+
+ // Set the initial namespace.
+ ctx, _, err = r.setNewNamespace(ctx, flags.NamespaceRoots...)
+ if err != nil {
+ return nil, err
+ }
+
+ // Set the initial stream manager.
+ ctx, _, err = r.setNewStreamManager(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ // The clinet we attach here is incomplete (has a nil principal) and only works
+ // because the agent uses anonymous unix sockets and VCSecurityNone.
+ // After security is initialized we will attach a real client.
+ ctx, _, err = r.SetNewClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ // Initialize security.
+ principal, err := initSecurity(ctx, handle, flags.Credentials)
+ if err != nil {
+ return nil, err
+ }
+ ctx = context.WithValue(ctx, principalKey, principal)
+
+ // Set up secure client.
+ ctx, _, err = r.SetNewClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ // Initialize management.
+ if err := initMgmt(ctx, r.GetAppCycle(ctx), handle); err != nil {
+ return nil, err
+ }
+
+ // TODO(suharshs,mattr): Go through the rt.Cleanup function and make sure everything
+ // gets cleaned up.
+
+ return ctx, nil
+}
+
+// initLogging configures logging for the runtime. It needs to be called after
+// flag.Parse and after signal handling has been initialized.
+func (*RuntimeX) initLogging(ctx *context.T) error {
+ if done := ctx.Done(); done != nil {
+ go func() {
+ <-done
+ vlog.FlushLog()
+ }()
+ }
+ return vlog.ConfigureLibraryLoggerFromFlags()
+}
+
+func initSignalHandling(ctx *context.T) {
+ // TODO(caprita): Given that our device manager implementation is to
+ // kill all child apps when the device manager dies, we should
+ // enable SIGHUP on apps by default.
+
+ // Automatically handle SIGHUP to prevent applications started as
+ // daemons from being killed. The developer can choose to still listen
+ // on SIGHUP and take a different action if desired.
+ signals = make(chan os.Signal, 1)
+ signal.Notify(signals, syscall.SIGHUP)
+ go func() {
+ for {
+ vlog.Infof("Received signal %v", <-signals)
+ }
+ }()
+
+ if done := ctx.Done(); done != nil {
+ go func() {
+ <-done
+ signal.Stop(signals)
+ }()
+ }
}
func (*RuntimeX) NewEndpoint(ep string) (naming.Endpoint, error) {
diff --git a/runtimes/google/rt/runtimex_test.go b/runtimes/google/rt/runtimex_test.go
index 46881eb..a78ad3a 100644
--- a/runtimes/google/rt/runtimex_test.go
+++ b/runtimes/google/rt/runtimex_test.go
@@ -10,17 +10,16 @@
)
// InitForTest creates a context for use in a test.
-// TODO(mattr): We should call runtimeX.Init once that is implemented.
func InitForTest(t *testing.T) (*context.T, context.CancelFunc) {
- rt, err := rt.New(profileOpt)
+ r := &rt.RuntimeX{}
+ var ctx *context.T
+ var cancel context.CancelFunc
+ var err error
+ ctx, cancel = context.WithCancel(ctx)
+ ctx, err = r.Init(ctx, nil)
if err != nil {
- t.Fatalf("Could not create runtime: %v", err)
+ t.Fatal(err)
}
- ctx, cancel := context.WithCancel(rt.NewContext())
- go func() {
- <-ctx.Done()
- rt.Cleanup()
- }()
return ctx, cancel
}
diff --git a/runtimes/google/rt/security.go b/runtimes/google/rt/security.go
index 59f5497..f8df35c 100644
--- a/runtimes/google/rt/security.go
+++ b/runtimes/google/rt/security.go
@@ -1,12 +1,8 @@
package rt
import (
- "fmt"
"os"
- "os/user"
- "strconv"
- "v.io/core/veyron2/mgmt"
"v.io/core/veyron2/security"
"v.io/core/veyron/lib/exec"
@@ -29,23 +25,6 @@
return nil
}
-// agentFD returns a non-negative file descriptor to be used to communicate with
-// the security agent if the current process has been configured to use the
-// agent.
-func agentFD(handle *exec.ChildHandle) (int, error) {
- var fd string
- if handle != nil {
- // We were started by a parent (presumably, device manager).
- fd, _ = handle.Config.Get(mgmt.SecurityAgentFDConfigKey)
- } else {
- fd = os.Getenv(agent.FdVarName)
- }
- if fd == "" {
- return -1, nil
- }
- return strconv.Atoi(fd)
-}
-
func (rt *vrt) setupPrincipal(handle *exec.ChildHandle, credentials string) error {
if rt.principal != nil {
return nil
@@ -80,19 +59,6 @@
return vsecurity.InitDefaultBlessings(rt.principal, defaultBlessingName())
}
-func defaultBlessingName() string {
- var name string
- if user, _ := user.Current(); user != nil && len(user.Username) > 0 {
- name = user.Username
- } else {
- name = "anonymous"
- }
- if host, _ := os.Hostname(); len(host) > 0 {
- name = name + "@" + host
- }
- return fmt.Sprintf("%s-%d", name, os.Getpid())
-}
-
func (rt *vrt) connectToAgent(fd int) (security.Principal, error) {
return agent.NewAgentPrincipal(rt.NewContext(), fd)
}
diff --git a/runtimes/google/rt/securityx.go b/runtimes/google/rt/securityx.go
new file mode 100644
index 0000000..8ebb15d
--- /dev/null
+++ b/runtimes/google/rt/securityx.go
@@ -0,0 +1,92 @@
+package rt
+
+import (
+ "fmt"
+ "os"
+ "os/user"
+ "strconv"
+
+ "v.io/core/veyron2/context"
+ "v.io/core/veyron2/mgmt"
+ "v.io/core/veyron2/security"
+
+ "v.io/core/veyron/lib/exec"
+ "v.io/core/veyron/lib/stats"
+ vsecurity "v.io/core/veyron/security"
+ "v.io/core/veyron/security/agent"
+)
+
+func initSecurity(ctx *context.T, handle *exec.ChildHandle, credentials string) (security.Principal, error) {
+ principal, err := setupPrincipal(ctx, handle, credentials)
+ if err != nil {
+ return nil, err
+ }
+
+ // TODO(suharshs,mattr): Move this code to SetNewPrincipal and determine what their string should be.
+ stats.NewString("security/principal/key").Set(principal.PublicKey().String())
+ stats.NewStringFunc("security/principal/blessingstore", principal.BlessingStore().DebugString)
+ stats.NewStringFunc("security/principal/blessingroots", principal.Roots().DebugString)
+ return principal, nil
+}
+
+func setupPrincipal(ctx *context.T, handle *exec.ChildHandle, credentials string) (security.Principal, error) {
+ var err error
+ var principal security.Principal
+ if principal, _ = ctx.Value(principalKey).(security.Principal); principal != nil {
+ return principal, nil
+ }
+ if fd, err := agentFD(handle); err != nil {
+ return nil, err
+ } else if fd >= 0 {
+ return agent.NewAgentPrincipal(ctx, fd)
+ }
+ if len(credentials) > 0 {
+ // TODO(ataly, ashankar): If multiple runtimes are getting
+ // initialized at the same time from the same VEYRON_CREDENTIALS
+ // we will need some kind of locking for the credential files.
+ if principal, err = vsecurity.LoadPersistentPrincipal(credentials, nil); err != nil {
+ if os.IsNotExist(err) {
+ if principal, err = vsecurity.CreatePersistentPrincipal(credentials, nil); err != nil {
+ return principal, err
+ }
+ return principal, vsecurity.InitDefaultBlessings(principal, defaultBlessingName())
+ }
+ return nil, err
+ }
+ return principal, nil
+ }
+ if principal, err = vsecurity.NewPrincipal(); err != nil {
+ return principal, err
+ }
+ return principal, vsecurity.InitDefaultBlessings(principal, defaultBlessingName())
+}
+
+// agentFD returns a non-negative file descriptor to be used to communicate with
+// the security agent if the current process has been configured to use the
+// agent.
+func agentFD(handle *exec.ChildHandle) (int, error) {
+ var fd string
+ if handle != nil {
+ // We were started by a parent (presumably, device manager).
+ fd, _ = handle.Config.Get(mgmt.SecurityAgentFDConfigKey)
+ } else {
+ fd = os.Getenv(agent.FdVarName)
+ }
+ if fd == "" {
+ return -1, nil
+ }
+ return strconv.Atoi(fd)
+}
+
+func defaultBlessingName() string {
+ var name string
+ if user, _ := user.Current(); user != nil && len(user.Username) > 0 {
+ name = user.Username
+ } else {
+ name = "anonymous"
+ }
+ if host, _ := os.Hostname(); len(host) > 0 {
+ name = name + "@" + host
+ }
+ return fmt.Sprintf("%s-%d", name, os.Getpid())
+}