Files
Adrian Reber bd819ef7dc Vendor in checkpointctl
checkpointctl contains common code to work with checkpoint images in
Podman, CRI-O and Kubernetes.

Use functions and definitions from checkpointctl where possible.

Signed-off-by: Adrian Reber <areber@redhat.com>
2021-03-02 17:00:06 +00:00

222 lines
7.0 KiB
Go

// SPDX-License-Identifier: Apache-2.0
package metadata
import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"time"
cnitypes "github.com/containernetworking/cni/pkg/types/current"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
type CheckpointedPod struct {
PodUID string `json:"io.kubernetes.pod.uid,omitempty"`
ID string `json:"SandboxID,omitempty"`
Name string `json:"io.kubernetes.pod.name,omitempty"`
TerminationGracePeriod int64 `json:"io.kubernetes.pod.terminationGracePeriod,omitempty"`
Namespace string `json:"io.kubernetes.pod.namespace,omitempty"`
ConfigSource string `json:"kubernetes.io/config.source,omitempty"`
ConfigSeen string `json:"kubernetes.io/config.seen,omitempty"`
Manager string `json:"io.container.manager,omitempty"`
Containers []CheckpointedContainer `json:"Containers"`
HostIP string `json:"hostIP,omitempty"`
PodIP string `json:"podIP,omitempty"`
PodIPs []string `json:"podIPs,omitempty"`
}
type CheckpointedContainer struct {
Name string `json:"io.kubernetes.container.name,omitempty"`
ID string `json:"id,omitempty"`
TerminationMessagePath string `json:"io.kubernetes.container.terminationMessagePath,omitempty"`
TerminationMessagePolicy string `json:"io.kubernetes.container.terminationMessagePolicy,omitempty"`
RestartCounter int32 `json:"io.kubernetes.container.restartCount,omitempty"`
TerminationMessagePathUID string `json:"terminationMessagePathUID,omitempty"`
Image string `json:"Image"`
}
type CheckpointMetadata struct {
Version int `json:"version"`
CheckpointedPods []CheckpointedPod
}
const (
// kubelet archive
CheckpointedPodsFile = "checkpointed.pods"
// container archive
ConfigDumpFile = "config.dump"
SpecDumpFile = "spec.dump"
NetworkStatusFile = "network.status"
CheckpointDirectory = "checkpoint"
RootFsDiffTar = "rootfs-diff.tar"
DeletedFilesFile = "deleted.files"
// pod archive
PodOptionsFile = "pod.options"
PodDumpFile = "pod.dump"
)
type CheckpointType int
const (
// The checkpoint archive contains a kubelet checkpoint
// One or multiple pods and kubelet metadata (checkpointed.pods)
Kubelet CheckpointType = iota
// The checkpoint archive contains one pod including one or multiple containers
Pod
// The checkpoint archive contains a single container
Container
Unknown
)
// This is a reduced copy of what Podman uses to store checkpoint metadata
type ContainerConfig struct {
ID string `json:"id"`
Name string `json:"name"`
RootfsImageName string `json:"rootfsImageName,omitempty"`
OCIRuntime string `json:"runtime,omitempty"`
CreatedTime time.Time `json:"createdTime"`
}
// This is metadata stored inside of a Pod checkpoint archive
type CheckpointedPodOptions struct {
Version int `json:"version"`
Containers []string `json:"containers,omitempty"`
MountLabel string `json:"mountLabel"`
ProcessLabel string `json:"processLabel"`
}
func DetectCheckpointArchiveType(checkpointDirectory string) (CheckpointType, error) {
_, err := os.Stat(filepath.Join(checkpointDirectory, CheckpointedPodsFile))
if err != nil && !os.IsNotExist(err) {
return Unknown, errors.Wrapf(err, "Failed to access %q\n", CheckpointedPodsFile)
}
if os.IsNotExist(err) {
return Container, nil
}
return Kubelet, nil
}
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 ReadContainerCheckpointNetworkStatus(checkpointDirectory string) ([]*cnitypes.Result, string, error) {
var networkStatus []*cnitypes.Result
networkStatusFile, err := ReadJSONFile(&networkStatus, checkpointDirectory, NetworkStatusFile)
return networkStatus, networkStatusFile, err
}
func ReadKubeletCheckpoints(checkpointsDirectory string) (*CheckpointMetadata, string, error) {
var checkpointMetadata CheckpointMetadata
checkpointMetadataPath, err := ReadJSONFile(&checkpointMetadata, checkpointsDirectory, CheckpointedPodsFile)
return &checkpointMetadata, checkpointMetadataPath, err
}
func GetIPFromNetworkStatus(networkStatus []*cnitypes.Result) net.IP {
if len(networkStatus) == 0 {
return nil
}
// Take the first IP address
if len(networkStatus[0].IPs) == 0 {
return nil
}
IP := networkStatus[0].IPs[0].Address.IP
return IP
}
func GetMACFromNetworkStatus(networkStatus []*cnitypes.Result) net.HardwareAddr {
if len(networkStatus) == 0 {
return nil
}
// Take the first device with a defined sandbox
if len(networkStatus[0].Interfaces) == 0 {
return nil
}
var MAC net.HardwareAddr
MAC = nil
for _, n := range networkStatus[0].Interfaces {
if n.Sandbox != "" {
MAC, _ = net.ParseMAC(n.Mac)
break
}
}
return MAC
}
// 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 "", errors.Wrapf(err, "Error marshalling JSON")
}
file = filepath.Join(dir, file)
if err := ioutil.WriteFile(file, fileJSON, 0o600); err != nil {
return "", errors.Wrapf(err, "Error writing to %q", file)
}
return file, nil
}
func ReadJSONFile(v interface{}, dir, file string) (string, error) {
file = filepath.Join(dir, file)
content, err := ioutil.ReadFile(file)
if err != nil {
return "", errors.Wrapf(err, "failed to read %s", file)
}
if err = json.Unmarshal(content, v); err != nil {
return "", errors.Wrapf(err, "failed to unmarshal %s", file)
}
return file, nil
}
func WriteKubeletCheckpointsMetadata(checkpointMetadata *CheckpointMetadata, dir string) error {
_, err := WriteJSONFile(checkpointMetadata, dir, CheckpointedPodsFile)
return err
}
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])
}