diff options
author | OpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com> | 2021-03-03 02:06:19 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-03-03 02:06:19 -0800 |
commit | 2a3460b26dc7056189abbbfd19c1f37367fa6b6c (patch) | |
tree | f4c93b23215606fd35a292f7edfe3cb564989ac7 /libpod | |
parent | 0a40c5a059455693de0cc9a3f4a2a16722fc61e9 (diff) | |
parent | 91b2f07d5ba75cacdc8a6607fc6aed4ee4bcc9cf (diff) | |
download | podman-2a3460b26dc7056189abbbfd19c1f37367fa6b6c.tar.gz podman-2a3460b26dc7056189abbbfd19c1f37367fa6b6c.tar.bz2 podman-2a3460b26dc7056189abbbfd19c1f37367fa6b6c.zip |
Merge pull request #9521 from adrianreber/2021-02-25-checkpointctl
Reorder checkpoint/restore code for CRI-O
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container_internal.go | 22 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 212 | ||||
-rw-r--r-- | libpod/oci_conmon_linux.go | 12 |
3 files changed, 49 insertions, 197 deletions
diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 2e0c24579..7e8226de4 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -13,6 +13,7 @@ import ( "strings" "time" + metadata "github.com/checkpoint-restore/checkpointctl/lib" "github.com/containers/buildah/copier" "github.com/containers/common/pkg/secrets" "github.com/containers/podman/v3/libpod/define" @@ -135,7 +136,7 @@ func (c *Container) ControlSocketPath() string { // CheckpointPath returns the path to the directory containing the checkpoint func (c *Container) CheckpointPath() string { - return filepath.Join(c.bundlePath(), "checkpoint") + return filepath.Join(c.bundlePath(), metadata.CheckpointDirectory) } // PreCheckpointPath returns the path to the directory containing the pre-checkpoint-images @@ -2141,26 +2142,11 @@ func (c *Container) canWithPrevious() error { return err } -// writeJSONFile marshalls and writes the given data to a JSON file -// in the bundle path -func (c *Container) writeJSONFile(v interface{}, file string) error { - fileJSON, err := json.MarshalIndent(v, "", " ") - if err != nil { - return errors.Wrapf(err, "error writing JSON to %s for container %s", file, c.ID()) - } - file = filepath.Join(c.bundlePath(), file) - if err := ioutil.WriteFile(file, fileJSON, 0644); err != nil { - return err - } - - return nil -} - // prepareCheckpointExport writes the config and spec to // JSON files for later export func (c *Container) prepareCheckpointExport() error { // save live config - if err := c.writeJSONFile(c.Config(), "config.dump"); err != nil { + if _, err := metadata.WriteJSONFile(c.Config(), c.bundlePath(), metadata.ConfigDumpFile); err != nil { return err } @@ -2171,7 +2157,7 @@ func (c *Container) prepareCheckpointExport() error { logrus.Debugf("generating spec for container %q failed with %v", c.ID(), err) return err } - if err := c.writeJSONFile(g.Config, "spec.dump"); err != nil { + if _, err := metadata.WriteJSONFile(g.Config, c.bundlePath(), metadata.SpecDumpFile); err != nil { return err } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index dc0418148..2684c2845 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -19,6 +19,7 @@ import ( "syscall" "time" + metadata "github.com/checkpoint-restore/checkpointctl/lib" cnitypes "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/plugins/pkg/ns" "github.com/containers/buildah/pkg/chrootuser" @@ -33,6 +34,7 @@ import ( "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/pkg/annotations" "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/lookup" "github.com/containers/podman/v3/pkg/resolvconf" @@ -884,80 +886,32 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error { logrus.Debugf("Exporting checkpoint image of container %q to %q", c.ID(), options.TargetFile) includeFiles := []string{ - "checkpoint", "artifacts", "ctr.log", - "config.dump", - "spec.dump", - "network.status"} + metadata.CheckpointDirectory, + metadata.ConfigDumpFile, + metadata.SpecDumpFile, + metadata.NetworkStatusFile, + } if options.PreCheckPoint { includeFiles[0] = "pre-checkpoint" } // Get root file-system changes included in the checkpoint archive - rootfsDiffPath := filepath.Join(c.bundlePath(), "rootfs-diff.tar") - deleteFilesList := filepath.Join(c.bundlePath(), "deleted.files") + var addToTarFiles []string if !options.IgnoreRootfs { // 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 { - 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 { - rootfsTar, err := archive.TarWithOptions(c.state.Mountpoint, &archive.TarOptions{ - Compression: archive.Uncompressed, - 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") + addToTarFiles, err := crutils.CRCreateRootFsDiffTar(&rootFsChanges, c.state.Mountpoint, c.bundlePath()) + if err != nil { + return err } - if len(deletedFiles) > 0 { - 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") - } + includeFiles = append(includeFiles, addToTarFiles...) } // Folder containing archived volumes that will be included in the export @@ -1034,8 +988,9 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error { return err } - os.Remove(rootfsDiffPath) - os.Remove(deleteFilesList) + for _, file := range addToTarFiles { + os.Remove(filepath.Join(c.bundlePath(), file)) + } if !options.IgnoreVolumes { os.RemoveAll(expVolDir) @@ -1054,23 +1009,6 @@ func (c *Container) checkpointRestoreSupported() error { 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 { if err := c.checkpointRestoreSupported(); err != nil { return err @@ -1084,7 +1022,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") } - if err := c.checkpointRestoreLabelLog("dump.log"); err != nil { + if err := crutils.CRCreateFileWithLabel(c.bundlePath(), "dump.log", c.MountLabel()); err != nil { return err } @@ -1095,11 +1033,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO // Save network.status. This is needed to restore the container with // the same IP. Currently limited to one IP address in a container // with one interface. - formatJSON, err := json.MarshalIndent(c.state.NetworkStatus, "", " ") - if err != nil { - return err - } - if err := ioutil.WriteFile(filepath.Join(c.bundlePath(), "network.status"), formatJSON, 0644); err != nil { + if _, err := metadata.WriteJSONFile(c.state.NetworkStatus, c.bundlePath(), metadata.NetworkStatusFile); err != nil { return err } @@ -1115,7 +1049,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO } if options.TargetFile != "" { - if err = c.exportCheckpoint(options); err != nil { + if err := c.exportCheckpoint(options); err != nil { return err } } @@ -1135,8 +1069,8 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO cleanup := []string{ "dump.log", "stats-dump", - "config.dump", - "spec.dump", + metadata.ConfigDumpFile, + metadata.SpecDumpFile, } for _, del := range cleanup { file := filepath.Join(c.bundlePath(), del) @@ -1151,28 +1085,13 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO } func (c *Container) importCheckpoint(input string) error { - archiveFile, err := os.Open(input) - if err != nil { - 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) + if err := crutils.CRImportCheckpointWithoutConfig(c.bundlePath(), input); err != nil { + return err } // Make sure the newly created config.json exists on disk 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") } @@ -1221,7 +1140,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") } - if err := c.checkpointRestoreLabelLog("restore.log"); err != nil { + if err := crutils.CRCreateFileWithLabel(c.bundlePath(), "restore.log", c.MountLabel()); err != nil { return err } @@ -1244,7 +1163,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti // Read network configuration from checkpoint // Currently only one interface with one IP is supported. - networkStatusFile, err := os.Open(filepath.Join(c.bundlePath(), "network.status")) + networkStatus, _, err := metadata.ReadContainerCheckpointNetworkStatus(c.bundlePath()) // If the restored container should get a new name, the IP address of // the container will not be restored. This assumes that if a new name is // specified, the container is restored multiple times. @@ -1254,43 +1173,14 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti if err == nil && options.Name == "" && (!options.IgnoreStaticIP || !options.IgnoreStaticMAC) { // The file with the network.status does exist. Let's restore the // container with the same IP address / MAC address as during checkpointing. - defer networkStatusFile.Close() - var networkStatus []*cnitypes.Result - networkJSON, err := ioutil.ReadAll(networkStatusFile) - if err != nil { - return err - } - if err := json.Unmarshal(networkJSON, &networkStatus); err != nil { - return err - } if !options.IgnoreStaticIP { - // Take the first IP address - var IP net.IP - if len(networkStatus) > 0 { - if len(networkStatus[0].IPs) > 0 { - IP = networkStatus[0].IPs[0].Address.IP - } - } - if IP != nil { + if IP := metadata.GetIPFromNetworkStatus(networkStatus); IP != nil { // Tell CNI which IP address we want. c.requestedIP = IP } } if !options.IgnoreStaticMAC { - // Take the first device with a defined sandbox. - var MAC net.HardwareAddr - if len(networkStatus) > 0 { - for _, n := range networkStatus[0].Interfaces { - if n.Sandbox != "" { - MAC, err = net.ParseMAC(n.Mac) - if err != nil { - return err - } - break - } - } - } - if MAC != nil { + if MAC := metadata.GetMACFromNetworkStatus(networkStatus); MAC != nil { // Tell CNI which MAC address we want. c.requestedMAC = MAC } @@ -1398,36 +1288,12 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti // 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.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) - } + if err := crutils.CRApplyRootFsDiffTar(c.bundlePath(), c.state.Mountpoint); err != nil { + return err } - deletedFilesPath := filepath.Join(c.bundlePath(), "deleted.files") - if _, err := os.Stat(deletedFilesPath); err == nil { - var deletedFiles []string - 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()) - } - } + + if err := crutils.CRRemoveDeletedFiles(c.ID(), c.bundlePath(), c.state.Mountpoint); err != nil { + return err } } @@ -1452,7 +1318,15 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti if err != nil { logrus.Debugf("Non-fatal: removal of pre-checkpoint directory (%s) failed: %v", c.PreCheckPointPath(), err) } - cleanup := [...]string{"restore.log", "dump.log", "stats-dump", "stats-restore", "network.status", "rootfs-diff.tar", "deleted.files"} + cleanup := [...]string{ + "restore.log", + "dump.log", + "stats-dump", + "stats-restore", + metadata.NetworkStatusFile, + metadata.RootFsDiffTar, + metadata.DeletedFilesFile, + } for _, del := range cleanup { file := filepath.Join(c.bundlePath(), del) err = os.Remove(file) diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index de7630c06..492bc807a 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -28,6 +28,7 @@ import ( "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/logs" "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/lookup" "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 // containers. func (r *ConmonOCIRuntime) SupportsCheckpoint() bool { - // Check if the runtime implements checkpointing. Currently only - // 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 + return crutils.CRRuntimeSupportsCheckpointRestore(r.path) } // SupportsJSONErrors checks if the OCI runtime supports JSON-formatted error |