mirror of
https://github.com/containers/podman.git
synced 2025-12-03 19:59:39 +08:00
Merge pull request #3443 from adrianreber/rootfs-changes-migration
Include changes to the container's root file-system in the checkpoint archive
This commit is contained in:
@@ -801,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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user