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 }