veyron/tools/mgmt/device,veyron/services/mgmt/device/impl: config override
for local-install
This changelist enables updates for apps installed with local-install. There are
two changes that make this work:
1. allow install-local to specify a config override (like install), so we can
specify configuration for the app independently of what will be present in the
app envelope downloaded during update. We use a command-line flag for this (and
change the "install" command to also use the flag instead of a positional arg).
2. allow install-local to specify an origin for the app (to override the default
origin which points back to the server run by the command-line tool). We use a
setting in the config to convey this information.
This is a convenient way to use for now to get things working. Long-term, we may
drop this in favor of other mechanisms (e.g. UpdateTo)
Change-Id: I363737c5013f510b3cdf6e3544e96f8d13c6ae2a
diff --git a/services/mgmt/device/impl/app_service.go b/services/mgmt/device/impl/app_service.go
index 2d40769..fd16a44 100644
--- a/services/mgmt/device/impl/app_service.go
+++ b/services/mgmt/device/impl/app_service.go
@@ -411,6 +411,10 @@
if _, err := newVersion(call.Context(), installationDir, envelope, ""); err != nil {
return "", err
}
+ if newOrigin, ok := config[mgmt.AppOriginConfigKey]; ok {
+ delete(config, mgmt.AppOriginConfigKey)
+ applicationVON = newOrigin
+ }
if err := saveOrigin(installationDir, applicationVON); err != nil {
return "", err
}
diff --git a/services/mgmt/device/impl/impl_test.go b/services/mgmt/device/impl/impl_test.go
index d146f15..ac9ec54 100644
--- a/services/mgmt/device/impl/impl_test.go
+++ b/services/mgmt/device/impl/impl_test.go
@@ -30,6 +30,7 @@
"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"
@@ -42,6 +43,7 @@
"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/signals"
"v.io/core/veyron/lib/testutil"
@@ -584,8 +586,17 @@
*envelope = envelopeFromShell(sh, []string{testEnvVarName + "=env-val-envelope"}, appCmd, "google naps", fmt.Sprintf("--%s=flag-val-envelope", testFlagName), "appV1")
// Install the app. The config-specified flag value for testFlagName
- // should override the value specified in the envelope above.
- appID := installApp(t, ctx, device.Config{testFlagName: "flag-val-install"})
+ // should override the value specified in the envelope above, and the
+ // config-specified value for origin should override the value in the
+ // Install rpc argument.
+ mtName, ok := sh.GetVar(consts.NamespaceRootPrefix)
+ if !ok {
+ t.Fatalf("failed to get namespace root var from shell")
+ }
+ // This rooted name should be equivalent to the relative name "ar", but
+ // we want to test that the config override for origin works.
+ rootedAppRepoName := naming.Join(mtName, "ar")
+ appID := installApp(t, ctx, device.Config{testFlagName: "flag-val-install", mgmt.AppOriginConfigKey: rootedAppRepoName})
installationDebug := debug(t, ctx, appID)
// We spot-check a couple pieces of information we expect in the debug
// output.
@@ -593,7 +604,7 @@
// logic that assumes too much about the format? This may be one
// argument in favor of making the output of Debug a struct instead of
// free-form string.
- if !strings.Contains(installationDebug, "Origin: ar") {
+ if !strings.Contains(installationDebug, fmt.Sprintf("Origin: %v", rootedAppRepoName)) {
t.Fatalf("debug response doesn't contain expected info: %v", installationDebug)
}
if !strings.Contains(installationDebug, "Config: map[random_test_flag:flag-val-install]") {
diff --git a/tools/mgmt/device/doc.go b/tools/mgmt/device/doc.go
index b760e71..c8f4780 100644
--- a/tools/mgmt/device/doc.go
+++ b/tools/mgmt/device/doc.go
@@ -75,21 +75,23 @@
Install the given application.
Usage:
- device install <device> <application> [<config override>]
+ device install [flags] <device> <application>
<device> is the veyron object name of the device manager's app service.
<application> is the veyron object name of the application.
-<config override> is an optional JSON-encoded device.Config object, of the form:
- '{"flag1":"value1","flag2":"value2"}'.
+The device install flags are:
+ -config={}
+ JSON-encoded device.Config object, of the form:
+ '{"flag1":"value1","flag2":"value2"}'
Device Install-Local
Install the given application, specified using a local path.
Usage:
- device install-local <device> <title> [ENV=VAL ...] binary [--flag=val ...]
+ device install-local [flags] <device> <title> [ENV=VAL ...] binary [--flag=val ...]
<device> is the veyron object name of the device manager's app service.
@@ -98,6 +100,11 @@
This is followed by an arbitrary number of environment variable settings, the
local path for the binary to install, and arbitrary flag settings.
+The device install-local flags are:
+ -config={}
+ JSON-encoded device.Config object, of the form:
+ '{"flag1":"value1","flag2":"value2"}'
+
Device Start
Start an instance of the given application.
diff --git a/tools/mgmt/device/impl/impl.go b/tools/mgmt/device/impl/impl.go
index 8762b8a..2600b4e 100644
--- a/tools/mgmt/device/impl/impl.go
+++ b/tools/mgmt/device/impl/impl.go
@@ -17,29 +17,43 @@
Name: "install",
Short: "Install the given application.",
Long: "Install the given application.",
- ArgsName: "<device> <application> [<config override>]",
+ ArgsName: "<device> <application>",
ArgsLong: `
<device> is the veyron object name of the device manager's app service.
<application> is the veyron object name of the application.
+`,
+}
-<config override> is an optional JSON-encoded device.Config object, of the form:
- '{"flag1":"value1","flag2":"value2"}'.`,
+type configFlag device.Config
+
+func (c *configFlag) String() string {
+ jsonConfig, _ := json.Marshal(c)
+ return string(jsonConfig)
+}
+func (c *configFlag) Set(s string) error {
+ if err := json.Unmarshal([]byte(s), c); err != nil {
+ return fmt.Errorf("Unmarshal(%v) failed: %v", s, err)
+ }
+ return nil
+}
+
+var configOverride configFlag = configFlag{}
+
+func init() {
+ cmdInstall.Flags.Var(&configOverride, "config", "JSON-encoded device.Config object, of the form: '{\"flag1\":\"value1\",\"flag2\":\"value2\"}'")
}
func runInstall(cmd *cmdline.Command, args []string) error {
- if expectedMin, expectedMax, got := 2, 3, len(args); expectedMin > got || expectedMax < got {
- return cmd.UsageErrorf("install: incorrect number of arguments, expected between %d and %d, got %d", expectedMin, expectedMax, got)
+ if expected, got := 2, len(args); expected != got {
+ return cmd.UsageErrorf("install: incorrect number of arguments, expected %d, got %d", expected, got)
}
deviceName, appName := args[0], args[1]
- var cfg device.Config
- if len(args) > 2 {
- jsonConfig := args[2]
- if err := json.Unmarshal([]byte(jsonConfig), &cfg); err != nil {
- return fmt.Errorf("Unmarshal(%v) failed: %v", jsonConfig, err)
- }
- }
- appID, err := device.ApplicationClient(deviceName).Install(gctx, appName, cfg)
+ appID, err := device.ApplicationClient(deviceName).Install(gctx, appName, device.Config(configOverride))
+ // Reset the value for any future invocations of "install" or
+ // "install-local" (we run more than one command per process in unit
+ // tests).
+ configOverride = configFlag{}
if err != nil {
return fmt.Errorf("Install failed: %v", err)
}
diff --git a/tools/mgmt/device/impl/impl_test.go b/tools/mgmt/device/impl/impl_test.go
index 435d185..09e0a83 100644
--- a/tools/mgmt/device/impl/impl_test.go
+++ b/tools/mgmt/device/impl/impl_test.go
@@ -190,35 +190,35 @@
expectedTape interface{}
}{
{
- []string{"install", "blech"},
+ []string{"blech"},
nil,
true,
nil,
nil,
},
{
- []string{"install", "blech1", "blech2", "blech3", "blech4"},
+ []string{"blech1", "blech2", "blech3", "blech4"},
nil,
true,
nil,
nil,
},
{
- []string{"install", deviceName, appNameNoFetch, "not-valid-json"},
+ []string{deviceName, appNameNoFetch, "not-valid-json"},
nil,
true,
nil,
nil,
},
{
- []string{"install", deviceName, appNameNoFetch},
+ []string{deviceName, appNameNoFetch},
nil,
false,
InstallResponse{appId, nil},
InstallStimulus{"Install", appNameNoFetch, nil, application.Envelope{}, 0},
},
{
- []string{"install", deviceName, appNameNoFetch},
+ []string{deviceName, appNameNoFetch},
cfg,
false,
InstallResponse{appId, nil},
@@ -231,8 +231,9 @@
if err != nil {
t.Fatalf("test case %d: Marshal(%v) failed: %v", i, c.config, err)
}
- c.args = append(c.args, string(jsonConfig))
+ c.args = append([]string{fmt.Sprintf("--config=%s", string(jsonConfig))}, c.args...)
}
+ c.args = append([]string{"install"}, c.args...)
err := cmd.Execute(c.args)
if c.shouldErr {
if err == nil {
diff --git a/tools/mgmt/device/impl/local_install.go b/tools/mgmt/device/impl/local_install.go
index a8b8fb9..1598559 100644
--- a/tools/mgmt/device/impl/local_install.go
+++ b/tools/mgmt/device/impl/local_install.go
@@ -23,9 +23,6 @@
"v.io/lib/cmdline"
)
-// TODO(caprita): Add a way to provide an origin for the app, so we can do
-// updates after it's been installed.
-
var cmdInstallLocal = &cmdline.Command{
Run: runInstallLocal,
Name: "install-local",
@@ -41,6 +38,10 @@
local path for the binary to install, and arbitrary flag settings.`,
}
+func init() {
+ cmdInstallLocal.Flags.Var(&configOverride, "config", "JSON-encoded device.Config object, of the form: '{\"flag1\":\"value1\",\"flag2\":\"value2\"}'")
+}
+
type openAuthorizer struct{}
func (openAuthorizer) Authorize(security.Context) error { return nil }
@@ -226,7 +227,11 @@
objects["application"] = repository.ApplicationServer(envelopeInvoker(envelope))
appName := naming.Join(name, "application")
- appID, err := device.ApplicationClient(deviceName).Install(gctx, appName, nil)
+ appID, err := device.ApplicationClient(deviceName).Install(gctx, appName, device.Config(configOverride))
+ // Reset the value for any future invocations of "install" or
+ // "install-local" (we run more than one command per process in unit
+ // tests).
+ configOverride = configFlag{}
if err != nil {
return fmt.Errorf("Install failed: %v", err)
}
diff --git a/tools/mgmt/device/impl/local_install_test.go b/tools/mgmt/device/impl/local_install_test.go
index 7abb65f..9a65931 100644
--- a/tools/mgmt/device/impl/local_install_test.go
+++ b/tools/mgmt/device/impl/local_install_test.go
@@ -2,6 +2,7 @@
import (
"bytes"
+ "encoding/json"
"fmt"
"os"
"reflect"
@@ -10,6 +11,7 @@
"v.io/core/veyron2/naming"
"v.io/core/veyron2/services/mgmt/application"
+ "v.io/core/veyron2/services/mgmt/device"
"v.io/core/veyron/tools/mgmt/device/impl"
)
@@ -35,18 +37,19 @@
stderrSubstr string
}{
{
- []string{"install-local", deviceName}, "incorrect number of arguments",
+ []string{deviceName}, "incorrect number of arguments",
},
{
- []string{"install-local", deviceName, appTitle}, "missing binary",
+ []string{deviceName, appTitle}, "missing binary",
},
{
- []string{"install-local", deviceName, appTitle, "a=b"}, "missing binary",
+ []string{deviceName, appTitle, "a=b"}, "missing binary",
},
{
- []string{"install-local", deviceName, appTitle, "foo"}, "binary foo not found",
+ []string{deviceName, appTitle, "foo"}, "binary foo not found",
},
} {
+ c.args = append([]string{"install-local"}, c.args...)
if err := cmd.Execute(c.args); err == nil {
t.Fatalf("test case %d: wrongly failed to receive a non-nil error.", i)
} else {
@@ -69,20 +72,37 @@
t.Fatalf("Failed to stat %v: %v", binary, err)
}
binarySize := fi.Size()
+ cfg := device.Config{"someflag": "somevalue"}
for i, c := range []struct {
args []string
+ config device.Config
expectedTape interface{}
}{
{
- []string{"install-local", deviceName, appTitle, binary},
+ []string{deviceName, appTitle, binary},
+ nil,
InstallStimulus{"Install", appNameAfterFetch, nil, application.Envelope{Title: appTitle, Binary: binaryNameAfterFetch}, binarySize},
},
{
- []string{"install-local", deviceName, appTitle, "ENV1=V1", "ENV2=V2", binary, "FLAG1=V1", "FLAG2=V2"},
+ []string{deviceName, appTitle, binary},
+ cfg,
+ InstallStimulus{"Install", appNameAfterFetch, cfg, application.Envelope{Title: appTitle, Binary: binaryNameAfterFetch}, binarySize},
+ },
+ {
+ []string{deviceName, appTitle, "ENV1=V1", "ENV2=V2", binary, "FLAG1=V1", "FLAG2=V2"},
+ nil,
InstallStimulus{"Install", appNameAfterFetch, nil, application.Envelope{Title: appTitle, Binary: binaryNameAfterFetch, Env: []string{"ENV1=V1", "ENV2=V2"}, Args: []string{"FLAG1=V1", "FLAG2=V2"}}, binarySize},
},
} {
tape.SetResponses([]interface{}{InstallResponse{appId, nil}})
+ if c.config != nil {
+ jsonConfig, err := json.Marshal(c.config)
+ if err != nil {
+ t.Fatalf("test case %d: Marshal(%v) failed: %v", i, c.config, err)
+ }
+ c.args = append([]string{fmt.Sprintf("--config=%s", string(jsonConfig))}, c.args...)
+ }
+ c.args = append([]string{"install-local"}, c.args...)
if err := cmd.Execute(c.args); err != nil {
t.Fatalf("test case %d: %v", i, err)
}