@@ -88,10 +88,13 @@ type DisplayNode struct {
8888 MAC string `json:"mac,omitempty"`
8989 CheckpointSize CheckpointSize `json:"checkpoint_size"`
9090 CriuDumpStatistics * StatsNode `json:"statistics,omitempty"`
91+ Metadata * MetadataNode `json:"metadata,omitempty"`
9192 ProcessTree * PsNode `json:"process_tree,omitempty"`
9293 FileDescriptors []FdNode `json:"file_descriptors,omitempty"`
9394 Sockets []SkNode `json:"sockets,omitempty"`
9495 Mounts []MountNode `json:"mounts,omitempty"`
96+ // Internal fields for tree rendering (not serialized to JSON)
97+ checkpointFilePath string
9598}
9699
97100type MountNode struct {
@@ -100,22 +103,32 @@ type MountNode struct {
100103 Source string `json:"source"`
101104}
102105
103- func RenderJSONView (tasks []Task ) error {
106+ type MetadataNode struct {
107+ PodName string `json:"pod_name,omitempty"`
108+ KubernetesNamespace string `json:"kubernetes_namespace,omitempty"`
109+ Annotations map [string ]string `json:"annotations,omitempty"`
110+ }
111+
112+ // CollectCheckpointData collects all checkpoint data into DisplayNode structures.
113+ // This is the single source of truth for checkpoint data that can be rendered
114+ // in multiple formats (JSON, tree, etc.).
115+ func CollectCheckpointData (tasks []Task ) ([]DisplayNode , error ) {
104116 var result []DisplayNode
105117
106118 for _ , task := range tasks {
107119 info , err := getCheckpointInfo (task )
108120 if err != nil {
109- return err
121+ return nil , err
110122 }
111123
112124 node := DisplayNode {
113- ContainerName : info .containerInfo .Name ,
114- Image : info .configDump .RootfsImageName ,
115- ID : info .configDump .ID ,
116- Runtime : info .configDump .OCIRuntime ,
117- Created : info .containerInfo .Created ,
118- Engine : info .containerInfo .Engine ,
125+ ContainerName : info .containerInfo .Name ,
126+ Image : info .configDump .RootfsImageName ,
127+ ID : info .configDump .ID ,
128+ Runtime : info .configDump .OCIRuntime ,
129+ Created : info .containerInfo .Created ,
130+ Engine : info .containerInfo .Engine ,
131+ checkpointFilePath : task .CheckpointFilePath ,
119132 }
120133
121134 if ! info .configDump .CheckpointedAt .IsZero () {
@@ -150,7 +163,7 @@ func RenderJSONView(tasks []Task) error {
150163 if Stats {
151164 dumpStats , err := crit .GetDumpStats (task .OutputDir )
152165 if err != nil {
153- return fmt .Errorf ("failed to get dump statistics: %w" , err )
166+ return nil , fmt .Errorf ("failed to get dump statistics: %w" , err )
154167 }
155168
156169 statsNode := StatsNode {
@@ -165,49 +178,73 @@ func RenderJSONView(tasks []Task) error {
165178 node .CriuDumpStatistics = & statsNode
166179 }
167180
181+ if Metadata {
182+ metadataNode := MetadataNode {}
183+ if info .containerInfo .Pod != "" {
184+ metadataNode .PodName = info .containerInfo .Pod
185+ }
186+ if info .containerInfo .Namespace != "" {
187+ metadataNode .KubernetesNamespace = info .containerInfo .Namespace
188+ }
189+ if len (info .specDump .Annotations ) > 0 {
190+ metadataNode .Annotations = info .specDump .Annotations
191+ }
192+ node .Metadata = & metadataNode
193+ }
194+
195+ checkpointDirectory := filepath .Join (task .OutputDir , "checkpoint" )
196+
168197 if PsTree {
169- psTree , err := crit .New (nil , nil , filepath . Join ( task . OutputDir , "checkpoint" ) , false , false ).ExplorePs ()
198+ psTree , err := crit .New (nil , nil , checkpointDirectory , false , false ).ExplorePs ()
170199 if err != nil {
171- return fmt .Errorf ("failed to get process tree: %w" , err )
200+ return nil , fmt .Errorf ("failed to get process tree: %w" , err )
172201 }
173202
174203 psTreeNode , err := buildJSONPsTree (psTree , task .OutputDir )
175204 if err != nil {
176- return fmt .Errorf ("failed to get process tree: %w" , err )
205+ return nil , fmt .Errorf ("failed to get process tree: %w" , err )
177206 }
178207
179208 node .ProcessTree = & psTreeNode
180209 }
181210
182211 if Files {
183- fds , err := crit .New (nil , nil , filepath . Join ( task . OutputDir , "checkpoint" ) , false , false ).ExploreFds ()
212+ fds , err := crit .New (nil , nil , checkpointDirectory , false , false ).ExploreFds ()
184213 if err != nil {
185- return fmt .Errorf ("failed to get file descriptors: %w" , err )
214+ return nil , fmt .Errorf ("failed to get file descriptors: %w" , err )
186215 }
187216
188217 node .FileDescriptors = buildJSONFds (fds )
189218 }
190219
191220 if Sockets {
192- fds , err := crit .New (nil , nil , filepath . Join ( task . OutputDir , "checkpoint" ) , false , false ).ExploreSk ()
221+ sks , err := crit .New (nil , nil , checkpointDirectory , false , false ).ExploreSk ()
193222 if err != nil {
194- return fmt .Errorf ("failed to get sockets: %w" , err )
223+ return nil , fmt .Errorf ("failed to get sockets: %w" , err )
195224 }
196225
197- node .Sockets , err = buildJSONSks (fds )
226+ node .Sockets , err = buildJSONSks (sks )
198227 if err != nil {
199- return fmt .Errorf ("failed to build sockets: %w" , err )
228+ return nil , fmt .Errorf ("failed to build sockets: %w" , err )
200229 }
201230 }
202231
203232 if Mounts {
204- specDump := info .specDump
205- node .Mounts = buildJSONMounts (specDump )
233+ node .Mounts = buildJSONMounts (info .specDump )
206234 }
207235
208236 result = append (result , node )
209237 }
210238
239+ return result , nil
240+ }
241+
242+ func RenderJSONView (tasks []Task ) error {
243+ result , err := CollectCheckpointData (tasks )
244+ if err != nil {
245+ return err
246+ }
247+
211248 jsonData , err := json .MarshalIndent (result , "" , " " )
212249 if err != nil {
213250 return err
@@ -269,7 +306,7 @@ func buildJSONPsNode(psTree *crit.PsTree, checkpointOutputDir string) (PsNode, e
269306 if PsTreeCmd {
270307 cmdline , err := getCmdline (checkpointOutputDir , psTree .PID )
271308 if err != nil {
272- return PsNode {}, err
309+ return PsNode {}, fmt . Errorf ( "failed to process command line arguments: %w" , err )
273310 }
274311 node .Cmdline = cmdline
275312 }
0 commit comments