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:
OpenShift Merge Robot
2019-07-19 02:38:26 +02:00
committed by GitHub
13 changed files with 276 additions and 16 deletions

View File

@@ -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

View File

@@ -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)

View 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