mirror of
https://github.com/containers/podman.git
synced 2025-10-17 11:14:40 +08:00

Checkpoint/restore pod tests are not running with an older runc and now that runc 1.1.0 appears in the repositories it was detected that the tests were failing. This was not detected in CI as CI was not using runc 1.1.0 yet. Signed-off-by: Adrian Reber <areber@redhat.com>
242 lines
7.6 KiB
Go
242 lines
7.6 KiB
Go
package checkpoint
|
|
|
|
import (
|
|
"context"
|
|
"io/ioutil"
|
|
"os"
|
|
|
|
metadata "github.com/checkpoint-restore/checkpointctl/lib"
|
|
"github.com/containers/common/libimage"
|
|
"github.com/containers/common/pkg/config"
|
|
"github.com/containers/podman/v4/libpod"
|
|
ann "github.com/containers/podman/v4/pkg/annotations"
|
|
"github.com/containers/podman/v4/pkg/checkpoint/crutils"
|
|
"github.com/containers/podman/v4/pkg/criu"
|
|
"github.com/containers/podman/v4/pkg/domain/entities"
|
|
"github.com/containers/podman/v4/pkg/specgen/generate"
|
|
"github.com/containers/podman/v4/pkg/specgenutil"
|
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// Prefixing the checkpoint/restore related functions with 'cr'
|
|
|
|
// CRImportCheckpoint it the function which imports the 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) {
|
|
// First get the container definition from the
|
|
// tarball to a temporary directory
|
|
dir, err := ioutil.TempDir("", "checkpoint")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() {
|
|
if err := os.RemoveAll(dir); err != nil {
|
|
logrus.Errorf("Could not recursively remove %s: %q", dir, err)
|
|
}
|
|
}()
|
|
if err := crutils.CRImportCheckpointConfigOnly(dir, restoreOptions.Import); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Load spec.dump from temporary directory
|
|
dumpSpec := new(spec.Spec)
|
|
if _, err := metadata.ReadJSONFile(dumpSpec, dir, metadata.SpecDumpFile); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Load config.dump from temporary directory
|
|
ctrConfig := new(libpod.ContainerConfig)
|
|
if _, err = metadata.ReadJSONFile(ctrConfig, dir, metadata.ConfigDumpFile); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if ctrConfig.Pod != "" && restoreOptions.Pod == "" {
|
|
return nil, errors.New("cannot restore pod container without --pod")
|
|
}
|
|
|
|
if ctrConfig.Pod == "" && restoreOptions.Pod != "" {
|
|
return nil, errors.New("cannot restore non pod container into pod")
|
|
}
|
|
|
|
// This should not happen as checkpoints with these options are not exported.
|
|
if len(ctrConfig.Dependencies) > 0 {
|
|
return nil, errors.Errorf("Cannot import checkpoints of containers with dependencies")
|
|
}
|
|
|
|
// Volumes included in the checkpoint should not exist
|
|
if !restoreOptions.IgnoreVolumes {
|
|
for _, vol := range ctrConfig.NamedVolumes {
|
|
exists, err := runtime.HasVolume(vol.Name)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if exists {
|
|
return nil, errors.Errorf("volume with name %s already exists. Use --ignore-volumes to not restore content of volumes", vol.Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
if restoreOptions.IgnoreStaticIP || restoreOptions.IgnoreStaticMAC {
|
|
for net, opts := range ctrConfig.Networks {
|
|
if restoreOptions.IgnoreStaticIP {
|
|
opts.StaticIPs = nil
|
|
}
|
|
if restoreOptions.IgnoreStaticMAC {
|
|
opts.StaticMAC = nil
|
|
}
|
|
ctrConfig.Networks[net] = opts
|
|
}
|
|
}
|
|
|
|
ctrID := ctrConfig.ID
|
|
newName := false
|
|
|
|
// Check if the restored container gets a new name
|
|
if restoreOptions.Name != "" {
|
|
ctrConfig.ID = ""
|
|
ctrConfig.Name = restoreOptions.Name
|
|
newName = true
|
|
}
|
|
|
|
if restoreOptions.Pod != "" {
|
|
// Restoring into a Pod requires much newer versions of CRIU
|
|
if !criu.CheckForCriu(criu.PodCriuVersion) {
|
|
return nil, errors.Errorf("restoring containers into pods requires at least CRIU %d", criu.PodCriuVersion)
|
|
}
|
|
// The runtime also has to support it
|
|
if !crutils.CRRuntimeSupportsPodCheckpointRestore(runtime.GetOCIRuntimePath()) {
|
|
return nil, errors.Errorf("runtime %s does not support pod restore", runtime.GetOCIRuntimePath())
|
|
}
|
|
// Restoring into an existing Pod
|
|
ctrConfig.Pod = restoreOptions.Pod
|
|
|
|
// According to podman pod create a pod can share the following namespaces:
|
|
// cgroup, ipc, net, pid, uts
|
|
// Let's make sure we a restoring into a pod with the same shared namespaces.
|
|
pod, err := runtime.LookupPod(ctrConfig.Pod)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "pod %q cannot be retrieved", ctrConfig.Pod)
|
|
}
|
|
|
|
infraContainer, err := pod.InfraContainer()
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "cannot retrieve infra container from pod %q", ctrConfig.Pod)
|
|
}
|
|
|
|
// If a namespaces was shared (!= "") it needs to be set to the new infrastructure container
|
|
// If the infrastructure container does not share the same namespaces as the to be restored
|
|
// container we abort.
|
|
if ctrConfig.IPCNsCtr != "" {
|
|
if !pod.SharesIPC() {
|
|
return nil, errors.Errorf("pod %s does not share the IPC namespace", ctrConfig.Pod)
|
|
}
|
|
ctrConfig.IPCNsCtr = infraContainer.ID()
|
|
}
|
|
|
|
if ctrConfig.NetNsCtr != "" {
|
|
if !pod.SharesNet() {
|
|
return nil, errors.Errorf("pod %s does not share the network namespace", ctrConfig.Pod)
|
|
}
|
|
ctrConfig.NetNsCtr = infraContainer.ID()
|
|
for net, opts := range ctrConfig.Networks {
|
|
opts.StaticIPs = nil
|
|
opts.StaticMAC = nil
|
|
ctrConfig.Networks[net] = opts
|
|
}
|
|
ctrConfig.StaticIP = nil
|
|
ctrConfig.StaticMAC = nil
|
|
}
|
|
|
|
if ctrConfig.PIDNsCtr != "" {
|
|
if !pod.SharesPID() {
|
|
return nil, errors.Errorf("pod %s does not share the PID namespace", ctrConfig.Pod)
|
|
}
|
|
ctrConfig.PIDNsCtr = infraContainer.ID()
|
|
}
|
|
|
|
if ctrConfig.UTSNsCtr != "" {
|
|
if !pod.SharesUTS() {
|
|
return nil, errors.Errorf("pod %s does not share the UTS namespace", ctrConfig.Pod)
|
|
}
|
|
ctrConfig.UTSNsCtr = infraContainer.ID()
|
|
}
|
|
|
|
if ctrConfig.CgroupNsCtr != "" {
|
|
if !pod.SharesCgroup() {
|
|
return nil, errors.Errorf("pod %s does not share the cgroup namespace", ctrConfig.Pod)
|
|
}
|
|
ctrConfig.CgroupNsCtr = infraContainer.ID()
|
|
}
|
|
|
|
// Change SELinux labels to infrastructure container labels
|
|
ctrConfig.MountLabel = infraContainer.MountLabel()
|
|
ctrConfig.ProcessLabel = infraContainer.ProcessLabel()
|
|
|
|
// Fix parent cgroup
|
|
cgroupPath, err := pod.CgroupPath()
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "cannot retrieve cgroup path from pod %q", ctrConfig.Pod)
|
|
}
|
|
ctrConfig.CgroupParent = cgroupPath
|
|
|
|
oldPodID := dumpSpec.Annotations[ann.SandboxID]
|
|
// Fix up SandboxID in the annotations
|
|
dumpSpec.Annotations[ann.SandboxID] = ctrConfig.Pod
|
|
// Fix up CreateCommand
|
|
for i, c := range ctrConfig.CreateCommand {
|
|
if c == oldPodID {
|
|
ctrConfig.CreateCommand[i] = ctrConfig.Pod
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(restoreOptions.PublishPorts) > 0 {
|
|
pubPorts, err := specgenutil.CreatePortBindings(restoreOptions.PublishPorts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ports, err := generate.ParsePortMapping(pubPorts, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ctrConfig.PortMappings = ports
|
|
}
|
|
|
|
pullOptions := &libimage.PullOptions{}
|
|
pullOptions.Writer = os.Stderr
|
|
if _, err := runtime.LibimageRuntime().Pull(ctx, ctrConfig.RootfsImageName, config.PullPolicyMissing, pullOptions); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Now create a new container from the just loaded information
|
|
container, err := runtime.RestoreContainer(ctx, dumpSpec, ctrConfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var containers []*libpod.Container
|
|
if container == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
containerConfig := container.Config()
|
|
ctrName := ctrConfig.Name
|
|
if containerConfig.Name != ctrName {
|
|
return nil, errors.Errorf("Name of restored container (%s) does not match requested name (%s)", containerConfig.Name, ctrName)
|
|
}
|
|
|
|
if !newName {
|
|
// Only check ID for a restore with the same name.
|
|
// Using -n to request a new name for the restored container, will also create a new ID
|
|
if containerConfig.ID != ctrID {
|
|
return nil, errors.Errorf("ID of restored container (%s) does not match requested ID (%s)", containerConfig.ID, ctrID)
|
|
}
|
|
}
|
|
|
|
containers = append(containers, container)
|
|
return containers, nil
|
|
}
|