mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +08:00
Move checkpoint/restore code to pkg/checkpoint/crutils
To be able to reuse common checkpoint/restore functions this commit moves code to pkg/checkpoint/crutils. This commit has not functional changes. It only moves code around. [NO TESTS NEEDED] - only moving code around Signed-off-by: Adrian Reber <areber@redhat.com>
This commit is contained in:
@ -33,6 +33,7 @@ import (
|
|||||||
"github.com/containers/podman/v3/libpod/events"
|
"github.com/containers/podman/v3/libpod/events"
|
||||||
"github.com/containers/podman/v3/pkg/annotations"
|
"github.com/containers/podman/v3/pkg/annotations"
|
||||||
"github.com/containers/podman/v3/pkg/cgroups"
|
"github.com/containers/podman/v3/pkg/cgroups"
|
||||||
|
"github.com/containers/podman/v3/pkg/checkpoint/crutils"
|
||||||
"github.com/containers/podman/v3/pkg/criu"
|
"github.com/containers/podman/v3/pkg/criu"
|
||||||
"github.com/containers/podman/v3/pkg/lookup"
|
"github.com/containers/podman/v3/pkg/lookup"
|
||||||
"github.com/containers/podman/v3/pkg/resolvconf"
|
"github.com/containers/podman/v3/pkg/resolvconf"
|
||||||
@ -895,69 +896,20 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error {
|
|||||||
includeFiles[0] = "pre-checkpoint"
|
includeFiles[0] = "pre-checkpoint"
|
||||||
}
|
}
|
||||||
// Get root file-system changes included in the checkpoint archive
|
// Get root file-system changes included in the checkpoint archive
|
||||||
rootfsDiffPath := filepath.Join(c.bundlePath(), "rootfs-diff.tar")
|
var addToTarFiles []string
|
||||||
deleteFilesList := filepath.Join(c.bundlePath(), "deleted.files")
|
|
||||||
if !options.IgnoreRootfs {
|
if !options.IgnoreRootfs {
|
||||||
// To correctly track deleted files, let's go through the output of 'podman diff'
|
// To correctly track deleted files, let's go through the output of 'podman diff'
|
||||||
tarFiles, err := c.runtime.GetDiff("", c.ID())
|
rootFsChanges, err := c.runtime.GetDiff("", c.ID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error exporting root file-system diff to %q", rootfsDiffPath)
|
return errors.Wrapf(err, "error exporting root file-system diff for %q", c.ID())
|
||||||
}
|
|
||||||
var rootfsIncludeFiles []string
|
|
||||||
var deletedFiles []string
|
|
||||||
|
|
||||||
for _, file := range tarFiles {
|
|
||||||
if file.Kind == archive.ChangeAdd {
|
|
||||||
rootfsIncludeFiles = append(rootfsIncludeFiles, file.Path)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if file.Kind == archive.ChangeDelete {
|
|
||||||
deletedFiles = append(deletedFiles, file.Path)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fileName, err := os.Stat(file.Path)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !fileName.IsDir() && file.Kind == archive.ChangeModify {
|
|
||||||
rootfsIncludeFiles = append(rootfsIncludeFiles, file.Path)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(rootfsIncludeFiles) > 0 {
|
addToTarFiles, err := crutils.CRCreateRootFsDiffTar(&rootFsChanges, c.state.Mountpoint, c.bundlePath())
|
||||||
rootfsTar, err := archive.TarWithOptions(c.state.Mountpoint, &archive.TarOptions{
|
if err != nil {
|
||||||
Compression: archive.Uncompressed,
|
return err
|
||||||
IncludeSourceDir: true,
|
|
||||||
IncludeFiles: rootfsIncludeFiles,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error exporting root file-system diff to %q", rootfsDiffPath)
|
|
||||||
}
|
|
||||||
rootfsDiffFile, err := os.Create(rootfsDiffPath)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error creating root file-system diff file %q", rootfsDiffPath)
|
|
||||||
}
|
|
||||||
defer rootfsDiffFile.Close()
|
|
||||||
_, err = io.Copy(rootfsDiffFile, rootfsTar)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
includeFiles = append(includeFiles, "rootfs-diff.tar")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(deletedFiles) > 0 {
|
includeFiles = append(includeFiles, addToTarFiles...)
|
||||||
formatJSON, err := json.MarshalIndent(deletedFiles, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error creating delete files list file %q", deleteFilesList)
|
|
||||||
}
|
|
||||||
if err := ioutil.WriteFile(deleteFilesList, formatJSON, 0600); err != nil {
|
|
||||||
return errors.Wrap(err, "error creating delete files list file")
|
|
||||||
}
|
|
||||||
|
|
||||||
includeFiles = append(includeFiles, "deleted.files")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Folder containing archived volumes that will be included in the export
|
// Folder containing archived volumes that will be included in the export
|
||||||
@ -1034,8 +986,9 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Remove(rootfsDiffPath)
|
for _, file := range addToTarFiles {
|
||||||
os.Remove(deleteFilesList)
|
os.Remove(filepath.Join(c.bundlePath(), file))
|
||||||
|
}
|
||||||
|
|
||||||
if !options.IgnoreVolumes {
|
if !options.IgnoreVolumes {
|
||||||
os.RemoveAll(expVolDir)
|
os.RemoveAll(expVolDir)
|
||||||
@ -1054,23 +1007,6 @@ func (c *Container) checkpointRestoreSupported() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) checkpointRestoreLabelLog(fileName string) error {
|
|
||||||
// Create the CRIU log file and label it
|
|
||||||
dumpLog := filepath.Join(c.bundlePath(), fileName)
|
|
||||||
|
|
||||||
logFile, err := os.OpenFile(dumpLog, os.O_CREATE, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to create CRIU log file")
|
|
||||||
}
|
|
||||||
if err := logFile.Close(); err != nil {
|
|
||||||
logrus.Error(err)
|
|
||||||
}
|
|
||||||
if err = label.SetFileLabel(dumpLog, c.MountLabel()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointOptions) error {
|
func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointOptions) error {
|
||||||
if err := c.checkpointRestoreSupported(); err != nil {
|
if err := c.checkpointRestoreSupported(); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -1084,7 +1020,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
|
|||||||
return errors.Errorf("cannot checkpoint containers that have been started with '--rm' unless '--export' is used")
|
return errors.Errorf("cannot checkpoint containers that have been started with '--rm' unless '--export' is used")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.checkpointRestoreLabelLog("dump.log"); err != nil {
|
if err := crutils.CRCreateFileWithLabel(c.bundlePath(), "dump.log", c.MountLabel()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1151,28 +1087,13 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) importCheckpoint(input string) error {
|
func (c *Container) importCheckpoint(input string) error {
|
||||||
archiveFile, err := os.Open(input)
|
if err := crutils.CRImportCheckpointWithoutConfig(c.bundlePath(), input); err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return errors.Wrap(err, "failed to open checkpoint archive for import")
|
|
||||||
}
|
|
||||||
|
|
||||||
defer archiveFile.Close()
|
|
||||||
options := &archive.TarOptions{
|
|
||||||
ExcludePatterns: []string{
|
|
||||||
// config.dump and spec.dump are only required
|
|
||||||
// container creation
|
|
||||||
"config.dump",
|
|
||||||
"spec.dump",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
err = archive.Untar(archiveFile, c.bundlePath(), options)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "unpacking of checkpoint archive %s failed", input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the newly created config.json exists on disk
|
// Make sure the newly created config.json exists on disk
|
||||||
g := generate.Generator{Config: c.config.Spec}
|
g := generate.Generator{Config: c.config.Spec}
|
||||||
if err = c.saveSpec(g.Config); err != nil {
|
if err := c.saveSpec(g.Config); err != nil {
|
||||||
return errors.Wrap(err, "saving imported container specification for restore failed")
|
return errors.Wrap(err, "saving imported container specification for restore failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1221,7 +1142,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
|
|||||||
return errors.Wrapf(err, "a complete checkpoint for this container cannot be found, cannot restore")
|
return errors.Wrapf(err, "a complete checkpoint for this container cannot be found, cannot restore")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.checkpointRestoreLabelLog("restore.log"); err != nil {
|
if err := crutils.CRCreateFileWithLabel(c.bundlePath(), "restore.log", c.MountLabel()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1398,36 +1319,12 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
|
|||||||
|
|
||||||
// Before actually restarting the container, apply the root file-system changes
|
// Before actually restarting the container, apply the root file-system changes
|
||||||
if !options.IgnoreRootfs {
|
if !options.IgnoreRootfs {
|
||||||
rootfsDiffPath := filepath.Join(c.bundlePath(), "rootfs-diff.tar")
|
if err := crutils.CRApplyRootFsDiffTar(c.bundlePath(), c.state.Mountpoint); err != nil {
|
||||||
if _, err := os.Stat(rootfsDiffPath); err == nil {
|
return err
|
||||||
// Only do this if a rootfs-diff.tar actually exists
|
|
||||||
rootfsDiffFile, err := os.Open(rootfsDiffPath)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to open root file-system diff file")
|
|
||||||
}
|
|
||||||
defer rootfsDiffFile.Close()
|
|
||||||
if err := c.runtime.ApplyDiffTarStream(c.ID(), rootfsDiffFile); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to apply root file-system diff file %s", rootfsDiffPath)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
deletedFilesPath := filepath.Join(c.bundlePath(), "deleted.files")
|
|
||||||
if _, err := os.Stat(deletedFilesPath); err == nil {
|
if err := crutils.CRRemoveDeletedFiles(c.ID(), c.bundlePath(), c.state.Mountpoint); err != nil {
|
||||||
var deletedFiles []string
|
return err
|
||||||
deletedFilesJSON, err := ioutil.ReadFile(deletedFilesPath)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to read deleted files file")
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(deletedFilesJSON, &deletedFiles); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to unmarshal deleted files file %s", deletedFilesPath)
|
|
||||||
}
|
|
||||||
for _, deleteFile := range deletedFiles {
|
|
||||||
// Using RemoveAll as deletedFiles, which is generated from 'podman diff'
|
|
||||||
// lists completely deleted directories as a single entry: 'D /root'.
|
|
||||||
err = os.RemoveAll(filepath.Join(c.state.Mountpoint, deleteFile))
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to delete files from container %s during restore", c.ID())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ import (
|
|||||||
"github.com/containers/podman/v3/libpod/define"
|
"github.com/containers/podman/v3/libpod/define"
|
||||||
"github.com/containers/podman/v3/libpod/logs"
|
"github.com/containers/podman/v3/libpod/logs"
|
||||||
"github.com/containers/podman/v3/pkg/cgroups"
|
"github.com/containers/podman/v3/pkg/cgroups"
|
||||||
|
"github.com/containers/podman/v3/pkg/checkpoint/crutils"
|
||||||
"github.com/containers/podman/v3/pkg/errorhandling"
|
"github.com/containers/podman/v3/pkg/errorhandling"
|
||||||
"github.com/containers/podman/v3/pkg/lookup"
|
"github.com/containers/podman/v3/pkg/lookup"
|
||||||
"github.com/containers/podman/v3/pkg/rootless"
|
"github.com/containers/podman/v3/pkg/rootless"
|
||||||
@ -837,16 +838,7 @@ func (r *ConmonOCIRuntime) CheckConmonRunning(ctr *Container) (bool, error) {
|
|||||||
// SupportsCheckpoint checks if the OCI runtime supports checkpointing
|
// SupportsCheckpoint checks if the OCI runtime supports checkpointing
|
||||||
// containers.
|
// containers.
|
||||||
func (r *ConmonOCIRuntime) SupportsCheckpoint() bool {
|
func (r *ConmonOCIRuntime) SupportsCheckpoint() bool {
|
||||||
// Check if the runtime implements checkpointing. Currently only
|
return crutils.CRRuntimeSupportsCheckpointRestore(r.path)
|
||||||
// runc's checkpoint/restore implementation is supported.
|
|
||||||
cmd := exec.Command(r.path, "checkpoint", "--help")
|
|
||||||
if err := cmd.Start(); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if err := cmd.Wait(); err == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SupportsJSONErrors checks if the OCI runtime supports JSON-formatted error
|
// SupportsJSONErrors checks if the OCI runtime supports JSON-formatted error
|
||||||
|
@ -4,15 +4,14 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
|
metadata "github.com/checkpoint-restore/checkpointctl/lib"
|
||||||
"github.com/containers/podman/v3/libpod"
|
"github.com/containers/podman/v3/libpod"
|
||||||
"github.com/containers/podman/v3/libpod/image"
|
"github.com/containers/podman/v3/libpod/image"
|
||||||
"github.com/containers/podman/v3/pkg/domain/entities"
|
"github.com/containers/podman/v3/pkg/domain/entities"
|
||||||
"github.com/containers/podman/v3/pkg/errorhandling"
|
"github.com/containers/podman/v3/pkg/errorhandling"
|
||||||
"github.com/containers/podman/v3/pkg/util"
|
"github.com/containers/podman/v3/pkg/util"
|
||||||
"github.com/containers/storage/pkg/archive"
|
"github.com/containers/storage/pkg/archive"
|
||||||
jsoniter "github.com/json-iterator/go"
|
|
||||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -20,21 +19,6 @@ import (
|
|||||||
|
|
||||||
// Prefixing the checkpoint/restore related functions with 'cr'
|
// Prefixing the checkpoint/restore related functions with 'cr'
|
||||||
|
|
||||||
// crImportFromJSON imports the JSON files stored in the exported
|
|
||||||
// checkpoint tarball
|
|
||||||
func crImportFromJSON(filePath string, v interface{}) error {
|
|
||||||
content, err := ioutil.ReadFile(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to read container definition for restore")
|
|
||||||
}
|
|
||||||
json := jsoniter.ConfigCompatibleWithStandardLibrary
|
|
||||||
if err = json.Unmarshal(content, v); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to unmarshal container definition %s for restore", filePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CRImportCheckpoint it the function which imports the information
|
// CRImportCheckpoint it the function which imports the information
|
||||||
// from checkpoint tarball and re-creates the container from that information
|
// from checkpoint tarball and re-creates the container from that information
|
||||||
func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOptions entities.RestoreOptions) ([]*libpod.Container, error) {
|
func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOptions entities.RestoreOptions) ([]*libpod.Container, error) {
|
||||||
@ -73,13 +57,13 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt
|
|||||||
|
|
||||||
// Load spec.dump from temporary directory
|
// Load spec.dump from temporary directory
|
||||||
dumpSpec := new(spec.Spec)
|
dumpSpec := new(spec.Spec)
|
||||||
if err := crImportFromJSON(filepath.Join(dir, "spec.dump"), dumpSpec); err != nil {
|
if _, err := metadata.ReadJSONFile(dumpSpec, dir, metadata.SpecDumpFile); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load config.dump from temporary directory
|
// Load config.dump from temporary directory
|
||||||
config := new(libpod.ContainerConfig)
|
config := new(libpod.ContainerConfig)
|
||||||
if err = crImportFromJSON(filepath.Join(dir, "config.dump"), config); err != nil {
|
if _, err = metadata.ReadJSONFile(config, dir, metadata.ConfigDumpFile); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
191
pkg/checkpoint/crutils/checkpoint_restore_utils.go
Normal file
191
pkg/checkpoint/crutils/checkpoint_restore_utils.go
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
package crutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
metadata "github.com/checkpoint-restore/checkpointctl/lib"
|
||||||
|
"github.com/containers/storage/pkg/archive"
|
||||||
|
"github.com/opencontainers/selinux/go-selinux/label"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This file mainly exist to make the checkpoint/restore functions
|
||||||
|
// available for other users. One possible candidate would be CRI-O.
|
||||||
|
|
||||||
|
// CRImportCheckpointWithoutConfig imports the checkpoint archive (input)
|
||||||
|
// into the directory destination without "config.dump" and "spec.dump"
|
||||||
|
func CRImportCheckpointWithoutConfig(destination, input string) error {
|
||||||
|
archiveFile, err := os.Open(input)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "Failed to open checkpoint archive %s for import", input)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer archiveFile.Close()
|
||||||
|
options := &archive.TarOptions{
|
||||||
|
ExcludePatterns: []string{
|
||||||
|
// Import everything else besides the container config
|
||||||
|
metadata.ConfigDumpFile,
|
||||||
|
metadata.SpecDumpFile,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err = archive.Untar(archiveFile, destination, options); err != nil {
|
||||||
|
return errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", input)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRRemoveDeletedFiles loads the list of deleted files and if
|
||||||
|
// it exists deletes all files listed.
|
||||||
|
func CRRemoveDeletedFiles(id, baseDirectory, containerRootDirectory string) error {
|
||||||
|
deletedFiles, _, err := metadata.ReadContainerCheckpointDeletedFiles(baseDirectory)
|
||||||
|
if os.IsNotExist(errors.Unwrap(errors.Unwrap(err))) {
|
||||||
|
// No files to delete. Just return
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to read deleted files file")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, deleteFile := range deletedFiles {
|
||||||
|
// Using RemoveAll as deletedFiles, which is generated from 'podman diff'
|
||||||
|
// lists completely deleted directories as a single entry: 'D /root'.
|
||||||
|
if err := os.RemoveAll(filepath.Join(containerRootDirectory, deleteFile)); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to delete files from container %s during restore", id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRApplyRootFsDiffTar applies the tar archive found in baseDirectory with the
|
||||||
|
// root file system changes on top of containerRootDirectory
|
||||||
|
func CRApplyRootFsDiffTar(baseDirectory, containerRootDirectory string) error {
|
||||||
|
rootfsDiffPath := filepath.Join(baseDirectory, metadata.RootFsDiffTar)
|
||||||
|
if _, err := os.Stat(rootfsDiffPath); err != nil {
|
||||||
|
// Only do this if a rootfs-diff.tar actually exists
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rootfsDiffFile, err := os.Open(rootfsDiffPath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to open root file-system diff file")
|
||||||
|
}
|
||||||
|
defer rootfsDiffFile.Close()
|
||||||
|
|
||||||
|
if err := archive.Untar(rootfsDiffFile, containerRootDirectory, nil); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to apply root file-system diff file %s", rootfsDiffPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRCreateRootFsDiffTar goes through the 'changes' and can create two files:
|
||||||
|
// * metadata.RootFsDiffTar will contain all new and changed files
|
||||||
|
// * metadata.DeletedFilesFile will contain a list of deleted files
|
||||||
|
// With these two files it is possible to restore the container file system to the same
|
||||||
|
// state it was during checkpointing.
|
||||||
|
// Changes to directories (owner, mode) are not handled.
|
||||||
|
func CRCreateRootFsDiffTar(changes *[]archive.Change, mountPoint, destination string) (includeFiles []string, err error) {
|
||||||
|
if len(*changes) == 0 {
|
||||||
|
return includeFiles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var rootfsIncludeFiles []string
|
||||||
|
var deletedFiles []string
|
||||||
|
|
||||||
|
rootfsDiffPath := filepath.Join(destination, metadata.RootFsDiffTar)
|
||||||
|
|
||||||
|
for _, file := range *changes {
|
||||||
|
if file.Kind == archive.ChangeAdd {
|
||||||
|
rootfsIncludeFiles = append(rootfsIncludeFiles, file.Path)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if file.Kind == archive.ChangeDelete {
|
||||||
|
deletedFiles = append(deletedFiles, file.Path)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fileName, err := os.Stat(file.Path)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !fileName.IsDir() && file.Kind == archive.ChangeModify {
|
||||||
|
rootfsIncludeFiles = append(rootfsIncludeFiles, file.Path)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rootfsIncludeFiles) > 0 {
|
||||||
|
rootfsTar, err := archive.TarWithOptions(mountPoint, &archive.TarOptions{
|
||||||
|
Compression: archive.Uncompressed,
|
||||||
|
IncludeSourceDir: true,
|
||||||
|
IncludeFiles: rootfsIncludeFiles,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return includeFiles, errors.Wrapf(err, "error exporting root file-system diff to %q", rootfsDiffPath)
|
||||||
|
}
|
||||||
|
rootfsDiffFile, err := os.Create(rootfsDiffPath)
|
||||||
|
if err != nil {
|
||||||
|
return includeFiles, errors.Wrapf(err, "error creating root file-system diff file %q", rootfsDiffPath)
|
||||||
|
}
|
||||||
|
defer rootfsDiffFile.Close()
|
||||||
|
if _, err = io.Copy(rootfsDiffFile, rootfsTar); err != nil {
|
||||||
|
return includeFiles, err
|
||||||
|
}
|
||||||
|
|
||||||
|
includeFiles = append(includeFiles, metadata.RootFsDiffTar)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(deletedFiles) == 0 {
|
||||||
|
return includeFiles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := metadata.WriteJSONFile(deletedFiles, destination, metadata.DeletedFilesFile); err != nil {
|
||||||
|
return includeFiles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
includeFiles = append(includeFiles, metadata.DeletedFilesFile)
|
||||||
|
|
||||||
|
return includeFiles, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRCreateFileWithLabel creates an empty file and sets the corresponding ('fileLabel')
|
||||||
|
// SELinux label on the file.
|
||||||
|
// This is necessary for CRIU log files because CRIU infects the processes in
|
||||||
|
// the container with a 'parasite' and this will also try to write to the log files
|
||||||
|
// from the context of the container processes.
|
||||||
|
func CRCreateFileWithLabel(directory, fileName, fileLabel string) error {
|
||||||
|
logFileName := filepath.Join(directory, fileName)
|
||||||
|
|
||||||
|
logFile, err := os.OpenFile(logFileName, os.O_CREATE, 0o600)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to create file %q", logFileName)
|
||||||
|
}
|
||||||
|
defer logFile.Close()
|
||||||
|
if err = label.SetFileLabel(logFileName, fileLabel); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to label file %q", logFileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRRuntimeSupportsCheckpointRestore tests if the given runtime at 'runtimePath'
|
||||||
|
// supports checkpointing. The checkpoint restore interface has no definition
|
||||||
|
// but crun implements all commands just as runc does. Whathh runc does it the
|
||||||
|
// official definition of the checkpoint/restore interface.
|
||||||
|
func CRRuntimeSupportsCheckpointRestore(runtimePath string) bool {
|
||||||
|
// Check if the runtime implements checkpointing. Currently only
|
||||||
|
// runc's and crun's checkpoint/restore implementation is supported.
|
||||||
|
cmd := exec.Command(runtimePath, "checkpoint", "--help")
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err := cmd.Wait(); err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
Reference in New Issue
Block a user