summaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/api/handlers/compat/images_build.go6
-rw-r--r--pkg/api/server/register_archive.go2
-rw-r--r--pkg/bindings/images/build.go6
-rw-r--r--pkg/checkpoint/checkpoint_restore.go34
-rw-r--r--pkg/checkpoint/crutils/checkpoint_restore_utils.go191
5 files changed, 213 insertions, 26 deletions
diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go
index d79b100e8..009fcf7e8 100644
--- a/pkg/api/handlers/compat/images_build.go
+++ b/pkg/api/handlers/compat/images_build.go
@@ -104,6 +104,7 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
Squash bool `schema:"squash"`
Tag []string `schema:"t"`
Target string `schema:"target"`
+ Timestamp int64 `schema:"timestamp"`
}{
Dockerfile: "Dockerfile",
Registry: "docker.io",
@@ -318,6 +319,11 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
Target: query.Target,
}
+ if _, found := r.URL.Query()["timestamp"]; found {
+ ts := time.Unix(query.Timestamp, 0)
+ buildOptions.Timestamp = &ts
+ }
+
runCtx, cancel := context.WithCancel(context.Background())
var imageID string
go func() {
diff --git a/pkg/api/server/register_archive.go b/pkg/api/server/register_archive.go
index 2a5cfba0b..2ac126644 100644
--- a/pkg/api/server/register_archive.go
+++ b/pkg/api/server/register_archive.go
@@ -91,7 +91,7 @@ func (s *APIServer) registerArchiveHandlers(r *mux.Router) error {
Libpod
*/
- // swagger:operation POST /libpod/containers/{name}/archive libpod libpodPutArchive
+ // swagger:operation PUT /libpod/containers/{name}/archive libpod libpodPutArchive
// ---
// summary: Copy files into a container
// description: Copy a tar archive of files into a container
diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go
index 6e16461e5..27706fd2c 100644
--- a/pkg/bindings/images/build.go
+++ b/pkg/bindings/images/build.go
@@ -185,6 +185,12 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO
if options.Squash {
params.Set("squash", "1")
}
+
+ if options.Timestamp != nil {
+ t := *options.Timestamp
+ params.Set("timestamp", strconv.FormatInt(t.Unix(), 10))
+ }
+
var (
headers map[string]string
err error
diff --git a/pkg/checkpoint/checkpoint_restore.go b/pkg/checkpoint/checkpoint_restore.go
index 6285680c0..77a993128 100644
--- a/pkg/checkpoint/checkpoint_restore.go
+++ b/pkg/checkpoint/checkpoint_restore.go
@@ -4,15 +4,14 @@ import (
"context"
"io/ioutil"
"os"
- "path/filepath"
+ metadata "github.com/checkpoint-restore/checkpointctl/lib"
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/libpod/image"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/containers/podman/v3/pkg/errorhandling"
"github.com/containers/podman/v3/pkg/util"
"github.com/containers/storage/pkg/archive"
- jsoniter "github.com/json-iterator/go"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -20,21 +19,6 @@ import (
// Prefixing the checkpoint/restore related functions with 'cr'
-// crImportFromJSON imports the JSON files stored in the exported
-// checkpoint tarball
-func crImportFromJSON(filePath string, v interface{}) error {
- content, err := ioutil.ReadFile(filePath)
- if err != nil {
- return errors.Wrap(err, "failed to read container definition for restore")
- }
- json := jsoniter.ConfigCompatibleWithStandardLibrary
- if err = json.Unmarshal(content, v); err != nil {
- return errors.Wrapf(err, "failed to unmarshal container definition %s for restore", filePath)
- }
-
- return nil
-}
-
// 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) ([]*libpod.Container, error) {
@@ -48,13 +32,13 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt
options := &archive.TarOptions{
// Here we only need the files config.dump and spec.dump
ExcludePatterns: []string{
- "checkpoint",
- "artifacts",
- "ctr.log",
- "rootfs-diff.tar",
- "network.status",
- "deleted.files",
"volumes",
+ "ctr.log",
+ "artifacts",
+ metadata.RootFsDiffTar,
+ metadata.DeletedFilesFile,
+ metadata.NetworkStatusFile,
+ metadata.CheckpointDirectory,
},
}
dir, err := ioutil.TempDir("", "checkpoint")
@@ -73,13 +57,13 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt
// Load spec.dump from temporary directory
dumpSpec := new(spec.Spec)
- if err := crImportFromJSON(filepath.Join(dir, "spec.dump"), dumpSpec); err != nil {
+ if _, err := metadata.ReadJSONFile(dumpSpec, dir, metadata.SpecDumpFile); err != nil {
return nil, err
}
// Load config.dump from temporary directory
config := new(libpod.ContainerConfig)
- if err = crImportFromJSON(filepath.Join(dir, "config.dump"), config); err != nil {
+ if _, err = metadata.ReadJSONFile(config, dir, metadata.ConfigDumpFile); err != nil {
return nil, err
}
diff --git a/pkg/checkpoint/crutils/checkpoint_restore_utils.go b/pkg/checkpoint/crutils/checkpoint_restore_utils.go
new file mode 100644
index 000000000..53ff55865
--- /dev/null
+++ b/pkg/checkpoint/crutils/checkpoint_restore_utils.go
@@ -0,0 +1,191 @@
+package crutils
+
+import (
+ "io"
+ "os"
+ "os/exec"
+ "path/filepath"
+
+ metadata "github.com/checkpoint-restore/checkpointctl/lib"
+ "github.com/containers/storage/pkg/archive"
+ "github.com/opencontainers/selinux/go-selinux/label"
+ "github.com/pkg/errors"
+)
+
+// This file mainly exist to make the checkpoint/restore functions
+// available for other users. One possible candidate would be CRI-O.
+
+// CRImportCheckpointWithoutConfig imports the checkpoint archive (input)
+// into the directory destination without "config.dump" and "spec.dump"
+func CRImportCheckpointWithoutConfig(destination, input string) error {
+ archiveFile, err := os.Open(input)
+ if err != nil {
+ return errors.Wrapf(err, "Failed to open checkpoint archive %s for import", input)
+ }
+
+ defer archiveFile.Close()
+ options := &archive.TarOptions{
+ ExcludePatterns: []string{
+ // Import everything else besides the container config
+ metadata.ConfigDumpFile,
+ metadata.SpecDumpFile,
+ },
+ }
+ if err = archive.Untar(archiveFile, destination, options); err != nil {
+ return errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", input)
+ }
+
+ return nil
+}
+
+// CRRemoveDeletedFiles loads the list of deleted files and if
+// it exists deletes all files listed.
+func CRRemoveDeletedFiles(id, baseDirectory, containerRootDirectory string) error {
+ deletedFiles, _, err := metadata.ReadContainerCheckpointDeletedFiles(baseDirectory)
+ if os.IsNotExist(errors.Unwrap(errors.Unwrap(err))) {
+ // No files to delete. Just return
+ return nil
+ }
+
+ if err != nil {
+ return errors.Wrapf(err, "failed to read deleted files file")
+ }
+
+ for _, deleteFile := range deletedFiles {
+ // Using RemoveAll as deletedFiles, which is generated from 'podman diff'
+ // lists completely deleted directories as a single entry: 'D /root'.
+ if err := os.RemoveAll(filepath.Join(containerRootDirectory, deleteFile)); err != nil {
+ return errors.Wrapf(err, "failed to delete files from container %s during restore", id)
+ }
+ }
+
+ return nil
+}
+
+// CRApplyRootFsDiffTar applies the tar archive found in baseDirectory with the
+// root file system changes on top of containerRootDirectory
+func CRApplyRootFsDiffTar(baseDirectory, containerRootDirectory string) error {
+ rootfsDiffPath := filepath.Join(baseDirectory, metadata.RootFsDiffTar)
+ if _, err := os.Stat(rootfsDiffPath); err != nil {
+ // Only do this if a rootfs-diff.tar actually exists
+ return nil
+ }
+
+ rootfsDiffFile, err := os.Open(rootfsDiffPath)
+ if err != nil {
+ return errors.Wrap(err, "failed to open root file-system diff file")
+ }
+ defer rootfsDiffFile.Close()
+
+ if err := archive.Untar(rootfsDiffFile, containerRootDirectory, nil); err != nil {
+ return errors.Wrapf(err, "failed to apply root file-system diff file %s", rootfsDiffPath)
+ }
+
+ return nil
+}
+
+// CRCreateRootFsDiffTar goes through the 'changes' and can create two files:
+// * metadata.RootFsDiffTar will contain all new and changed files
+// * metadata.DeletedFilesFile will contain a list of deleted files
+// With these two files it is possible to restore the container file system to the same
+// state it was during checkpointing.
+// Changes to directories (owner, mode) are not handled.
+func CRCreateRootFsDiffTar(changes *[]archive.Change, mountPoint, destination string) (includeFiles []string, err error) {
+ if len(*changes) == 0 {
+ return includeFiles, nil
+ }
+
+ var rootfsIncludeFiles []string
+ var deletedFiles []string
+
+ rootfsDiffPath := filepath.Join(destination, metadata.RootFsDiffTar)
+
+ for _, file := range *changes {
+ if file.Kind == archive.ChangeAdd {
+ rootfsIncludeFiles = append(rootfsIncludeFiles, file.Path)
+ continue
+ }
+ if file.Kind == archive.ChangeDelete {
+ deletedFiles = append(deletedFiles, file.Path)
+ continue
+ }
+ fileName, err := os.Stat(file.Path)
+ if err != nil {
+ continue
+ }
+ if !fileName.IsDir() && file.Kind == archive.ChangeModify {
+ rootfsIncludeFiles = append(rootfsIncludeFiles, file.Path)
+ continue
+ }
+ }
+
+ if len(rootfsIncludeFiles) > 0 {
+ rootfsTar, err := archive.TarWithOptions(mountPoint, &archive.TarOptions{
+ Compression: archive.Uncompressed,
+ IncludeSourceDir: true,
+ IncludeFiles: rootfsIncludeFiles,
+ })
+ if err != nil {
+ return includeFiles, errors.Wrapf(err, "error exporting root file-system diff to %q", rootfsDiffPath)
+ }
+ rootfsDiffFile, err := os.Create(rootfsDiffPath)
+ if err != nil {
+ return includeFiles, errors.Wrapf(err, "error creating root file-system diff file %q", rootfsDiffPath)
+ }
+ defer rootfsDiffFile.Close()
+ if _, err = io.Copy(rootfsDiffFile, rootfsTar); err != nil {
+ return includeFiles, err
+ }
+
+ includeFiles = append(includeFiles, metadata.RootFsDiffTar)
+ }
+
+ if len(deletedFiles) == 0 {
+ return includeFiles, nil
+ }
+
+ if _, err := metadata.WriteJSONFile(deletedFiles, destination, metadata.DeletedFilesFile); err != nil {
+ return includeFiles, nil
+ }
+
+ includeFiles = append(includeFiles, metadata.DeletedFilesFile)
+
+ return includeFiles, nil
+}
+
+// CRCreateFileWithLabel creates an empty file and sets the corresponding ('fileLabel')
+// SELinux label on the file.
+// This is necessary for CRIU log files because CRIU infects the processes in
+// the container with a 'parasite' and this will also try to write to the log files
+// from the context of the container processes.
+func CRCreateFileWithLabel(directory, fileName, fileLabel string) error {
+ logFileName := filepath.Join(directory, fileName)
+
+ logFile, err := os.OpenFile(logFileName, os.O_CREATE, 0o600)
+ if err != nil {
+ return errors.Wrapf(err, "failed to create file %q", logFileName)
+ }
+ defer logFile.Close()
+ if err = label.SetFileLabel(logFileName, fileLabel); err != nil {
+ return errors.Wrapf(err, "failed to label file %q", logFileName)
+ }
+
+ return nil
+}
+
+// CRRuntimeSupportsCheckpointRestore tests if the given runtime at 'runtimePath'
+// supports checkpointing. The checkpoint restore interface has no definition
+// but crun implements all commands just as runc does. Whathh runc does it the
+// official definition of the checkpoint/restore interface.
+func CRRuntimeSupportsCheckpointRestore(runtimePath string) bool {
+ // Check if the runtime implements checkpointing. Currently only
+ // runc's and crun's checkpoint/restore implementation is supported.
+ cmd := exec.Command(runtimePath, "checkpoint", "--help")
+ if err := cmd.Start(); err != nil {
+ return false
+ }
+ if err := cmd.Wait(); err == nil {
+ return true
+ }
+ return false
+}