tremplin: Implement StopContainer RPC.
This allows us to stop individual containers on a VM without
taking down the whole VM.
BUG=chromium:1261319
TEST=Manual testing
Cq-Depend: chromium:3325825
Change-Id: I7133ad60a37c1488625242f558d6fa9db94bfc52
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/tremplin/+/3325709
Reviewed-by: David Munro <davidmunro@google.com>
Tested-by: Nicholas Verne <nverne@chromium.org>
Commit-Queue: Nicholas Verne <nverne@chromium.org>
diff --git a/src/chromiumos/tremplin/tremplin.go b/src/chromiumos/tremplin/tremplin.go
index 6021655..2568d99 100644
--- a/src/chromiumos/tremplin/tremplin.go
+++ b/src/chromiumos/tremplin/tremplin.go
@@ -321,7 +321,7 @@
}
// stopContainer stops the specified container.
-func (s *tremplinServer) stopContainer(containerName string) error {
+func (s *tremplinServer) stopContainer(containerName string, wait bool) (lxd.Operation, error) {
reqState := api.ContainerStatePut{
Action: "stop",
Timeout: -1,
@@ -330,12 +330,15 @@
log.Printf("stopContainer name: %v lxd: %v", containerName, s.lxd)
op, err := s.lxd.UpdateContainerState(containerName, reqState, "")
if err != nil {
- return fmt.Errorf("error calling stop container %s: %v", containerName, err)
+ return nil, fmt.Errorf("error calling stop container %s: %v", containerName, err)
+ }
+ if !wait {
+ return op, nil
}
if err = op.Wait(); err != nil {
- return fmt.Errorf("error waiting to stop container %s: %v", containerName, err)
+ return nil, fmt.Errorf("error waiting to stop container %s: %v", containerName, err)
}
- return nil
+ return nil, nil
}
// writeContainerFile writes content to fileName in containerName.
@@ -463,7 +466,7 @@
// Stop <container>-crostini-remap.
log.Printf("Remap: stop %s", remapName)
- err = s.stopContainer(remapName)
+ _, err = s.stopContainer(remapName, true)
if err != nil {
return nil, "", err
}
@@ -996,12 +999,7 @@
}
if container.StatusCode == api.Running {
- reqState := api.ContainerStatePut{
- Action: "stop",
- Timeout: -1,
- Force: true,
- }
- op, err := s.lxd.UpdateContainerState(container.Name, reqState, "")
+ op, err := s.stopContainer(container.Name, false)
if err != nil {
response.Status = pb.DeleteContainerResponse_FAILED
@@ -1029,6 +1027,70 @@
return response, nil
}
+// StopContainer implements tremplin.StopContainer.
+func (s *tremplinServer) StopContainer(ctx context.Context, in *pb.StopContainerRequest) (*pb.StopContainerResponse, error) {
+ log.Printf("Received StopContainer RPC: %s", in.ContainerName)
+
+ response := &pb.StopContainerResponse{}
+
+ container, _, err := s.lxd.GetContainer(in.ContainerName)
+ if container == nil {
+ response.Status = pb.StopContainerResponse_DOES_NOT_EXIST
+ return response, nil
+ }
+ if err != nil {
+ response.Status = pb.StopContainerResponse_FAILED
+ response.FailureReason = fmt.Sprintf("failed to find container: %v", err)
+ return response, nil
+ }
+
+ sendStatus := func(op api.Operation) {
+ req := &pb.ContainerStopProgress{ContainerName: container.Name}
+ switch op.StatusCode {
+ case api.Pending, api.Running:
+ req.Status = pb.ContainerStopProgress_STOPPING
+ case api.Success:
+ req.Status = pb.ContainerStopProgress_STOPPED
+ case api.Cancelled:
+ req.Status = pb.ContainerStopProgress_CANCELLED
+ req.FailureReason = op.Err
+ case api.Failure:
+ req.Status = pb.ContainerStopProgress_FAILED
+ req.FailureReason = op.Err
+ default:
+ req.Status = pb.ContainerStopProgress_UNKNOWN
+ req.FailureReason = fmt.Sprintf("unhandled stop status: %s, %s", op.Status, op.Err)
+ }
+ if _, err := s.listenerClient.UpdateStopStatus(context.Background(), req); err != nil {
+ log.Printf("Could not update stop status on host: %v", err)
+ }
+
+ }
+
+ if container.StatusCode == api.Running {
+ op, err := s.stopContainer(container.Name, false)
+
+ if err != nil {
+ response.Status = pb.StopContainerResponse_FAILED
+ response.FailureReason = fmt.Sprintf("failed to stop container: %v", err)
+ return response, nil
+ }
+
+ _, err = op.AddHandler(sendStatus)
+
+ if err != nil {
+ response.Status = pb.StopContainerResponse_FAILED
+ response.FailureReason = fmt.Sprintf("failed to add stop operation handler: %v", err)
+ return response, nil
+ }
+ response.Status = pb.StopContainerResponse_STOPPING
+ } else {
+ response.Status = pb.StopContainerResponse_STOPPED
+ }
+
+ return response, nil
+}
+
func (s *tremplinServer) handleStopOperation(containerName string, op api.Operation) {
req := &pb.ContainerDeletionProgress{
ContainerName: containerName,
@@ -1723,7 +1785,7 @@
}
if c.StatusCode != 0 && c.StatusCode != api.Stopped {
log.Printf("Force stopping container %s before deleting", containerName)
- err := s.stopContainer(containerName)
+ _, err := s.stopContainer(containerName, true)
if err != nil {
return err
}
diff --git a/src/chromiumos/tremplin/upgrade_container.go b/src/chromiumos/tremplin/upgrade_container.go
index 64d9061..e2a60e8 100644
--- a/src/chromiumos/tremplin/upgrade_container.go
+++ b/src/chromiumos/tremplin/upgrade_container.go
@@ -19,7 +19,7 @@
func (s *tremplinServer) shutDownContainer(containerName string) error {
log.Printf("shutdown")
- err := s.stopContainer(containerName)
+ _, err := s.stopContainer(containerName, true)
if err != nil {
return fmt.Errorf("Failed to stop container %v with error %v", containerName, err)
}