From ae5aac50aaacbdcc20e622e86f40c02136690fe7 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Fri, 1 Dec 2017 13:26:58 -0500 Subject: Add handling for system restart in libpod Signed-off-by: Matthew Heon --- libpod/container.go | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++-- libpod/runtime.go | 45 ++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 2 deletions(-) (limited to 'libpod') diff --git a/libpod/container.go b/libpod/container.go index bd7455147..e1b002548 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -407,8 +407,54 @@ func (c *Container) teardownStorage() error { return nil } +// Refresh refreshes the container's state after a restart +func (c *Container) refresh() error { + c.lock.Lock() + defer c.lock.Unlock() + + if !c.valid { + return errors.Wrapf(ErrCtrRemoved, "container %s has been removed from the state", c.ID()) + } + + // We need to get the container's temporary directory from c/storage + // It was lost in the reboot and must be recreated + dir, err := c.runtime.storageService.GetRunDir(c.ID()) + if err != nil { + return errors.Wrapf(err, "error retrieving temporary directory for container %s", c.ID()) + } + c.state.RunDir = dir + + // The container is no longer mounted + c.state.Mounted = false + c.state.Mountpoint = "" + + // The container is no longe running + c.state.PID = 0 + + // Check the container's state. If it's not created in runc yet, we're + // done + if c.state.State == ContainerStateConfigured { + if err := c.runtime.state.SaveContainer(c); err != nil { + return errors.Wrapf(err, "error refreshing state for container %s", c.ID()) + } + + return nil + } + + // The container must be recreated in runc + if err := c.init(); err != nil { + return err + } + + if err := c.runtime.state.SaveContainer(c); err != nil { + return errors.Wrapf(err, "error refreshing state for container %s", c.ID()) + } + + return nil +} + // Init creates a container in the OCI runtime -func (c *Container) Init() (err error) { +func (c *Container) Init() error { c.lock.Lock() defer c.lock.Unlock() @@ -420,7 +466,12 @@ func (c *Container) Init() (err error) { return errors.Wrapf(ErrCtrExists, "container %s has already been created in runtime", c.ID()) } - // Mount storage for the container + return c.init() +} + +// Creates container in OCI runtime +// Internal only - does not lock or check state +func (c *Container) init() (err error) { if err := c.mountStorage(); err != nil { return err } @@ -436,6 +487,17 @@ func (c *Container) Init() (err error) { // Save the OCI spec to disk jsonPath := filepath.Join(c.bundlePath(), "config.json") + // If the OCI spec already exists, replace it + if _, err := os.Stat(jsonPath); err != nil { + if !os.IsNotExist(err) { + return errors.Wrapf(err, "error doing stat on container %s spec", c.ID()) + } + } else { + // No error, the spec exists. Remove so we can replace. + if err := os.Remove(jsonPath); err != nil { + return errors.Wrapf(err, "error replacing spec of container %s", c.ID()) + } + } fileJSON, err := json.Marshal(c.runningSpec) if err != nil { return errors.Wrapf(err, "error exporting runtime spec for container %s to JSON", c.ID()) diff --git a/libpod/runtime.go b/libpod/runtime.go index b86e06546..7997679e2 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -173,6 +173,24 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) { runtime.state = state } + // We now need to see if the system has restarted + // We check for the presence of a file in our tmp directory to verify this + runtimeAliveFile := filepath.Join(runtime.config.TmpDir, "alive") + _, err = os.Stat(runtimeAliveFile) + if err != nil { + // If the file doesn't exist, we need to refresh the state + // This will trigger on first use as well, but refreshing an + // empty state only creates a single file + // As such, it's not really a performance concern + if os.IsNotExist(err) { + if err2 := runtime.refresh(runtimeAliveFile); err2 != nil { + return nil, err2 + } + } else { + return nil, errors.Wrapf(err, "error reading runtime status file %s", runtimeAliveFile) + } + } + // Mark the runtime as valid - ready to be used, cannot be modified // further runtime.valid = true @@ -238,3 +256,30 @@ func (r *Runtime) Shutdown(force bool) error { return lastError } + +// Reconfigures the runtime after a reboot +// Refreshes the state, recreating temporary files +// Does not check validity as the runtime is not valid until after this has run +// TODO: there's a potential race here, where multiple libpods could be in this +// function before the runtime ready file is created +// This probably doesn't matter as the actual container operations are locked +func (r *Runtime) refresh(alivePath string) error { + // We need to refresh the state of all containers + ctrs, err := r.state.AllContainers() + if err != nil { + return errors.Wrapf(err, "error retrieving all containers from state") + } + for _, ctr := range ctrs { + if err := ctr.refresh(); err != nil { + return err + } + } + + file, err := os.OpenFile(alivePath, os.O_RDONLY|os.O_CREATE, 0644) + if err != nil { + return errors.Wrapf(err, "error creating runtime status file %s", alivePath) + } + defer file.Close() + + return nil +} -- cgit v1.2.3-54-g00ecf