blob: 130cad45fffce8705b3a22845b6fac1c5e0b1f18 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package utiltest
import (
"fmt"
"io"
"io/ioutil"
"os"
goexec "os/exec"
"strings"
"testing"
"v.io/v23"
"v.io/v23/rpc"
"v.io/x/lib/gosh"
"v.io/x/ref"
"v.io/x/ref/internal/logger"
"v.io/x/ref/lib/signals"
"v.io/x/ref/services/device/deviced/internal/starter"
"v.io/x/ref/services/device/deviced/internal/versioning"
"v.io/x/ref/services/device/internal/config"
"v.io/x/ref/services/device/internal/suid"
"v.io/x/ref/test"
"v.io/x/ref/test/v23test"
)
const (
RedirectEnv = "DEVICE_MANAGER_DONT_REDIRECT_STDOUT_STDERR"
TestEnvVarName = "V23_RANDOM_ENV_VALUE"
NoPairingToken = ""
)
// ExecScript launches the script passed as argument.
var ExecScript = gosh.RegisterFunc("ExecScript", func(script string) error {
osenv := []string{RedirectEnv + "=1"}
if os.Getenv("PAUSE_BEFORE_STOP") == "1" {
osenv = append(osenv, "PAUSE_BEFORE_STOP=1")
}
cmd := goexec.Cmd{
Path: script,
Env: osenv,
Stdin: os.Stdin,
Stderr: os.Stderr,
Stdout: os.Stdout,
}
return cmd.Run()
})
// DeviceManager sets up a device manager server. It accepts the name to
// publish the server under as an argument. Additional arguments can optionally
// specify device manager config settings.
var DeviceManager = gosh.RegisterFunc("DeviceManager", deviceManagerFunc)
func waitForEOF(r io.Reader) {
io.Copy(ioutil.Discard, r)
}
func deviceManagerFunc(publishName string, args ...string) error {
ctx, shutdown := test.V23Init()
defer shutdown()
defer fmt.Printf("%v terminated\n", publishName)
defer ctx.VI(1).Infof("%v terminated", publishName)
// Satisfy the contract described in doc.go by passing the config state
// through to the device manager dispatcher constructor.
configState, err := config.Load()
if err != nil {
ctx.Fatalf("Failed to decode config state: %v", err)
}
// This exemplifies how to override or set specific config fields, if,
// for example, the device manager is invoked 'by hand' instead of via a
// script prepared by a previous version of the device manager.
var pairingToken string
if len(args) > 0 {
if want, got := 4, len(args); want > got {
ctx.Fatalf("expected atleast %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]
if len(args) > 4 {
pairingToken = args[4]
}
}
// We grab the shutdown channel at this point in order to ensure that we
// register a listener for the app cycle manager Stop before we start
// running the device manager service. Otherwise, any device manager
// method that calls Stop on the app cycle manager (e.g. the Stop RPC)
// will precipitate an immediate process exit.
shutdownChan := signals.ShutdownOnSignals(ctx)
listenSpec := rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}}
blessings, _ := v23.GetPrincipal(ctx).BlessingStore().Default()
claimableEps, stop, err := starter.Start(ctx, starter.Args{
Namespace: starter.NamespaceArgs{
ListenSpec: listenSpec,
},
Device: starter.DeviceArgs{
Name: publishName,
ListenSpec: listenSpec,
ConfigState: configState,
TestMode: strings.HasSuffix(fmt.Sprint(blessings), ":testdm"),
RestartCallback: func() { fmt.Println("restart handler") },
PairingToken: pairingToken,
},
// TODO(rthellend): Wire up the local mounttable like the real device
// manager, i.e. mount the device manager and the apps on it, and mount
// the local mounttable in the global namespace.
// MountGlobalNamespaceInLocalNamespace: true,
})
if err != nil {
ctx.Errorf("starter.Start failed: %v", err)
return err
}
defer stop()
// Update the namespace roots to remove the server blessing from the
// endpoints. This is needed to be able to publish into the 'global'
// mounttable before we have compatible credentials.
ctx, err = SetNamespaceRootsForUnclaimedDevice(ctx)
if err != nil {
return err
}
// Manually mount the claimable service in the 'global' mounttable.
for _, ep := range claimableEps {
v23.GetNamespace(ctx).Mount(ctx, "claimable", ep.Name(), 0)
}
fmt.Println("READY")
<-shutdownChan
if os.Getenv("PAUSE_BEFORE_STOP") == "1" {
waitForEOF(os.Stdin)
}
// TODO(ashankar): Figure out a way to incorporate this check in the test.
// if impl.DispatcherLeaking(dispatcher) {
// ctx.Fatalf("device manager leaking resources")
// }
return nil
}
// This is the same as DeviceManager above, except that it has a different major
// version number.
var DeviceManagerV10 = gosh.RegisterFunc("DeviceManagerV10", func(publishName string, args ...string) error {
versioning.CurrentVersion = versioning.Version{10, 0} // Set the version number to 10.0
return deviceManagerFunc(publishName, args...)
})
func DeviceManagerCmd(sh *v23test.Shell, f *gosh.Func, args ...interface{}) *v23test.Cmd {
dm := sh.FuncCmd(f, args...)
// Make sure the device manager command is not provided with credentials.
delete(dm.Vars, ref.EnvCredentials)
delete(dm.Vars, ref.EnvAgentPath)
return dm
}
func TestMainImpl(m *testing.M) {
isSuidHelper := len(os.Getenv("V23_SUIDHELPER_TEST")) > 0
if isSuidHelper {
os.Exit(m.Run())
}
v23test.TestMain(m)
}
// TestSuidHelper is testing boilerplate for suidhelper that does not
// create a runtime because the suidhelper is not a Vanadium application.
func TestSuidHelperImpl(t *testing.T) {
if os.Getenv("V23_SUIDHELPER_TEST") != "1" {
return
}
logger.Global().VI(1).Infof("TestSuidHelper starting")
if err := suid.Run(os.Environ()); err != nil {
logger.Global().Fatalf("Failed to Run() setuidhelper: %v", err)
}
// Don't show "PASS"
os.Exit(0)
}