summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Heon <matthew.heon@gmail.com>2018-06-21 09:45:03 -0400
committerAtomic Bot <atomic-devel@projectatomic.io>2018-06-22 19:26:46 +0000
commit334345671759d521ee6fbe7f6e1e7392ee312ab4 (patch)
tree0478ad55c1430af8834ac94a8306272499fb98a2
parent7a7d0f1446590f5895869d26f7dc9893fa5be3a2 (diff)
downloadpodman-334345671759d521ee6fbe7f6e1e7392ee312ab4.tar.gz
podman-334345671759d521ee6fbe7f6e1e7392ee312ab4.tar.bz2
podman-334345671759d521ee6fbe7f6e1e7392ee312ab4.zip
Add Refresh() to ctrs to refresh state after db change
The Refresh() function is used to reset a container's state after a database format change to state is made that requires migration Signed-off-by: Matthew Heon <matthew.heon@gmail.com> Closes: #981 Approved by: baude
-rw-r--r--libpod/boltdb_state.go12
-rw-r--r--libpod/container_api.go121
-rw-r--r--libpod/container_internal.go43
3 files changed, 151 insertions, 25 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
index 7880265b6..3ee24122a 100644
--- a/libpod/boltdb_state.go
+++ b/libpod/boltdb_state.go
@@ -174,15 +174,9 @@ func (s *BoltState) Refresh() error {
return errors.Wrapf(err, "error unmarshalling state for container %s", string(id))
}
- state.PID = 0
- state.Mountpoint = ""
- state.Mounted = false
- state.State = ContainerStateConfigured
- state.ExecSessions = make(map[string]*ExecSession)
- state.IPs = nil
- state.Interfaces = nil
- state.Routes = nil
- state.BindMounts = make(map[string]string)
+ if err := resetState(state); err != nil {
+ return errors.Wrapf(err, "error resetting state for container %s", string(id))
+ }
newStateBytes, err := json.Marshal(state)
if err != nil {
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 7613c8ea5..d181af2a8 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -477,15 +477,8 @@ func (c *Container) Pause() error {
if c.state.State != ContainerStateRunning {
return errors.Wrapf(ErrCtrStateInvalid, "%q is not running, can't pause", c.state.State)
}
- if err := c.runtime.ociRuntime.pauseContainer(c); err != nil {
- return err
- }
-
- logrus.Debugf("Paused container %s", c.ID())
- c.state.State = ContainerStatePaused
-
- return c.save()
+ return c.pause()
}
// Unpause unpauses a container
@@ -502,15 +495,8 @@ func (c *Container) Unpause() error {
if c.state.State != ContainerStatePaused {
return errors.Wrapf(ErrCtrStateInvalid, "%q is not paused, can't unpause", c.ID())
}
- if err := c.runtime.ociRuntime.unpauseContainer(c); err != nil {
- return err
- }
-
- logrus.Debugf("Unpaused container %s", c.ID())
-
- c.state.State = ContainerStateRunning
- return c.save()
+ return c.unpause()
}
// Export exports a container's root filesystem as a tar archive
@@ -753,3 +739,106 @@ func (c *Container) RestartWithTimeout(ctx context.Context, timeout uint) (err e
return c.start()
}
+
+// Refresh refreshes a container's state in the database, restarting the
+// container if it is running
+func (c *Container) Refresh(ctx context.Context) error {
+ if !c.batched {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if err := c.syncContainer(); err != nil {
+ return err
+ }
+ }
+
+ wasCreated := false
+ if c.state.State == ContainerStateCreated {
+ wasCreated = true
+ }
+ wasRunning := false
+ if c.state.State == ContainerStateRunning {
+ wasRunning = true
+ }
+ wasPaused := false
+ if c.state.State == ContainerStatePaused {
+ wasPaused = true
+ }
+
+ // First, unpause the container if it's paused
+ if c.state.State == ContainerStatePaused {
+ if err := c.unpause(); err != nil {
+ return err
+ }
+ }
+
+ // Next, if the container is running, stop it
+ if c.state.State == ContainerStateRunning {
+ if err := c.stop(c.config.StopTimeout); err != nil {
+ return err
+ }
+ }
+
+ // If there are active exec sessions, we need to kill them
+ if len(c.state.ExecSessions) > 0 {
+ logrus.Infof("Killing %d exec sessions in container %s. They will not be restored after refresh.",
+ len(c.state.ExecSessions), c.ID())
+ if err := c.runtime.ociRuntime.execStopContainer(c, c.config.StopTimeout); err != nil {
+ return err
+ }
+ }
+
+ // If the container is in ContainerStateStopped, we need to delete it
+ // from the runtime and clear conmon state
+ if c.state.State == ContainerStateStopped {
+ if err := c.delete(ctx); err != nil {
+ return err
+ }
+ if err := c.removeConmonFiles(); err != nil {
+ return err
+ }
+ }
+
+ // Fire cleanup code one more time unconditionally to ensure we are good
+ // to refresh
+ if err := c.cleanup(); err != nil {
+ return err
+ }
+
+ // We've finished unwinding the container back to its initial state
+ // Now safe to refresh container state
+ if err := resetState(c.state); err != nil {
+ return errors.Wrapf(err, "error resetting state of container %s", c.ID())
+ }
+ if err := c.refresh(); err != nil {
+ return err
+ }
+
+ logrus.Debugf("Successfully refresh container %s state")
+
+ // Initialize the container if it was created in runc
+ if wasCreated || wasRunning || wasPaused {
+ if err := c.prepare(); err != nil {
+ return err
+ }
+ if err := c.init(ctx); err != nil {
+ return err
+ }
+ }
+
+ // If the container was running before, start it
+ if wasRunning || wasPaused {
+ if err := c.start(); err != nil {
+ return err
+ }
+ }
+
+ // If the container was paused before, re-pause it
+ if wasPaused {
+ if err := c.pause(); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index f3be6f73b..fee13953c 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -316,6 +316,23 @@ func (c *Container) teardownStorage() error {
return nil
}
+// Reset resets state fields to default values
+// It is performed before a refresh and clears the state after a reboot
+// It does not save the results - assumes the database will do that for us
+func resetState(state *containerState) error {
+ state.PID = 0
+ state.Mountpoint = ""
+ state.Mounted = false
+ state.State = ContainerStateConfigured
+ state.ExecSessions = make(map[string]*ExecSession)
+ state.IPs = nil
+ state.Interfaces = nil
+ state.Routes = nil
+ state.BindMounts = make(map[string]string)
+
+ return nil
+}
+
// Refresh refreshes the container's state after a restart
func (c *Container) refresh() error {
c.lock.Lock()
@@ -681,6 +698,32 @@ func (c *Container) stop(timeout uint) error {
return c.cleanup()
}
+// Internal, non-locking function to pause a container
+func (c *Container) pause() error {
+ if err := c.runtime.ociRuntime.pauseContainer(c); err != nil {
+ return err
+ }
+
+ logrus.Debugf("Paused container %s", c.ID())
+
+ c.state.State = ContainerStatePaused
+
+ return c.save()
+}
+
+// Internal, non-locking function to unpause a container
+func (c *Container) unpause() error {
+ if err := c.runtime.ociRuntime.unpauseContainer(c); err != nil {
+ return err
+ }
+
+ logrus.Debugf("Unpaused container %s", c.ID())
+
+ c.state.State = ContainerStateRunning
+
+ return c.save()
+}
+
// mountStorage sets up the container's root filesystem
// It mounts the image and any other requested mounts
// TODO: Add ability to override mount label so we can use this for Mount() too