Wire through CRI ContainerCheckpoint RPC
This connects the new CRI ContainerCheckpoint RPC to the existing internal checkpoint functions. With this commit it is possible to checkpoint a container in Kubernetes using the Forensic Container Checkpointing KEP (#2008): # curl X POST "https://localhost:10250/checkpoint/namespace/podId/container" Which will result in containerd creating a checkpoint in the location specified by Kubernetes (usually /var/lib/kubelet/checkpoints). This is a Linux only feature because CRIU only exists on Linux. Rewritten with the help of Phil Estes. Signed-off-by: Phil Estes <estesp@gmail.com> Signed-off-by: Adrian Reber <areber@redhat.com>
This commit is contained in:
145
vendor/github.com/checkpoint-restore/checkpointctl/lib/metadata.go
generated
vendored
Normal file
145
vendor/github.com/checkpoint-restore/checkpointctl/lib/metadata.go
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
const (
|
||||
// container archive
|
||||
ConfigDumpFile = "config.dump"
|
||||
SpecDumpFile = "spec.dump"
|
||||
NetworkStatusFile = "network.status"
|
||||
CheckpointDirectory = "checkpoint"
|
||||
CheckpointVolumesDirectory = "volumes"
|
||||
DevShmCheckpointTar = "devshm-checkpoint.tar"
|
||||
RootFsDiffTar = "rootfs-diff.tar"
|
||||
DeletedFilesFile = "deleted.files"
|
||||
DumpLogFile = "dump.log"
|
||||
RestoreLogFile = "restore.log"
|
||||
// pod archive
|
||||
PodOptionsFile = "pod.options"
|
||||
PodDumpFile = "pod.dump"
|
||||
// containerd only
|
||||
StatusFile = "status"
|
||||
// CRIU Images
|
||||
PagesPrefix = "pages-"
|
||||
AmdgpuPagesPrefix = "amdgpu-pages-"
|
||||
)
|
||||
|
||||
// This is a reduced copy of what Podman uses to store checkpoint metadata
|
||||
type ContainerConfig struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
RootfsImage string `json:"rootfsImage,omitempty"`
|
||||
RootfsImageRef string `json:"rootfsImageRef,omitempty"`
|
||||
RootfsImageName string `json:"rootfsImageName,omitempty"`
|
||||
OCIRuntime string `json:"runtime,omitempty"`
|
||||
CreatedTime time.Time `json:"createdTime"`
|
||||
CheckpointedAt time.Time `json:"checkpointedTime"`
|
||||
RestoredAt time.Time `json:"restoredTime"`
|
||||
Restored bool `json:"restored"`
|
||||
}
|
||||
|
||||
type ContainerdStatus struct {
|
||||
CreatedAt int64
|
||||
StartedAt int64
|
||||
FinishedAt int64
|
||||
ExitCode int32
|
||||
Pid uint32
|
||||
Reason string
|
||||
Message string
|
||||
}
|
||||
|
||||
// This structure is used by the KubernetesContainerCheckpointMetadata structure
|
||||
type KubernetesCheckpoint struct {
|
||||
Archive string `json:"archive,omitempty"`
|
||||
Size int64 `json:"size,omitempty"`
|
||||
Timestamp int64 `json:"timestamp,omitempty"`
|
||||
}
|
||||
|
||||
// This structure is the basis for Kubernetes to track how many checkpoints
|
||||
// for a certain container have been created.
|
||||
type KubernetesContainerCheckpointMetadata struct {
|
||||
PodFullName string `json:"podFullName,omitempty"`
|
||||
ContainerName string `json:"containerName,omitempty"`
|
||||
TotalSize int64 `json:"totalSize,omitempty"`
|
||||
Checkpoints []KubernetesCheckpoint `json:"checkpoints"`
|
||||
}
|
||||
|
||||
func ReadContainerCheckpointSpecDump(checkpointDirectory string) (*spec.Spec, string, error) {
|
||||
var specDump spec.Spec
|
||||
specDumpFile, err := ReadJSONFile(&specDump, checkpointDirectory, SpecDumpFile)
|
||||
|
||||
return &specDump, specDumpFile, err
|
||||
}
|
||||
|
||||
func ReadContainerCheckpointConfigDump(checkpointDirectory string) (*ContainerConfig, string, error) {
|
||||
var containerConfig ContainerConfig
|
||||
configDumpFile, err := ReadJSONFile(&containerConfig, checkpointDirectory, ConfigDumpFile)
|
||||
|
||||
return &containerConfig, configDumpFile, err
|
||||
}
|
||||
|
||||
func ReadContainerCheckpointDeletedFiles(checkpointDirectory string) ([]string, string, error) {
|
||||
var deletedFiles []string
|
||||
deletedFilesFile, err := ReadJSONFile(&deletedFiles, checkpointDirectory, DeletedFilesFile)
|
||||
|
||||
return deletedFiles, deletedFilesFile, err
|
||||
}
|
||||
|
||||
func ReadContainerCheckpointStatusFile(checkpointDirectory string) (*ContainerdStatus, string, error) {
|
||||
var containerdStatus ContainerdStatus
|
||||
statusFile, err := ReadJSONFile(&containerdStatus, checkpointDirectory, StatusFile)
|
||||
|
||||
return &containerdStatus, statusFile, err
|
||||
}
|
||||
|
||||
// WriteJSONFile marshalls and writes the given data to a JSON file
|
||||
func WriteJSONFile(v interface{}, dir, file string) (string, error) {
|
||||
fileJSON, err := json.MarshalIndent(v, "", " ")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error marshalling JSON: %w", err)
|
||||
}
|
||||
file = filepath.Join(dir, file)
|
||||
if err := os.WriteFile(file, fileJSON, 0o600); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func ReadJSONFile(v interface{}, dir, file string) (string, error) {
|
||||
file = filepath.Join(dir, file)
|
||||
content, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err = json.Unmarshal(content, v); err != nil {
|
||||
return "", fmt.Errorf("failed to unmarshal %s: %w", file, err)
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func ByteToString(b int64) string {
|
||||
const unit = 1024
|
||||
if b < unit {
|
||||
return fmt.Sprintf("%d B", b)
|
||||
}
|
||||
div, exp := int64(unit), 0
|
||||
for n := b / unit; n >= unit; n /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%.1f %ciB",
|
||||
float64(b)/float64(div), "KMGTPE"[exp])
|
||||
}
|
||||
Reference in New Issue
Block a user