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