github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/checkpoint/checkpoint_restore.go (about)

     1  package checkpoint
     2  
     3  import (
     4  	"context"
     5  	"io/ioutil"
     6  	"os"
     7  
     8  	metadata "github.com/checkpoint-restore/checkpointctl/lib"
     9  	"github.com/containers/common/libimage"
    10  	"github.com/containers/common/pkg/config"
    11  	"github.com/hanks177/podman/v4/libpod"
    12  	ann "github.com/hanks177/podman/v4/pkg/annotations"
    13  	"github.com/hanks177/podman/v4/pkg/checkpoint/crutils"
    14  	"github.com/hanks177/podman/v4/pkg/criu"
    15  	"github.com/hanks177/podman/v4/pkg/domain/entities"
    16  	"github.com/hanks177/podman/v4/pkg/specgen/generate"
    17  	"github.com/hanks177/podman/v4/pkg/specgenutil"
    18  	spec "github.com/opencontainers/runtime-spec/specs-go"
    19  	"github.com/pkg/errors"
    20  	"github.com/sirupsen/logrus"
    21  )
    22  
    23  // Prefixing the checkpoint/restore related functions with 'cr'
    24  
    25  func CRImportCheckpointTar(ctx context.Context, runtime *libpod.Runtime, restoreOptions entities.RestoreOptions) ([]*libpod.Container, error) {
    26  	// First get the container definition from the
    27  	// tarball to a temporary directory
    28  	dir, err := ioutil.TempDir("", "checkpoint")
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  	defer func() {
    33  		if err := os.RemoveAll(dir); err != nil {
    34  			logrus.Errorf("Could not recursively remove %s: %q", dir, err)
    35  		}
    36  	}()
    37  	if err := crutils.CRImportCheckpointConfigOnly(dir, restoreOptions.Import); err != nil {
    38  		return nil, err
    39  	}
    40  	return CRImportCheckpoint(ctx, runtime, restoreOptions, dir)
    41  }
    42  
    43  // CRImportCheckpoint it the function which imports the information
    44  // from checkpoint tarball and re-creates the container from that information
    45  func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOptions entities.RestoreOptions, dir string) ([]*libpod.Container, error) {
    46  	// Load spec.dump from temporary directory
    47  	dumpSpec := new(spec.Spec)
    48  	if _, err := metadata.ReadJSONFile(dumpSpec, dir, metadata.SpecDumpFile); err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	// Load config.dump from temporary directory
    53  	ctrConfig := new(libpod.ContainerConfig)
    54  	if _, err := metadata.ReadJSONFile(ctrConfig, dir, metadata.ConfigDumpFile); err != nil {
    55  		return nil, err
    56  	}
    57  
    58  	if ctrConfig.Pod != "" && restoreOptions.Pod == "" {
    59  		return nil, errors.New("cannot restore pod container without --pod")
    60  	}
    61  
    62  	if ctrConfig.Pod == "" && restoreOptions.Pod != "" {
    63  		return nil, errors.New("cannot restore non pod container into pod")
    64  	}
    65  
    66  	// This should not happen as checkpoints with these options are not exported.
    67  	if len(ctrConfig.Dependencies) > 0 {
    68  		return nil, errors.Errorf("Cannot import checkpoints of containers with dependencies")
    69  	}
    70  
    71  	// Volumes included in the checkpoint should not exist
    72  	if !restoreOptions.IgnoreVolumes {
    73  		for _, vol := range ctrConfig.NamedVolumes {
    74  			exists, err := runtime.HasVolume(vol.Name)
    75  			if err != nil {
    76  				return nil, err
    77  			}
    78  			if exists {
    79  				return nil, errors.Errorf("volume with name %s already exists. Use --ignore-volumes to not restore content of volumes", vol.Name)
    80  			}
    81  		}
    82  	}
    83  
    84  	if restoreOptions.IgnoreStaticIP || restoreOptions.IgnoreStaticMAC {
    85  		for net, opts := range ctrConfig.Networks {
    86  			if restoreOptions.IgnoreStaticIP {
    87  				opts.StaticIPs = nil
    88  			}
    89  			if restoreOptions.IgnoreStaticMAC {
    90  				opts.StaticMAC = nil
    91  			}
    92  			ctrConfig.Networks[net] = opts
    93  		}
    94  	}
    95  
    96  	ctrID := ctrConfig.ID
    97  	newName := false
    98  
    99  	// Check if the restored container gets a new name
   100  	if restoreOptions.Name != "" {
   101  		ctrConfig.ID = ""
   102  		ctrConfig.Name = restoreOptions.Name
   103  		newName = true
   104  	}
   105  
   106  	if restoreOptions.Pod != "" {
   107  		// Restoring into a Pod requires much newer versions of CRIU
   108  		if !criu.CheckForCriu(criu.PodCriuVersion) {
   109  			return nil, errors.Errorf("restoring containers into pods requires at least CRIU %d", criu.PodCriuVersion)
   110  		}
   111  		// The runtime also has to support it
   112  		if !crutils.CRRuntimeSupportsPodCheckpointRestore(runtime.GetOCIRuntimePath()) {
   113  			return nil, errors.Errorf("runtime %s does not support pod restore", runtime.GetOCIRuntimePath())
   114  		}
   115  		// Restoring into an existing Pod
   116  		ctrConfig.Pod = restoreOptions.Pod
   117  
   118  		// According to podman pod create a pod can share the following namespaces:
   119  		// cgroup, ipc, net, pid, uts
   120  		// Let's make sure we a restoring into a pod with the same shared namespaces.
   121  		pod, err := runtime.LookupPod(ctrConfig.Pod)
   122  		if err != nil {
   123  			return nil, errors.Wrapf(err, "pod %q cannot be retrieved", ctrConfig.Pod)
   124  		}
   125  
   126  		infraContainer, err := pod.InfraContainer()
   127  		if err != nil {
   128  			return nil, errors.Wrapf(err, "cannot retrieve infra container from pod %q", ctrConfig.Pod)
   129  		}
   130  
   131  		// If a namespaces was shared (!= "") it needs to be set to the new infrastructure container
   132  		// If the infrastructure container does not share the same namespaces as the to be restored
   133  		// container we abort.
   134  		if ctrConfig.IPCNsCtr != "" {
   135  			if !pod.SharesIPC() {
   136  				return nil, errors.Errorf("pod %s does not share the IPC namespace", ctrConfig.Pod)
   137  			}
   138  			ctrConfig.IPCNsCtr = infraContainer.ID()
   139  		}
   140  
   141  		if ctrConfig.NetNsCtr != "" {
   142  			if !pod.SharesNet() {
   143  				return nil, errors.Errorf("pod %s does not share the network namespace", ctrConfig.Pod)
   144  			}
   145  			ctrConfig.NetNsCtr = infraContainer.ID()
   146  			for net, opts := range ctrConfig.Networks {
   147  				opts.StaticIPs = nil
   148  				opts.StaticMAC = nil
   149  				ctrConfig.Networks[net] = opts
   150  			}
   151  			ctrConfig.StaticIP = nil
   152  			ctrConfig.StaticMAC = nil
   153  		}
   154  
   155  		if ctrConfig.PIDNsCtr != "" {
   156  			if !pod.SharesPID() {
   157  				return nil, errors.Errorf("pod %s does not share the PID namespace", ctrConfig.Pod)
   158  			}
   159  			ctrConfig.PIDNsCtr = infraContainer.ID()
   160  		}
   161  
   162  		if ctrConfig.UTSNsCtr != "" {
   163  			if !pod.SharesUTS() {
   164  				return nil, errors.Errorf("pod %s does not share the UTS namespace", ctrConfig.Pod)
   165  			}
   166  			ctrConfig.UTSNsCtr = infraContainer.ID()
   167  		}
   168  
   169  		if ctrConfig.CgroupNsCtr != "" {
   170  			if !pod.SharesCgroup() {
   171  				return nil, errors.Errorf("pod %s does not share the cgroup namespace", ctrConfig.Pod)
   172  			}
   173  			ctrConfig.CgroupNsCtr = infraContainer.ID()
   174  		}
   175  
   176  		// Change SELinux labels to infrastructure container labels
   177  		ctrConfig.MountLabel = infraContainer.MountLabel()
   178  		ctrConfig.ProcessLabel = infraContainer.ProcessLabel()
   179  
   180  		// Fix parent cgroup
   181  		cgroupPath, err := pod.CgroupPath()
   182  		if err != nil {
   183  			return nil, errors.Wrapf(err, "cannot retrieve cgroup path from pod %q", ctrConfig.Pod)
   184  		}
   185  		ctrConfig.CgroupParent = cgroupPath
   186  
   187  		oldPodID := dumpSpec.Annotations[ann.SandboxID]
   188  		// Fix up SandboxID in the annotations
   189  		dumpSpec.Annotations[ann.SandboxID] = ctrConfig.Pod
   190  		// Fix up CreateCommand
   191  		for i, c := range ctrConfig.CreateCommand {
   192  			if c == oldPodID {
   193  				ctrConfig.CreateCommand[i] = ctrConfig.Pod
   194  			}
   195  		}
   196  	}
   197  
   198  	if len(restoreOptions.PublishPorts) > 0 {
   199  		pubPorts, err := specgenutil.CreatePortBindings(restoreOptions.PublishPorts)
   200  		if err != nil {
   201  			return nil, err
   202  		}
   203  
   204  		ports, err := generate.ParsePortMapping(pubPorts, nil)
   205  		if err != nil {
   206  			return nil, err
   207  		}
   208  		ctrConfig.PortMappings = ports
   209  	}
   210  
   211  	pullOptions := &libimage.PullOptions{}
   212  	pullOptions.Writer = os.Stderr
   213  	if _, err := runtime.LibimageRuntime().Pull(ctx, ctrConfig.RootfsImageName, config.PullPolicyMissing, pullOptions); err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	// Now create a new container from the just loaded information
   218  	container, err := runtime.RestoreContainer(ctx, dumpSpec, ctrConfig)
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  
   223  	var containers []*libpod.Container
   224  	if container == nil {
   225  		return nil, nil
   226  	}
   227  
   228  	containerConfig := container.Config()
   229  	ctrName := ctrConfig.Name
   230  	if containerConfig.Name != ctrName {
   231  		return nil, errors.Errorf("Name of restored container (%s) does not match requested name (%s)", containerConfig.Name, ctrName)
   232  	}
   233  
   234  	if !newName {
   235  		// Only check ID for a restore with the same name.
   236  		// Using -n to request a new name for the restored container, will also create a new ID
   237  		if containerConfig.ID != ctrID {
   238  			return nil, errors.Errorf("ID of restored container (%s) does not match requested ID (%s)", containerConfig.ID, ctrID)
   239  		}
   240  	}
   241  
   242  	containers = append(containers, container)
   243  	return containers, nil
   244  }