summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/container_api.go18
-rw-r--r--libpod/container_internal_linux.go62
-rw-r--r--libpod/diff.go56
3 files changed, 122 insertions, 14 deletions
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 3577b8e8c..6f530f75f 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -305,6 +305,11 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir
if err != nil {
if exited {
// If the runtime exited, propagate the error we got from the process.
+ // We need to remove PID files to ensure no memory leaks
+ if err2 := os.Remove(pidFile); err2 != nil {
+ logrus.Errorf("Error removing exit file for container %s exec session %s: %v", c.ID(), sessionID, err2)
+ }
+
return err
}
return errors.Wrapf(err, "timed out waiting for runtime to create pidfile for exec session in container %s", c.ID())
@@ -312,6 +317,10 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir
// Pidfile exists, read it
contents, err := ioutil.ReadFile(pidFile)
+ // We need to remove PID files to ensure no memory leaks
+ if err2 := os.Remove(pidFile); err2 != nil {
+ logrus.Errorf("Error removing exit file for container %s exec session %s: %v", c.ID(), sessionID, err2)
+ }
if err != nil {
// We don't know the PID of the exec session
// However, it may still be alive
@@ -792,15 +801,16 @@ type ContainerCheckpointOptions struct {
// TCPEstablished tells the API to checkpoint a container
// even if it contains established TCP connections
TCPEstablished bool
- // Export tells the API to write the checkpoint image to
- // the filename set in TargetFile
- // Import tells the API to read the checkpoint image from
- // the filename set in TargetFile
+ // TargetFile tells the API to read (or write) the checkpoint image
+ // from (or to) the filename set in TargetFile
TargetFile string
// Name tells the API that during restore from an exported
// checkpoint archive a new name should be used for the
// restored container
Name string
+ // IgnoreRootfs tells the API to not export changes to
+ // the container's root file-system (or to not import)
+ IgnoreRootfs bool
}
// Checkpoint checkpoints a container
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index aa477611f..399220b9a 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -510,21 +510,44 @@ func (c *Container) addNamespaceContainer(g *generate.Generator, ns LinuxNS, ctr
return nil
}
-func (c *Container) exportCheckpoint(dest string) (err error) {
+func (c *Container) exportCheckpoint(dest string, ignoreRootfs bool) (err error) {
if (len(c.config.NamedVolumes) > 0) || (len(c.Dependencies()) > 0) {
return errors.Errorf("Cannot export checkpoints of containers with named volumes or dependencies")
}
logrus.Debugf("Exporting checkpoint image of container %q to %q", c.ID(), dest)
+
+ includeFiles := []string{
+ "checkpoint",
+ "artifacts",
+ "ctr.log",
+ "config.dump",
+ "spec.dump",
+ "network.status"}
+
+ // Get root file-system changes included in the checkpoint archive
+ rootfsDiffPath := filepath.Join(c.bundlePath(), "rootfs-diff.tar")
+ if !ignoreRootfs {
+ rootfsDiffFile, err := os.Create(rootfsDiffPath)
+ if err != nil {
+ return errors.Wrapf(err, "error creating root file-system diff file %q", rootfsDiffPath)
+ }
+ tarStream, err := c.runtime.GetDiffTarStream("", c.ID())
+ if err != nil {
+ return errors.Wrapf(err, "error exporting root file-system diff to %q", rootfsDiffPath)
+ }
+ _, err = io.Copy(rootfsDiffFile, tarStream)
+ if err != nil {
+ return errors.Wrapf(err, "error exporting root file-system diff to %q", rootfsDiffPath)
+ }
+ tarStream.Close()
+ rootfsDiffFile.Close()
+ includeFiles = append(includeFiles, "rootfs-diff.tar")
+ }
+
input, err := archive.TarWithOptions(c.bundlePath(), &archive.TarOptions{
Compression: archive.Gzip,
IncludeSourceDir: true,
- IncludeFiles: []string{
- "checkpoint",
- "artifacts",
- "ctr.log",
- "config.dump",
- "spec.dump",
- "network.status"},
+ IncludeFiles: includeFiles,
})
if err != nil {
@@ -546,6 +569,8 @@ func (c *Container) exportCheckpoint(dest string) (err error) {
return err
}
+ os.Remove(rootfsDiffPath)
+
return nil
}
@@ -605,7 +630,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
}
if options.TargetFile != "" {
- if err = c.exportCheckpoint(options.TargetFile); err != nil {
+ if err = c.exportCheckpoint(options.TargetFile, options.IgnoreRootfs); err != nil {
return err
}
}
@@ -792,6 +817,23 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
if err := c.saveSpec(g.Spec()); err != nil {
return err
}
+
+ // Before actually restarting the container, apply the root file-system changes
+ if !options.IgnoreRootfs {
+ rootfsDiffPath := filepath.Join(c.bundlePath(), "rootfs-diff.tar")
+ if _, err := os.Stat(rootfsDiffPath); err == nil {
+ // Only do this if a rootfs-diff.tar actually exists
+ rootfsDiffFile, err := os.Open(rootfsDiffPath)
+ if err != nil {
+ return errors.Wrapf(err, "Failed to open root file-system diff file %s", rootfsDiffPath)
+ }
+ if err := c.runtime.ApplyDiffTarStream(c.ID(), rootfsDiffFile); err != nil {
+ return errors.Wrapf(err, "Failed to apply root file-system diff file %s", rootfsDiffPath)
+ }
+ rootfsDiffFile.Close()
+ }
+ }
+
if err := c.ociRuntime.createContainer(c, c.config.CgroupParent, &options); err != nil {
return err
}
@@ -809,7 +851,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
if err != nil {
logrus.Debugf("Non-fatal: removal of checkpoint directory (%s) failed: %v", c.CheckpointPath(), err)
}
- cleanup := [...]string{"restore.log", "dump.log", "stats-dump", "stats-restore", "network.status"}
+ cleanup := [...]string{"restore.log", "dump.log", "stats-dump", "stats-restore", "network.status", "rootfs-diff.tar"}
for _, del := range cleanup {
file := filepath.Join(c.bundlePath(), del)
err = os.Remove(file)
diff --git a/libpod/diff.go b/libpod/diff.go
index f348e6b81..925bda927 100644
--- a/libpod/diff.go
+++ b/libpod/diff.go
@@ -1,6 +1,9 @@
package libpod
import (
+ "archive/tar"
+ "io"
+
"github.com/containers/libpod/libpod/layers"
"github.com/containers/storage/pkg/archive"
"github.com/pkg/errors"
@@ -44,6 +47,59 @@ func (r *Runtime) GetDiff(from, to string) ([]archive.Change, error) {
return rchanges, err
}
+// skipFileInTarAchive is an archive.TarModifierFunc function
+// which tells archive.ReplaceFileTarWrapper to skip files
+// from the tarstream
+func skipFileInTarAchive(path string, header *tar.Header, content io.Reader) (*tar.Header, []byte, error) {
+ return nil, nil, nil
+}
+
+// GetDiffTarStream returns the differences between the two images, layers, or containers.
+// It is the same functionality as GetDiff() except that it returns a tarstream
+func (r *Runtime) GetDiffTarStream(from, to string) (io.ReadCloser, error) {
+ toLayer, err := r.getLayerID(to)
+ if err != nil {
+ return nil, err
+ }
+ fromLayer := ""
+ if from != "" {
+ fromLayer, err = r.getLayerID(from)
+ if err != nil {
+ return nil, err
+ }
+ }
+ rc, err := r.store.Diff(fromLayer, toLayer, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ // Skip files in the tar archive which are listed
+ // in containerMounts map. Just as in the GetDiff()
+ // function from above
+ filterMap := make(map[string]archive.TarModifierFunc)
+ for key := range containerMounts {
+ filterMap[key[1:]] = skipFileInTarAchive
+ // In the tarstream directories always include a trailing '/'.
+ // For simplicity this duplicates every entry from
+ // containerMounts with a trailing '/', as containerMounts
+ // does not use trailing '/' for directories.
+ filterMap[key[1:]+"/"] = skipFileInTarAchive
+ }
+
+ filteredTarStream := archive.ReplaceFileTarWrapper(rc, filterMap)
+ return filteredTarStream, nil
+}
+
+// ApplyDiffTarStream applies the changes stored in 'diff' to the layer 'to'
+func (r *Runtime) ApplyDiffTarStream(to string, diff io.Reader) error {
+ toLayer, err := r.getLayerID(to)
+ if err != nil {
+ return err
+ }
+ _, err = r.store.ApplyDiff(toLayer, diff)
+ return err
+}
+
// GetLayerID gets a full layer id given a full or partial id
// If the id matches a container or image, the id of the top layer is returned
// If the id matches a layer, the top layer id is returned