Merge "services/device/device: updateall: kill instance only if running; recover if err"
diff --git a/services/device/device/doc.go b/services/device/device/doc.go
index 53c9a17..3fe77dd 100644
--- a/services/device/device/doc.go
+++ b/services/device/device/doc.go
@@ -295,7 +295,7 @@
<object name> is the vanadium object name to update, as follows:
<devicename>/apps/apptitle/installationid/instanceid: updates the given
-instance, suspending/resuming it if running
+instance, killing/restarting it if running
<devicename>/apps/apptitle/installationid: updates the given installation and
then all its instances
diff --git a/services/device/device/instance_impl.go b/services/device/device/instance_impl.go
index 8f20c8d..7a3febd 100644
--- a/services/device/device/instance_impl.go
+++ b/services/device/device/instance_impl.go
@@ -37,6 +37,8 @@
return nil
}
+const killDeadline = 10 * time.Second
+
var cmdKill = &cmdline.Command{
Run: runKill,
Name: "kill",
@@ -53,7 +55,7 @@
}
appName := args[0]
- if err := device.ApplicationClient(appName).Kill(gctx, 5*time.Second); err != nil {
+ if err := device.ApplicationClient(appName).Kill(gctx, killDeadline); err != nil {
return fmt.Errorf("Kill failed: %v", err)
}
fmt.Fprintf(cmd.Stdout(), "Kill succeeded\n")
diff --git a/services/device/device/instance_impl_test.go b/services/device/device/instance_impl_test.go
index e58d1e1..fdbe781 100644
--- a/services/device/device/instance_impl_test.go
+++ b/services/device/device/instance_impl_test.go
@@ -68,7 +68,7 @@
t.Fatalf("Unexpected output from list. Got %q, expected %q", got, expected)
}
expected := []interface{}{
- KillStimulus{"Kill", 5 * time.Second},
+ KillStimulus{"Kill", 10 * time.Second},
}
if got := appTape.Play(); !reflect.DeepEqual(expected, got) {
t.Errorf("invalid call sequence. Got %v, want %v", got, expected)
diff --git a/services/device/device/updateall.go b/services/device/device/updateall.go
index 4a4e57f..c660863 100644
--- a/services/device/device/updateall.go
+++ b/services/device/device/updateall.go
@@ -33,7 +33,7 @@
ArgsLong: `
<object name> is the vanadium object name to update, as follows:
-<devicename>/apps/apptitle/installationid/instanceid: updates the given instance, suspending/resuming it if running
+<devicename>/apps/apptitle/installationid/instanceid: updates the given instance, killing/restarting it if running
<devicename>/apps/apptitle/installationid: updates the given installation and then all its instances
@@ -83,6 +83,18 @@
return nil
}
+func instanceIsRunning(von string) (bool, error) {
+ status, err := device.ApplicationClient(von).Status(gctx)
+ if err != nil {
+ return false, fmt.Errorf("Failed to get status for instance %q: %v", von, err)
+ }
+ s, ok := status.(device.StatusInstance)
+ if !ok {
+ return false, fmt.Errorf("Status for instance %q of wrong type (%T)", von, status)
+ }
+ return s.Value.State == device.InstanceStateRunning, nil
+}
+
func updateInstance(cmd *cmdline.Command, von string) (retErr error) {
defer func() {
if retErr == nil {
@@ -92,9 +104,27 @@
fmt.Fprintf(cmd.Stderr(), "ERROR: %v.\n", retErr)
}
}()
- // Try killing the app in case it was running.
- switch err := device.ApplicationClient(von).Kill(gctx, 5*time.Second); {
- case err == nil:
+ running, err := instanceIsRunning(von)
+ if err != nil {
+ return err
+ }
+ if running {
+ // Try killing the app.
+ if err := device.ApplicationClient(von).Kill(gctx, killDeadline); err != nil {
+ // Check the app's state again in case we killed it,
+ // nevermind any errors. The sleep is because Kill
+ // currently (4/29/15) returns asynchronously with the
+ // device manager shooting the app down.
+ time.Sleep(time.Second)
+ running, rerr := instanceIsRunning(von)
+ if rerr != nil {
+ return rerr
+ }
+ if running {
+ return fmt.Errorf("failed to kill instance %q: %v", von, err)
+ }
+ fmt.Fprintf(cmd.Stderr(), "Kill(%s) returned an error (%s) but app is now not running.\n", von, err)
+ }
// App was running, and we killed it.
defer func() {
// Re-start the instance.
@@ -107,21 +137,14 @@
}
}
}()
- case verror.ErrorID(err) == deviceimpl.ErrInvalidOperation.ID:
- // App was likely not running.
- //
- // TODO(caprita): change returned error to distinguish no-op
- // app sttate transitions from failed state transitions.
- default:
- return fmt.Errorf("failed to suspend instance %q: %v.\n", von, err)
}
// Update the instance.
switch err := device.ApplicationClient(von).Update(gctx); {
case err == nil:
return nil
case verror.ErrorID(err) == deviceimpl.ErrUpdateNoOp.ID:
- // TODO(caprita): Ideally, we wouldn't even attempt a suspend /
- // resume if there's no newer version of the application.
+ // TODO(caprita): Ideally, we wouldn't even attempt a kill /
+ // restart if there's no newer version of the application.
fmt.Fprintf(cmd.Stdout(), "Instance %q already up to date.\n", von)
return nil
default: