222 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			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])
 | |
| }
 |