mirror of
https://github.com/containers/podman.git
synced 2025-05-21 17:16:22 +08:00

Currently, when Podman restores a container into a Pod, it always fails with the following error: Error: cannot add container f96670b26e53e70f7f451191ea39a093c940c6c48b47218aeeef1396cb860042 to pod h2-pod: no such pod This error occurs because r.state.Pod() is called in setupContainer() with the Pod name instead of ID. This patch fixes this problem by setting ctrConfig.Pod to pod.ID(). Reported-by: Stanislav Kosorin <stanokosorin4@gmail.com> Signed-off-by: Radostin Stoyanov <rstoyanov@fedoraproject.org>
236 lines
7.5 KiB
Go
236 lines
7.5 KiB
Go
//go:build !remote
|
|
|
|
package checkpoint
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
|
|
metadata "github.com/checkpoint-restore/checkpointctl/lib"
|
|
"github.com/containers/common/libimage"
|
|
"github.com/containers/common/pkg/config"
|
|
"github.com/containers/podman/v5/libpod"
|
|
ann "github.com/containers/podman/v5/pkg/annotations"
|
|
"github.com/containers/podman/v5/pkg/checkpoint/crutils"
|
|
"github.com/containers/podman/v5/pkg/criu"
|
|
"github.com/containers/podman/v5/pkg/domain/entities"
|
|
"github.com/containers/podman/v5/pkg/specgen/generate"
|
|
"github.com/containers/podman/v5/pkg/specgenutil"
|
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// Prefixing the checkpoint/restore related functions with 'cr'
|
|
|
|
func CRImportCheckpointTar(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 := os.MkdirTemp("", "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
|
|
}
|
|
return CRImportCheckpoint(ctx, runtime, restoreOptions, dir)
|
|
}
|
|
|
|
// 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, dir string) ([]*libpod.Container, error) {
|
|
// 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.New("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, fmt.Errorf("volume with name %s already exists. Use --ignore-volumes to not restore content of volumes", vol.Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
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 err := criu.CheckForCriu(criu.PodCriuVersion); err != nil {
|
|
return nil, fmt.Errorf("restoring containers into pod: %w", err)
|
|
}
|
|
// The runtime also has to support it
|
|
if !crutils.CRRuntimeSupportsPodCheckpointRestore(runtime.GetOCIRuntimePath()) {
|
|
return nil, fmt.Errorf("runtime %s does not support pod restore", runtime.GetOCIRuntimePath())
|
|
}
|
|
|
|
// According to podman pod create a pod can share the following namespaces:
|
|
// cgroup, ipc, net, pid, uts
|
|
// Let's make sure we are restoring into a pod with the same shared namespaces.
|
|
pod, err := runtime.LookupPod(restoreOptions.Pod)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("pod %q cannot be retrieved: %w", ctrConfig.Pod, err)
|
|
}
|
|
|
|
// Restoring into an existing Pod
|
|
ctrConfig.Pod = pod.ID()
|
|
|
|
infraContainer, err := pod.InfraContainer()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot retrieve infra container from pod %q: %w", ctrConfig.Pod, err)
|
|
}
|
|
|
|
// If a namespace 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, fmt.Errorf("pod %s does not share the IPC namespace", ctrConfig.Pod)
|
|
}
|
|
ctrConfig.IPCNsCtr = infraContainer.ID()
|
|
}
|
|
|
|
if ctrConfig.NetNsCtr != "" {
|
|
if !pod.SharesNet() {
|
|
return nil, fmt.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, fmt.Errorf("pod %s does not share the PID namespace", ctrConfig.Pod)
|
|
}
|
|
ctrConfig.PIDNsCtr = infraContainer.ID()
|
|
}
|
|
|
|
if ctrConfig.UTSNsCtr != "" {
|
|
if !pod.SharesUTS() {
|
|
return nil, fmt.Errorf("pod %s does not share the UTS namespace", ctrConfig.Pod)
|
|
}
|
|
ctrConfig.UTSNsCtr = infraContainer.ID()
|
|
}
|
|
|
|
if ctrConfig.CgroupNsCtr != "" {
|
|
if !pod.SharesCgroup() {
|
|
return nil, fmt.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, fmt.Errorf("cannot retrieve cgroup path from pod %q: %w", ctrConfig.Pod, err)
|
|
}
|
|
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, fmt.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, fmt.Errorf("ID of restored container (%s) does not match requested ID (%s)", containerConfig.ID, ctrID)
|
|
}
|
|
}
|
|
|
|
containers = append(containers, container)
|
|
return containers, nil
|
|
}
|