github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/checkpoint/checkpoint_restore.go (about) 1 package checkpoint 2 3 import ( 4 "context" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 9 "github.com/containers/podman/v2/libpod" 10 "github.com/containers/podman/v2/libpod/image" 11 "github.com/containers/podman/v2/pkg/errorhandling" 12 "github.com/containers/podman/v2/pkg/util" 13 "github.com/containers/storage/pkg/archive" 14 jsoniter "github.com/json-iterator/go" 15 spec "github.com/opencontainers/runtime-spec/specs-go" 16 "github.com/pkg/errors" 17 "github.com/sirupsen/logrus" 18 ) 19 20 // Prefixing the checkpoint/restore related functions with 'cr' 21 22 // crImportFromJSON imports the JSON files stored in the exported 23 // checkpoint tarball 24 func crImportFromJSON(filePath string, v interface{}) error { 25 content, err := ioutil.ReadFile(filePath) 26 if err != nil { 27 return errors.Wrap(err, "failed to read container definition for restore") 28 } 29 json := jsoniter.ConfigCompatibleWithStandardLibrary 30 if err = json.Unmarshal(content, v); err != nil { 31 return errors.Wrapf(err, "failed to unmarshal container definition %s for restore", filePath) 32 } 33 34 return nil 35 } 36 37 // CRImportCheckpoint it the function which imports the information 38 // from checkpoint tarball and re-creates the container from that information 39 func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input string, name string) ([]*libpod.Container, error) { 40 // First get the container definition from the 41 // tarball to a temporary directory 42 archiveFile, err := os.Open(input) 43 if err != nil { 44 return nil, errors.Wrap(err, "failed to open checkpoint archive for import") 45 } 46 defer errorhandling.CloseQuiet(archiveFile) 47 options := &archive.TarOptions{ 48 // Here we only need the files config.dump and spec.dump 49 ExcludePatterns: []string{ 50 "checkpoint", 51 "artifacts", 52 "ctr.log", 53 "rootfs-diff.tar", 54 "network.status", 55 "deleted.files", 56 }, 57 } 58 dir, err := ioutil.TempDir("", "checkpoint") 59 if err != nil { 60 return nil, err 61 } 62 defer func() { 63 if err := os.RemoveAll(dir); err != nil { 64 logrus.Errorf("could not recursively remove %s: %q", dir, err) 65 } 66 }() 67 err = archive.Untar(archiveFile, dir, options) 68 if err != nil { 69 return nil, errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", input) 70 } 71 72 // Load spec.dump from temporary directory 73 dumpSpec := new(spec.Spec) 74 if err := crImportFromJSON(filepath.Join(dir, "spec.dump"), dumpSpec); err != nil { 75 return nil, err 76 } 77 78 // Load config.dump from temporary directory 79 config := new(libpod.ContainerConfig) 80 if err = crImportFromJSON(filepath.Join(dir, "config.dump"), config); err != nil { 81 return nil, err 82 } 83 84 // This should not happen as checkpoints with these options are not exported. 85 if (len(config.Dependencies) > 0) || (len(config.NamedVolumes) > 0) { 86 return nil, errors.Errorf("Cannot import checkpoints of containers with named volumes or dependencies") 87 } 88 89 ctrID := config.ID 90 newName := false 91 92 // Check if the restored container gets a new name 93 if name != "" { 94 config.ID = "" 95 config.Name = name 96 newName = true 97 } 98 99 ctrName := config.Name 100 101 // The code to load the images is copied from create.go 102 // In create.go this only set if '--quiet' does not exist. 103 writer := os.Stderr 104 rtc, err := runtime.GetConfig() 105 if err != nil { 106 return nil, err 107 } 108 109 _, err = runtime.ImageRuntime().New(ctx, config.RootfsImageName, rtc.Engine.SignaturePolicyPath, "", writer, nil, image.SigningOptions{}, nil, util.PullImageMissing) 110 if err != nil { 111 return nil, err 112 } 113 114 // Now create a new container from the just loaded information 115 container, err := runtime.RestoreContainer(ctx, dumpSpec, config) 116 if err != nil { 117 return nil, err 118 } 119 120 var containers []*libpod.Container 121 if container == nil { 122 return nil, nil 123 } 124 125 containerConfig := container.Config() 126 if containerConfig.Name != ctrName { 127 return nil, errors.Errorf("Name of restored container (%s) does not match requested name (%s)", containerConfig.Name, ctrName) 128 } 129 130 if !newName { 131 // Only check ID for a restore with the same name. 132 // Using -n to request a new name for the restored container, will also create a new ID 133 if containerConfig.ID != ctrID { 134 return nil, errors.Errorf("ID of restored container (%s) does not match requested ID (%s)", containerConfig.ID, ctrID) 135 } 136 } 137 138 // Check if the ExitCommand points to the correct container ID 139 if containerConfig.ExitCommand[len(containerConfig.ExitCommand)-1] != containerConfig.ID { 140 return nil, errors.Errorf("'ExitCommandID' uses ID %s instead of container ID %s", containerConfig.ExitCommand[len(containerConfig.ExitCommand)-1], containerConfig.ID) 141 } 142 143 containers = append(containers, container) 144 return containers, nil 145 }