Skip to content

Commit

Permalink
Merge pull request #148 from adrianreber/2024-10-17-metadata
Browse files Browse the repository at this point in the history
Add option to print metadata information
  • Loading branch information
rst0git authored Oct 23, 2024
2 parents d2276c1 + 692adc3 commit c770752
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 6 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ $ checkpointctl show /var/lib/kubelet/checkpoints/checkpoint-counters_default-co
To retrieve low-level information about a container checkpoint, use the `checkpointctl inspect` command:

```console
$ checkpointctl inspect /tmp/ubuntu_looper.tar.gz --ps-tree
$ checkpointctl inspect /tmp/ubuntu_looper.tar.gz --ps-tree --metadata

awesome_booth
├── Image: docker.io/library/ubuntu:latest
Expand All @@ -65,6 +65,10 @@ awesome_booth
├── Engine: Podman
├── Checkpoint size: 2.8 MiB
├── Root FS diff size: 309.0 KiB
├── Metadata
│ └── Annotations
│ ├── io.container.manager: libpod
│ └── org.opencontainers.image.stopSignal: 15
└── Process tree
└── [1] bash
└── [5] su
Expand Down
7 changes: 7 additions & 0 deletions cmd/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ func Inspect() *cobra.Command {
"tree",
"Specify the output format: tree or json",
)
flags.BoolVar(
showMetdata,
"metadata",
false,
"Show metadata about the container",
)

return cmd
}
Expand All @@ -91,6 +97,7 @@ func inspect(cmd *cobra.Command, args []string) error {
*psTreeEnv = true
*files = true
*sockets = true
*showMetdata = true
}

requiredFiles := []string{metadata.SpecDumpFile, metadata.ConfigDumpFile}
Expand Down
1 change: 1 addition & 0 deletions cmd/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ var (
searchPattern *string = &internal.SearchPattern
searchRegexPattern *string = &internal.SearchRegexPattern
searchContext *int = &internal.SearchContext
showMetdata *bool = &internal.Metadata
)
1 change: 1 addition & 0 deletions internal/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ var (
SearchPattern string
SearchRegexPattern string
SearchContext int
Metadata bool
)
71 changes: 68 additions & 3 deletions internal/tree.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package internal

import (
"encoding/json"
"fmt"
"path/filepath"
"strings"
Expand All @@ -22,6 +23,7 @@ func RenderTreeView(tasks []Task) error {

tree := buildTree(info.containerInfo, info.configDump, info.archiveSizes)

checkpointDirectory := filepath.Join(task.OutputDir, metadata.CheckpointDirectory)
if Stats {
dumpStats, err := crit.GetDumpStats(task.OutputDir)
if err != nil {
Expand All @@ -31,8 +33,12 @@ func RenderTreeView(tasks []Task) error {
addDumpStatsToTree(tree, dumpStats)
}

if Metadata {
addPodInfoToTree(tree, info)
}

if PsTree {
c := crit.New(nil, nil, filepath.Join(task.OutputDir, "checkpoint"), false, false)
c := crit.New(nil, nil, checkpointDirectory, false, false)
psTree, err := c.ExplorePs()
if err != nil {
return fmt.Errorf("failed to get process tree: %w", err)
Expand All @@ -48,7 +54,7 @@ func RenderTreeView(tasks []Task) error {
if !Files {
return nil, nil
}
c := crit.New(nil, nil, filepath.Join(task.OutputDir, "checkpoint"), false, false)
c := crit.New(nil, nil, checkpointDirectory, false, false)
fds, err := c.ExploreFds()
if err != nil {
return nil, fmt.Errorf("failed to get file descriptors: %w", err)
Expand All @@ -63,7 +69,7 @@ func RenderTreeView(tasks []Task) error {
if !Sockets {
return nil, nil
}
c := crit.New(nil, nil, filepath.Join(task.OutputDir, "checkpoint"), false, false)
c := crit.New(nil, nil, checkpointDirectory, false, false)
sks, err := c.ExploreSk()
if err != nil {
return nil, fmt.Errorf("failed to get sockets: %w", err)
Expand Down Expand Up @@ -294,3 +300,62 @@ func updatePsTreeCommToCmdline(checkpointOutputDir string, psTree *crit.PsTree)
}
return nil
}

// Taken from the CRI API
type mountAnnotations struct {
ContainerPath string `json:"container_path,omitempty"`
HostPath string `json:"host_path,omitempty"`
Readonly bool `json:"readonly,omitempty"`
SelinuxRelabel bool `json:"selinux_relabel,omitempty"`
Propagation int `json:"propagation,omitempty"`
UidMappings []*int `json:"uidMappings,omitempty"`
GidMappings []*int `json:"gidMappings,omitempty"`
RecursiveReadOnly bool `json:"recursive_read_only,omitempty"`
}

func addPodInfoToTree(tree treeprint.Tree, info *checkpointInfo) {
podTree := tree.AddBranch("Metadata")
if len(info.containerInfo.Pod) > 0 {
podTree.AddBranch(fmt.Sprintf("Pod name: %s", info.containerInfo.Pod))
}
if len(info.containerInfo.Namespace) > 0 {
podTree.AddBranch(fmt.Sprintf("Kubernetes namespace: %s", info.containerInfo.Namespace))
}
if len(info.specDump.Annotations) > 0 {
annotationTree := podTree.AddBranch("Annotations")
for key := range info.specDump.Annotations {
switch key {
case "io.kubernetes.cri-o.Labels",
"io.kubernetes.cri-o.Annotations",
"io.kubernetes.cri-o.Metadata",
"kubectl.kubernetes.io/last-applied-configuration":
// We know that some annotations contain a JSON string we can pretty print
local := make(map[string]interface{})
if err := json.Unmarshal([]byte(info.specDump.Annotations[key]), &local); err != nil {
continue
}
localTree := annotationTree.AddBranch(key)
for labelKey := range local {
localTree.AddBranch(fmt.Sprintf("%s: %s", labelKey, local[labelKey]))
}
case "io.kubernetes.cri-o.Volumes":
// We know that some annotations contain a JSON string we can pretty print
var local []mountAnnotations
if err := json.Unmarshal([]byte(info.specDump.Annotations[key]), &local); err != nil {
fmt.Printf("error: %s", err)
}
localTree := annotationTree.AddBranch(key)
for _, mount := range local {
containerPath := localTree.AddBranch(mount.ContainerPath)
containerPath.AddBranch(fmt.Sprintf("host path: %s", mount.HostPath))
containerPath.AddBranch(fmt.Sprintf("read-only: %t", mount.Readonly))
containerPath.AddBranch(fmt.Sprintf("selinux relabel: %t", mount.SelinuxRelabel))
containerPath.AddBranch(fmt.Sprintf("recursive read-only: %t", mount.RecursiveReadOnly))
containerPath.AddBranch(fmt.Sprintf("propagation: %d", mount.Propagation))
}
default:
annotationTree.AddBranch(fmt.Sprintf("%s: %s", key, info.specDump.Annotations[key]))
}
}
}
}
5 changes: 3 additions & 2 deletions test/checkpointctl.bats
Original file line number Diff line number Diff line change
Expand Up @@ -449,13 +449,14 @@ function teardown() {
[[ ${lines[9]} == *"CRIU dump statistics"* ]]
[[ ${lines[13]} == *"Memwrite time"* ]]
[[ ${lines[14]} =~ [1-9] ]]
[[ ${lines[16]} == *"Process tree"* ]]
[[ ${lines[17]} == *"piggie"* ]]

expected_messages=(
"[REG 0]"
"[cwd]"
"[root]"
"Metadata"
"Process tree"
"piggie"
"Overview of mounts"
"Destination"
"proc"
Expand Down

0 comments on commit c770752

Please sign in to comment.