Merge "services/device/interna/impl: split app service state"
diff --git a/lib/apilog/log.go b/lib/apilog/apilog.go
similarity index 75%
rename from lib/apilog/log.go
rename to lib/apilog/apilog.go
index 3eb4f92..83c11b0 100644
--- a/lib/apilog/log.go
+++ b/lib/apilog/apilog.go
@@ -2,16 +2,25 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package apilog provides a function to be used in conjunction with vtrace
-// and logcop automatically injected logging calls. These are called on
-// entry/exit for methods that implements a v23 API call.
+// Package apilog provides functions to be used in conjunction with logcop.
+// In particular, logcop will inject calls to these functions as the first
+// statement in methods that implement the v23 API. The output can
+// be controlled by vlog verbosity or vtrace.
+// The log lines generated have a header that refers to this file
+// and the message includes the file and line # of the caller. This is
+// currently the best we can do given the functionality of the underlying
+// vlog package. It has the advantage the api logging can be selectively
+// enabled/disabled globally using --vmodule=apilog=<level>, but the
+// disadvantage that it can't be controlled at the per file level.
package apilog
import (
"fmt"
"path"
+ "path/filepath"
"reflect"
"runtime"
+ "strconv"
"sync/atomic"
"v.io/x/lib/vlog"
@@ -22,17 +31,23 @@
// logCallLogLevel is the log level beyond which calls are logged.
const logCallLogLevel = 1
-func callerFuncName() string {
+var logger vlog.Logger
+
+func init() {
+ logger = vlog.Log
+}
+
+func callerLocation() string {
var funcName string
const stackSkip = 1
- pc, _, _, ok := runtime.Caller(stackSkip + 1)
+ pc, file, line, ok := runtime.Caller(stackSkip + 1)
if ok {
function := runtime.FuncForPC(pc)
if function != nil {
funcName = path.Base(function.Name())
}
}
- return funcName
+ return filepath.Base(file) + ":" + strconv.Itoa(line) + " " + funcName
}
// TODO(cnicolaou): remove LogCall from vlog.
@@ -89,27 +104,28 @@
// }
//
func LogCall(ctx *context.T, v ...interface{}) func(*context.T, ...interface{}) {
- if !vlog.V(logCallLogLevel) { // TODO(mattr): add call to vtrace.
+ if !logger.V(logCallLogLevel) { // TODO(mattr): add call to vtrace.
return func(*context.T, ...interface{}) {}
}
- callerFuncName := callerFuncName()
+ callerLocation := callerLocation()
invocationId := newInvocationIdentifier()
var output string
if len(v) > 0 {
- output = fmt.Sprintf("call[%s %s]: args:%v", callerFuncName, invocationId, v)
+ output = fmt.Sprintf("call[%s %s]: args:%v", callerLocation, invocationId, v)
} else {
- output = fmt.Sprintf("call[%s %s]", callerFuncName, invocationId)
+ output = fmt.Sprintf("call[%s %s]", callerLocation, invocationId)
}
- vlog.Info(output)
+ logger.Info(output)
+
// TODO(mattr): annotate vtrace span.
return func(ctx *context.T, v ...interface{}) {
var output string
if len(v) > 0 {
- output = fmt.Sprintf("return[%s %s]: %v", callerFuncName, invocationId, derefSlice(v))
+ output = fmt.Sprintf("return[%s %s]: %v", callerLocation, invocationId, derefSlice(v))
} else {
- output = fmt.Sprintf("return[%s %s]", callerFuncName, invocationId)
+ output = fmt.Sprintf("return[%s %s]", callerLocation, invocationId)
}
- vlog.Info(output)
+ logger.Info(output)
// TODO(mattr): annotate vtrace span.
}
}
@@ -124,17 +140,17 @@
// }
//
func LogCallf(ctx *context.T, format string, v ...interface{}) func(*context.T, string, ...interface{}) {
- if !vlog.V(logCallLogLevel) { // TODO(mattr): add call to vtrace.
+ if !logger.V(logCallLogLevel) { // TODO(mattr): add call to vtrace.
return func(*context.T, string, ...interface{}) {}
}
- callerFuncName := callerFuncName()
+ callerLocation := callerLocation()
invocationId := newInvocationIdentifier()
- output := fmt.Sprintf("call[%s %s]: %s", callerFuncName, invocationId, fmt.Sprintf(format, v...))
- vlog.Info(output)
+ output := fmt.Sprintf("call[%s %s]: %s", callerLocation, invocationId, fmt.Sprintf(format, v...))
+ logger.Info(output)
// TODO(mattr): annotate vtrace span.
return func(ctx *context.T, format string, v ...interface{}) {
- output := fmt.Sprintf("return[%s %s]: %v", callerFuncName, invocationId, fmt.Sprintf(format, derefSlice(v)...))
- vlog.Info(output)
+ output := fmt.Sprintf("return[%s %s]: %v", callerLocation, invocationId, fmt.Sprintf(format, derefSlice(v)...))
+ logger.Info(output)
// TODO(mattr): annotate vtrace span.
}
}
diff --git a/lib/apilog/apilog_test.go b/lib/apilog/apilog_test.go
new file mode 100644
index 0000000..d085efc
--- /dev/null
+++ b/lib/apilog/apilog_test.go
@@ -0,0 +1,93 @@
+// 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 apilog_test
+
+import (
+ "bufio"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "regexp"
+ "strings"
+ "testing"
+
+ "v.io/x/lib/vlog"
+
+ "v.io/x/ref/lib/apilog"
+)
+
+func readLogFiles(dir string) ([]string, error) {
+ files, err := ioutil.ReadDir(dir)
+ if err != nil {
+ return nil, err
+ }
+ var contents []string
+ for _, fi := range files {
+ // Skip symlinks to avoid double-counting log lines.
+ if !fi.Mode().IsRegular() {
+ continue
+ }
+ file, err := os.Open(filepath.Join(dir, fi.Name()))
+ if err != nil {
+ return nil, err
+ }
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ if line := scanner.Text(); len(line) > 0 && line[0] == 'I' {
+ contents = append(contents, line)
+ }
+ }
+ }
+ return contents, nil
+}
+
+func myLoggedFunc() {
+ f := apilog.LogCall(nil, "entry")
+ f(nil, "exit")
+}
+
+func TestLogCall(t *testing.T) {
+ dir, err := ioutil.TempDir("", "logtest")
+ defer os.RemoveAll(dir)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ logger := vlog.NewLogger("testHeader")
+ logger.Configure(vlog.LogDir(dir), vlog.Level(2))
+ saveLog := apilog.Log()
+ defer func() { apilog.SetLog(saveLog) }()
+ apilog.SetLog(logger)
+ myLoggedFunc()
+ logger.FlushLog()
+ contents, err := readLogFiles(dir)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ if want, got := 2, len(contents); want != got {
+ t.Errorf("Expected %d info lines, got %d instead", want, got)
+ }
+ logCallLineRE := regexp.MustCompile(`\S+ \S+ \S+ ([^:]*):.*(call|return)\[(\S*) (\S*)`)
+ for _, line := range contents {
+ match := logCallLineRE.FindStringSubmatch(line)
+ if len(match) != 5 {
+ t.Errorf("failed to match %s", line)
+ continue
+ }
+ fileName, callType, location, funcName := match[1], match[2], match[3], match[4]
+ if fileName != "apilog.go" {
+ t.Errorf("unexpected file name: %s", fileName)
+ continue
+ }
+ if callType != "call" && callType != "return" {
+ t.Errorf("unexpected call type: %s", callType)
+ }
+ if !strings.HasPrefix(location, "apilog_test.go:") {
+ t.Errorf("unexpected location: %s", location)
+ }
+ if funcName != "apilog_test.myLoggedFunc" {
+ t.Errorf("unexpected func name: %s", funcName)
+ }
+ }
+}
diff --git a/lib/apilog/util_test.go b/lib/apilog/util_test.go
new file mode 100644
index 0000000..9d2ea7d
--- /dev/null
+++ b/lib/apilog/util_test.go
@@ -0,0 +1,15 @@
+// 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 apilog
+
+import "v.io/x/lib/vlog"
+
+func SetLog(l vlog.Logger) {
+ logger = l
+}
+
+func Log() vlog.Logger {
+ return logger
+}
diff --git a/lib/vdl/codegen/java/file_client_impl.go b/lib/vdl/codegen/java/file_client_impl.go
index 36b5e20..be2d92c 100644
--- a/lib/vdl/codegen/java/file_client_impl.go
+++ b/lib/vdl/codegen/java/file_client_impl.go
@@ -70,7 +70,7 @@
// Start the call.
java.lang.Object[] _args = new java.lang.Object[]{ {{ $method.CallingArgs }} };
java.lang.reflect.Type[] _argTypes = new java.lang.reflect.Type[]{ {{ $method.CallingArgTypes }} };
- final io.v.v23.rpc.Client.Call _call = getClient(context).startCall(context, this.vName, "{{ $method.Name }}", _args, _argTypes, vOpts);
+ final io.v.v23.rpc.ClientCall _call = getClient(context).startCall(context, this.vName, "{{ $method.Name }}", _args, _argTypes, vOpts);
// Finish the call.
{{/* Now handle returning from the function. */}}
@@ -99,7 +99,7 @@
{{ end }} {{/* end if $method.IsVoid */}}
{{else }} {{/* else $method.NotStreaming */}}
- return new io.v.v23.vdl.ClientStream<{{ $method.SendType }}, {{ $method.RecvType }}, {{ $method.DeclaredObjectRetType }}>() {
+ return new io.v.v23.vdl.TypedClientStream<{{ $method.SendType }}, {{ $method.RecvType }}, {{ $method.DeclaredObjectRetType }}>() {
@Override
public void send({{ $method.SendType }} item) throws io.v.v23.verror.VException {
java.lang.reflect.Type type = new com.google.common.reflect.TypeToken<{{ $method.SendType }}>() {}.getType();
diff --git a/lib/vdl/codegen/java/file_client_interface.go b/lib/vdl/codegen/java/file_client_interface.go
index 83ee6f1..f4f8c32 100644
--- a/lib/vdl/codegen/java/file_client_interface.go
+++ b/lib/vdl/codegen/java/file_client_interface.go
@@ -71,7 +71,7 @@
func clientInterfaceOutArg(iface *compile.Interface, method *compile.Method, env *compile.Env) string {
if isStreamingMethod(method) {
- return fmt.Sprintf("io.v.v23.vdl.ClientStream<%s, %s, %s>", javaType(method.InStream, true, env), javaType(method.OutStream, true, env), clientInterfaceNonStreamingOutArg(iface, method, true, env))
+ return fmt.Sprintf("io.v.v23.vdl.TypedClientStream<%s, %s, %s>", javaType(method.InStream, true, env), javaType(method.OutStream, true, env), clientInterfaceNonStreamingOutArg(iface, method, true, env))
}
return clientInterfaceNonStreamingOutArg(iface, method, false, env)
}
diff --git a/lib/vdl/codegen/java/file_server_interface.go b/lib/vdl/codegen/java/file_server_interface.go
index 0b0d96c..e978a4e 100644
--- a/lib/vdl/codegen/java/file_server_interface.go
+++ b/lib/vdl/codegen/java/file_server_interface.go
@@ -73,7 +73,7 @@
func processServerInterfaceMethod(method *compile.Method, iface *compile.Interface, env *compile.Env) serverInterfaceMethod {
args := javaDeclarationArgStr(method.InArgs, env, true)
if isStreamingMethod(method) {
- args += fmt.Sprintf(", io.v.v23.vdl.Stream<%s, %s> stream", javaType(method.OutStream, true, env), javaType(method.InStream, true, env))
+ args += fmt.Sprintf(", io.v.v23.vdl.TypedStream<%s, %s> stream", javaType(method.OutStream, true, env), javaType(method.InStream, true, env))
}
retArgs := make([]serverInterfaceArg, len(method.OutArgs))
for i := 0; i < len(method.OutArgs); i++ {
diff --git a/lib/vdl/codegen/java/file_server_wrapper.go b/lib/vdl/codegen/java/file_server_wrapper.go
index cfca39c..6889463 100644
--- a/lib/vdl/codegen/java/file_server_wrapper.go
+++ b/lib/vdl/codegen/java/file_server_wrapper.go
@@ -114,7 +114,7 @@
{{ $method.JavaDoc }}
public {{ $method.RetType }} {{ $method.Name }}(io.v.v23.context.VContext ctx, final io.v.v23.rpc.StreamServerCall call{{ $method.DeclarationArgs }}) throws io.v.v23.verror.VException {
{{ if $method.IsStreaming }}
- io.v.v23.vdl.Stream<{{ $method.SendType }}, {{ $method.RecvType }}> _stream = new io.v.v23.vdl.Stream<{{ $method.SendType }}, {{ $method.RecvType }}>() {
+ io.v.v23.vdl.TypedStream<{{ $method.SendType }}, {{ $method.RecvType }}> _stream = new io.v.v23.vdl.TypedStream<{{ $method.SendType }}, {{ $method.RecvType }}>() {
@Override
public void send({{ $method.SendType }} item) throws io.v.v23.verror.VException {
java.lang.reflect.Type type = new com.google.common.reflect.TypeToken< {{ $method.SendType }} >() {}.getType();
diff --git a/services/application/application/impl_test.go b/services/application/application/impl_test.go
index 4227acd..8f7a084 100644
--- a/services/application/application/impl_test.go
+++ b/services/application/application/impl_test.go
@@ -37,6 +37,8 @@
File: "/path/to/package1",
},
},
+ Restarts: 0,
+ RestartTimeWindow: 0,
}
jsonEnv = `{
"Title": "fifa world cup",
@@ -70,7 +72,9 @@
"S": null
}
}
- }
+ },
+ "Restarts": 0,
+ "RestartTimeWindow": 0
}`
)
diff --git a/services/device/internal/impl/applife/app_life_test.go b/services/device/internal/impl/applife/app_life_test.go
index f336bb2..8484495 100644
--- a/services/device/internal/impl/applife/app_life_test.go
+++ b/services/device/internal/impl/applife/app_life_test.go
@@ -89,7 +89,7 @@
utiltest.Resolve(t, ctx, "pingserver", 1)
// Create an envelope for a first version of the app.
- *envelope = utiltest.EnvelopeFromShell(sh, []string{utiltest.TestEnvVarName + "=env-val-envelope"}, utiltest.AppCmd, "google naps", fmt.Sprintf("--%s=flag-val-envelope", utiltest.TestFlagName), "appV1")
+ *envelope = utiltest.EnvelopeFromShell(sh, []string{utiltest.TestEnvVarName + "=env-val-envelope"}, utiltest.AppCmd, "google naps", 0, 0, fmt.Sprintf("--%s=flag-val-envelope", utiltest.TestFlagName), "appV1")
// Install the app. The config-specified flag value for testFlagName
// should override the value specified in the envelope above, and the
@@ -185,12 +185,12 @@
utiltest.UpdateAppExpectError(t, ctx, appID, impl.ErrUpdateNoOp.ID)
// Updating the installation should not work with a mismatched title.
- *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "bogus")
+ *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "bogus", 0, 0)
utiltest.UpdateAppExpectError(t, ctx, appID, impl.ErrAppTitleMismatch.ID)
// Create a second version of the app and update the app to it.
- *envelope = utiltest.EnvelopeFromShell(sh, []string{utiltest.TestEnvVarName + "=env-val-envelope"}, utiltest.AppCmd, "google naps", "appV2")
+ *envelope = utiltest.EnvelopeFromShell(sh, []string{utiltest.TestEnvVarName + "=env-val-envelope"}, utiltest.AppCmd, "google naps", 0, 0, "appV2")
utiltest.UpdateApp(t, ctx, appID)
@@ -303,7 +303,7 @@
// cleanly Do this by installing, instantiating, running, and killing
// hangingApp, which sleeps (rather than exits) after being asked to
// Stop()
- *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.HangingAppCmd, "hanging ap", "hAppV1")
+ *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.HangingAppCmd, "hanging ap", 0, 0, "hAppV1")
hAppID := utiltest.InstallApp(t, ctx)
hInstanceID := utiltest.LaunchApp(t, ctx, hAppID)
hangingPid := pingCh.WaitForPingArgs(t).Pid
diff --git a/services/device/internal/impl/applife/instance_reaping_test.go b/services/device/internal/impl/applife/instance_reaping_test.go
index ef023ca..dfe3592 100644
--- a/services/device/internal/impl/applife/instance_reaping_test.go
+++ b/services/device/internal/impl/applife/instance_reaping_test.go
@@ -34,7 +34,7 @@
utiltest.Resolve(t, ctx, "pingserver", 1)
// Create an envelope for a first version of the app.
- *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", "appV1")
+ *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 0, 0, "appV1")
// Install the app. The config-specified flag value for testFlagName
// should override the value specified in the envelope above.
diff --git a/services/device/internal/impl/daemonreap/daemon_reaping_test.go b/services/device/internal/impl/daemonreap/daemon_reaping_test.go
new file mode 100644
index 0000000..84d0602
--- /dev/null
+++ b/services/device/internal/impl/daemonreap/daemon_reaping_test.go
@@ -0,0 +1,87 @@
+// 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 daemonreap_test
+
+import (
+ "syscall"
+ "testing"
+ "time"
+
+ "v.io/v23/naming"
+ "v.io/v23/services/device"
+ "v.io/v23/services/stats"
+ "v.io/v23/vdl"
+
+ "v.io/x/ref/services/device/internal/impl/utiltest"
+ "v.io/x/ref/services/internal/servicetest"
+)
+
+func TestDaemonRestart(t *testing.T) {
+ cleanup, ctx, sh, envelope, root, helperPath, _ := utiltest.StartupHelper(t)
+ defer cleanup()
+
+ // Set up the device manager. Since we won't do device manager updates,
+ // don't worry about its application envelope and current link.
+ dmh := servicetest.RunCommand(t, sh, nil, utiltest.DeviceManagerCmd, "dm", root, helperPath, "unused_app_repo_name", "unused_curr_link")
+ servicetest.ReadPID(t, dmh)
+ utiltest.ClaimDevice(t, ctx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
+
+ // Create the local server that the app uses to let us know it's ready.
+ pingCh, cleanup := utiltest.SetupPingServer(t, ctx)
+ defer cleanup()
+
+ utiltest.Resolve(t, ctx, "pingserver", 1)
+
+ // Create an envelope for a first version of the app that will be restarted once.
+ *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 1, 10*time.Second, "appV1")
+ appID := utiltest.InstallApp(t, ctx)
+
+ // Start an instance of the app.
+ instance1ID := utiltest.LaunchApp(t, ctx, appID)
+
+ // Wait until the app pings us that it's ready.
+ pingCh.VerifyPingArgs(t, utiltest.UserName(t), "default", "")
+
+ // Get application pid.
+ name := naming.Join("dm", "apps/"+appID+"/"+instance1ID+"/stats/system/pid")
+ c := stats.StatsClient(name)
+ v, err := c.Value(ctx)
+ if err != nil {
+ t.Fatalf("Value() failed: %v\n", err)
+ }
+ var pid int
+ if err := vdl.Convert(&pid, v); err != nil {
+ t.Fatalf("pid returned from stats interface is not an int: %v", err)
+ }
+
+ utiltest.VerifyState(t, ctx, device.InstanceStateRunning, appID, instance1ID)
+ syscall.Kill(int(pid), 9)
+
+ // Start a second instance of the app which will force polling to happen.
+ instance2ID := utiltest.LaunchApp(t, ctx, appID)
+ pingCh.VerifyPingArgs(t, utiltest.UserName(t), "default", "")
+
+ // TODO(rjkroege): Because there is no daemon mode, instance1ID is not running even
+ // though it should be.
+ utiltest.VerifyState(t, ctx, device.InstanceStateNotRunning, appID, instance1ID)
+
+ // TODO(rjkroege): Demonstrate that the device manager will only restart the app the
+ // configured number of times (1)
+
+ // instance2ID is still running though.
+ utiltest.VerifyState(t, ctx, device.InstanceStateRunning, appID, instance2ID)
+
+ // Cleanup.
+ utiltest.TerminateApp(t, ctx, appID, instance2ID)
+
+ // TODO(rjkroege): instance1ID isn't running but should be.
+ // utiltest.TerminateApp(t, ctx, appID, instance1ID)
+
+ // Cleanly shut down the device manager.
+ utiltest.VerifyNoRunningProcesses(t)
+ syscall.Kill(dmh.Pid(), syscall.SIGINT)
+ dmh.Expect("dm terminated")
+ dmh.ExpectEOF()
+}
diff --git a/services/device/internal/impl/daemonreap/doc.go b/services/device/internal/impl/daemonreap/doc.go
new file mode 100644
index 0000000..c0def53
--- /dev/null
+++ b/services/device/internal/impl/daemonreap/doc.go
@@ -0,0 +1,8 @@
+// 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 daemonreap
+
+// Test code for the device manager's facility to run daemon
+// processes and reconcile processes via kill.
diff --git a/services/device/internal/impl/daemonreap/impl_test.go b/services/device/internal/impl/daemonreap/impl_test.go
new file mode 100644
index 0000000..dd72081
--- /dev/null
+++ b/services/device/internal/impl/daemonreap/impl_test.go
@@ -0,0 +1,19 @@
+// 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 daemonreap_test
+
+import (
+ "testing"
+
+ "v.io/x/ref/services/device/internal/impl/utiltest"
+)
+
+func TestMain(m *testing.M) {
+ utiltest.TestMainImpl(m)
+}
+
+func TestSuidHelper(t *testing.T) {
+ utiltest.TestSuidHelperImpl(t)
+}
diff --git a/services/device/internal/impl/instance_reaping_kill_test.go b/services/device/internal/impl/daemonreap/instance_reaping_kill_test.go
similarity index 98%
rename from services/device/internal/impl/instance_reaping_kill_test.go
rename to services/device/internal/impl/daemonreap/instance_reaping_kill_test.go
index 8e6c68b..c04f8b0 100644
--- a/services/device/internal/impl/instance_reaping_kill_test.go
+++ b/services/device/internal/impl/daemonreap/instance_reaping_kill_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package impl_test
+package daemonreap_test
import (
"fmt"
@@ -42,7 +42,7 @@
utiltest.Resolve(t, ctx, "pingserver", 1)
// Create an envelope for the app.
- *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", "appV1")
+ *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 0, 0, "appV1")
// Install the app.
appID := utiltest.InstallApp(t, ctx)
diff --git a/services/device/internal/impl/globsuid/glob_test.go b/services/device/internal/impl/globsuid/glob_test.go
index f9becde..30fdc23 100644
--- a/services/device/internal/impl/globsuid/glob_test.go
+++ b/services/device/internal/impl/globsuid/glob_test.go
@@ -45,7 +45,7 @@
defer cleanup()
// Create the envelope for the first version of the app.
- *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", "appV1")
+ *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 0, 0, "appV1")
// Device must be claimed before applications can be installed.
utiltest.ClaimDevice(t, ctx, "claimable", "dm", "mydevice", utiltest.NoPairingToken)
diff --git a/services/device/internal/impl/globsuid/suid_test.go b/services/device/internal/impl/globsuid/suid_test.go
index 83ae185..12afe73 100644
--- a/services/device/internal/impl/globsuid/suid_test.go
+++ b/services/device/internal/impl/globsuid/suid_test.go
@@ -82,7 +82,7 @@
defer cleanup()
// Create an envelope for a first version of the app.
- *envelope = utiltest.EnvelopeFromShell(sh, []string{utiltest.TestEnvVarName + "=env-var"}, utiltest.AppCmd, "google naps", fmt.Sprintf("--%s=flag-val-envelope", utiltest.TestFlagName), "appV1")
+ *envelope = utiltest.EnvelopeFromShell(sh, []string{utiltest.TestEnvVarName + "=env-var"}, utiltest.AppCmd, "google naps", 0, 0, fmt.Sprintf("--%s=flag-val-envelope", utiltest.TestFlagName), "appV1")
// Install and start the app as root/self.
appID := utiltest.InstallApp(t, selfCtx)
diff --git a/services/device/internal/impl/impl_test.go b/services/device/internal/impl/impl_test.go
index 14425d8..4a7743d 100644
--- a/services/device/internal/impl/impl_test.go
+++ b/services/device/internal/impl/impl_test.go
@@ -137,7 +137,7 @@
// Brand new device manager must be claimed first.
utiltest.ClaimDevice(t, ctx, "claimable", "factoryDM", "mydevice", utiltest.NoPairingToken)
// Simulate an invalid envelope in the application repository.
- *envelope = utiltest.EnvelopeFromShell(sh, dmPauseBeforeStopEnv, utiltest.DeviceManagerCmd, "bogus", dmArgs...)
+ *envelope = utiltest.EnvelopeFromShell(sh, dmPauseBeforeStopEnv, utiltest.DeviceManagerCmd, "bogus", 0, 0, dmArgs...)
utiltest.UpdateDeviceExpectError(t, ctx, "factoryDM", impl.ErrAppTitleMismatch.ID)
utiltest.RevertDeviceExpectError(t, ctx, "factoryDM", impl.ErrUpdateNoOp.ID)
@@ -145,7 +145,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.
- *envelope = utiltest.EnvelopeFromShell(sh, dmEnv, utiltest.DeviceManagerCmd, application.DeviceManagerTitle, "v2DM")
+ *envelope = utiltest.EnvelopeFromShell(sh, dmEnv, utiltest.DeviceManagerCmd, application.DeviceManagerTitle, 0, 0, "v2DM")
utiltest.UpdateDevice(t, ctx, "factoryDM")
// Current link should have been updated to point to v2.
@@ -189,7 +189,7 @@
// Try issuing an update with a binary that has a different major version
// number. It should fail.
utiltest.ResolveExpectNotFound(t, ctx, "v2.5DM") // Ensure a clean slate.
- *envelope = utiltest.EnvelopeFromShell(sh, dmEnv, utiltest.DeviceManagerV10Cmd, application.DeviceManagerTitle, "v2.5DM")
+ *envelope = utiltest.EnvelopeFromShell(sh, dmEnv, utiltest.DeviceManagerV10Cmd, application.DeviceManagerTitle, 0, 0, "v2.5DM")
utiltest.UpdateDeviceExpectError(t, ctx, "v2DM", impl.ErrOperationFailed.ID)
if evalLink() != scriptPathV2 {
@@ -197,7 +197,7 @@
}
// Create a third version of the device manager and issue an update.
- *envelope = utiltest.EnvelopeFromShell(sh, dmEnv, utiltest.DeviceManagerCmd, application.DeviceManagerTitle, "v3DM")
+ *envelope = utiltest.EnvelopeFromShell(sh, dmEnv, utiltest.DeviceManagerCmd, application.DeviceManagerTitle, 0, 0, "v3DM")
utiltest.UpdateDevice(t, ctx, "v2DM")
scriptPathV3 := evalLink()
@@ -404,7 +404,7 @@
defer cleanup()
// Create the envelope for the first version of the app.
- *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", "appV1")
+ *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 0, 0, "appV1")
envelope.Packages = map[string]application.SignedFile{
"test": application.SignedFile{
File: "realbin/testpkg",
diff --git a/services/device/internal/impl/perms/debug_perms_test.go b/services/device/internal/impl/perms/debug_perms_test.go
index 9c6e757..296c3b8 100644
--- a/services/device/internal/impl/perms/debug_perms_test.go
+++ b/services/device/internal/impl/perms/debug_perms_test.go
@@ -62,7 +62,7 @@
// TODO(rjkroege): Set AccessLists here that conflict with the one provided by the device
// manager and show that the one set here is overridden.
// Create the envelope for the first version of the app.
- *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", "appV1")
+ *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 0, 0, "appV1")
// Install the app.
appID := utiltest.InstallApp(t, ctx)
diff --git a/services/device/internal/impl/perms/perms_test.go b/services/device/internal/impl/perms/perms_test.go
index 83bd086..69fe3c1 100644
--- a/services/device/internal/impl/perms/perms_test.go
+++ b/services/device/internal/impl/perms/perms_test.go
@@ -59,7 +59,7 @@
pid := servicetest.ReadPID(t, dmh)
defer syscall.Kill(pid, syscall.SIGINT)
- *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", "trapp")
+ *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 0, 0, "trapp")
claimantCtx := utiltest.CtxWithNewPrincipal(t, ctx, idp, "claimant")
octx, err := v23.WithPrincipal(ctx, testutil.NewPrincipal("other"))
@@ -142,7 +142,7 @@
defer utiltest.VerifyNoRunningProcesses(t)
// Create an envelope for an app.
- *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps")
+ *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 0, 0)
// On an unclaimed device manager, there will be no AccessLists.
if _, _, err := device.DeviceClient("claimable").GetPermissions(selfCtx); err == nil {
diff --git a/services/device/internal/impl/reaping/instance_reaping_test.go b/services/device/internal/impl/reaping/instance_reaping_test.go
index 7dd0c55..d4c1aeb 100644
--- a/services/device/internal/impl/reaping/instance_reaping_test.go
+++ b/services/device/internal/impl/reaping/instance_reaping_test.go
@@ -43,7 +43,7 @@
utiltest.Resolve(t, ctx, "pingserver", 1)
// Create an envelope for the app.
- *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", "appV1")
+ *envelope = utiltest.EnvelopeFromShell(sh, nil, utiltest.AppCmd, "google naps", 0, 0, "appV1")
// Install the app.
appID := utiltest.InstallApp(t, ctx)
diff --git a/services/device/internal/impl/utiltest/helpers.go b/services/device/internal/impl/utiltest/helpers.go
index 0f2305a..b92adf6 100644
--- a/services/device/internal/impl/utiltest/helpers.go
+++ b/services/device/internal/impl/utiltest/helpers.go
@@ -69,15 +69,17 @@
}
}
-func EnvelopeFromShell(sh *modules.Shell, env []string, cmd, title string, args ...string) application.Envelope {
+func EnvelopeFromShell(sh *modules.Shell, env []string, cmd, title string, retries int, window time.Duration, args ...string) application.Envelope {
args, nenv := sh.CommandEnvelope(cmd, env, args...)
return application.Envelope{
Title: title,
Args: args[1:],
// TODO(caprita): revisit how the environment is sanitized for arbirary
// apps.
- Env: impl.VanadiumEnvironment(nenv),
- Binary: application.SignedFile{File: MockBinaryRepoName},
+ Env: impl.VanadiumEnvironment(nenv),
+ Binary: application.SignedFile{File: MockBinaryRepoName},
+ Restarts: int32(retries),
+ RestartTimeWindow: window,
}
}
diff --git a/services/identity/internal/blesser/macaroon.go b/services/identity/internal/blesser/macaroon.go
index c96c7cf..1cada67 100644
--- a/services/identity/internal/blesser/macaroon.go
+++ b/services/identity/internal/blesser/macaroon.go
@@ -5,7 +5,9 @@
package blesser
import (
+ "errors"
"fmt"
+ "reflect"
"time"
"v.io/x/ref/services/identity"
@@ -45,6 +47,13 @@
if secCall.LocalPrincipal() == nil {
return empty, fmt.Errorf("server misconfiguration: no authentication happened")
}
+ macaroonPublicKey, err := security.UnmarshalPublicKey(m.PublicKey)
+ if err != nil {
+ return empty, fmt.Errorf("failed to unmarshal public key in macaroon: %v", err)
+ }
+ if !reflect.DeepEqual(secCall.RemoteBlessings().PublicKey(), macaroonPublicKey) {
+ return empty, errors.New("remote end's public key does not match public key in macaroon")
+ }
if len(m.Caveats) == 0 {
m.Caveats = []security.Caveat{security.UnconstrainedUse()}
}
diff --git a/services/identity/internal/blesser/macaroon_test.go b/services/identity/internal/blesser/macaroon_test.go
index eee0dab..541f903 100644
--- a/services/identity/internal/blesser/macaroon_test.go
+++ b/services/identity/internal/blesser/macaroon_test.go
@@ -22,6 +22,7 @@
var (
key = make([]byte, 16)
provider, user = testutil.NewPrincipal(), testutil.NewPrincipal()
+ userKey, _ = user.PublicKey().MarshalBinary()
cOnlyMethodFoo = newCaveat(security.NewMethodCaveat("Foo"))
ctx, call = fakeContextAndCall(provider, user)
)
@@ -30,12 +31,20 @@
}
blesser := NewMacaroonBlesserServer(key)
- m := oauth.BlessingMacaroon{Creation: time.Now().Add(-1 * time.Hour), Name: "foo"}
+ m := oauth.BlessingMacaroon{Creation: time.Now().Add(-1 * time.Hour), Name: "foo", PublicKey: userKey}
wantErr := "macaroon has expired"
if _, err := blesser.Bless(ctx, call, newMacaroon(t, key, m)); err == nil || err.Error() != wantErr {
t.Errorf("Bless(...) failed with error: %v, want: %v", err, wantErr)
}
- m = oauth.BlessingMacaroon{Creation: time.Now(), Name: "bugsbunny", Caveats: []security.Caveat{cOnlyMethodFoo}}
+
+ otherKey, _ := testutil.NewPrincipal().PublicKey().MarshalBinary()
+ m = oauth.BlessingMacaroon{Creation: time.Now(), Name: "foo", PublicKey: otherKey}
+ wantErr = "remote end's public key does not match public key in macaroon"
+ if _, err := blesser.Bless(ctx, call, newMacaroon(t, key, m)); err == nil || err.Error() != wantErr {
+ t.Errorf("Bless(...) failed with error: %v, want: %v", err, wantErr)
+ }
+
+ m = oauth.BlessingMacaroon{Creation: time.Now(), PublicKey: userKey, Name: "bugsbunny", Caveats: []security.Caveat{cOnlyMethodFoo}}
b, err := blesser.Bless(ctx, call, newMacaroon(t, key, m))
if err != nil {
t.Errorf("Bless failed: %v", err)
diff --git a/services/identity/internal/oauth/handler.go b/services/identity/internal/oauth/handler.go
index 97e962c..6d339b6 100644
--- a/services/identity/internal/oauth/handler.go
+++ b/services/identity/internal/oauth/handler.go
@@ -89,9 +89,10 @@
// BlessingMacaroon contains the data that is encoded into the macaroon for creating blessings.
type BlessingMacaroon struct {
- Creation time.Time
- Caveats []security.Caveat
- Name string
+ Creation time.Time
+ Caveats []security.Caveat
+ Name string
+ PublicKey []byte // Marshaled public key of the principal tool.
}
func redirectURL(baseURL, suffix string) string {
@@ -313,6 +314,7 @@
type seekBlessingsMacaroon struct {
RedirectURL, State string
+ PublicKey []byte // Marshaled public key of the principal tool.
}
func validLoopbackURL(u string) (*url.URL, error) {
@@ -340,9 +342,16 @@
util.HTTPBadRequest(w, r, fmt.Errorf("invalid redirect_url: %v", err))
return
}
+ pubKeyBytes, err := base64.URLEncoding.DecodeString(r.FormValue("public_key"))
+ if err != nil {
+ vlog.Infof("seekBlessings failed: invalid public_key: %v", err)
+ util.HTTPBadRequest(w, r, fmt.Errorf("invalid public_key: %v", err))
+ return
+ }
outputMacaroon, err := h.csrfCop.NewToken(w, r, clientIDCookie, seekBlessingsMacaroon{
RedirectURL: redirect,
State: r.FormValue("state"),
+ PublicKey: pubKeyBytes,
})
if err != nil {
vlog.Infof("Failed to create CSRF token[%v] for request %#v", err, r)
@@ -354,6 +363,7 @@
type addCaveatsMacaroon struct {
ToolRedirectURL, ToolState, Email string
+ ToolPublicKey []byte // Marshaled public key of the principal tool.
}
func (h *handler) addCaveats(w http.ResponseWriter, r *http.Request) {
@@ -370,6 +380,7 @@
outputMacaroon, err := h.csrfCop.NewToken(w, r, clientIDCookie, addCaveatsMacaroon{
ToolRedirectURL: inputMacaroon.RedirectURL,
ToolState: inputMacaroon.State,
+ ToolPublicKey: inputMacaroon.PublicKey,
Email: email,
})
if err != nil {
@@ -426,9 +437,10 @@
return
}
m := BlessingMacaroon{
- Creation: time.Now(),
- Caveats: caveats,
- Name: strings.Join(parts, security.ChainSeparator),
+ Creation: time.Now(),
+ Caveats: caveats,
+ Name: strings.Join(parts, security.ChainSeparator),
+ PublicKey: inputMacaroon.ToolPublicKey,
}
macBytes, err := vom.Encode(m)
if err != nil {
diff --git a/services/internal/fs/simplestore.go b/services/internal/fs/simplestore.go
index 8ae5457..2211ecd 100644
--- a/services/internal/fs/simplestore.go
+++ b/services/internal/fs/simplestore.go
@@ -15,6 +15,7 @@
"sort"
"strings"
"sync"
+ "time"
"v.io/x/ref/services/profile"
@@ -86,12 +87,14 @@
// implementation by removing support for loading files in the
// now legacy GOB format.
type applicationEnvelope struct {
- Title string
- Args []string
- Binary application.SignedFile
- Publisher security.WireBlessings
- Env []string
- Packages application.Packages
+ Title string
+ Args []string
+ Binary application.SignedFile
+ Publisher security.WireBlessings
+ Env []string
+ Packages application.Packages
+ Restarts int32
+ RestartTimeWindow time.Duration
}
// This function is needed only to support existing serialized data and
@@ -114,12 +117,14 @@
return nil, err
}
return application.Envelope{
- Title: env.Title,
- Args: env.Args,
- Binary: env.Binary,
- Publisher: publisher,
- Env: env.Env,
- Packages: env.Packages,
+ Title: env.Title,
+ Args: env.Args,
+ Binary: env.Binary,
+ Publisher: publisher,
+ Env: env.Env,
+ Packages: env.Packages,
+ Restarts: env.Restarts,
+ RestartTimeWindow: env.RestartTimeWindow,
}, nil
}
@@ -131,7 +136,7 @@
// Ensure that no fields have been added to application.Envelope,
// because if so, then applicationEnvelope defined in this package
// needs to change
- if n := reflect.TypeOf(application.Envelope{}).NumField(); n != 6 {
+ if n := reflect.TypeOf(application.Envelope{}).NumField(); n != 8 {
panic(fmt.Sprintf("It appears that fields have been added to or removed from application.Envelope before the hack in this file around gob-encodeability was removed. Please also update applicationEnvelope, translateToGobEncodeable and translateToGobDecodeable in this file"))
}
}