github.com/containerd/nerdctl/v2@v2.0.0-beta.5.0.20240520001846-b5758f54fa28/pkg/cmd/container/inspect.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package container 18 19 import ( 20 "context" 21 "fmt" 22 "time" 23 24 "github.com/containerd/containerd" 25 "github.com/containerd/containerd/snapshots" 26 "github.com/containerd/log" 27 "github.com/containerd/nerdctl/v2/pkg/api/types" 28 "github.com/containerd/nerdctl/v2/pkg/containerinspector" 29 "github.com/containerd/nerdctl/v2/pkg/formatter" 30 "github.com/containerd/nerdctl/v2/pkg/idutil/containerwalker" 31 "github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat" 32 ) 33 34 // Inspect prints detailed information for each container in `containers`. 35 func Inspect(ctx context.Context, client *containerd.Client, containers []string, options types.ContainerInspectOptions) error { 36 f := &containerInspector{ 37 mode: options.Mode, 38 size: options.Size, 39 snapshotter: client.SnapshotService(options.GOptions.Snapshotter), 40 } 41 42 walker := &containerwalker.ContainerWalker{ 43 Client: client, 44 OnFound: f.Handler, 45 } 46 47 err := walker.WalkAll(ctx, containers, true) 48 if len(f.entries) > 0 { 49 if formatErr := formatter.FormatSlice(options.Format, options.Stdout, f.entries); formatErr != nil { 50 log.L.Error(formatErr) 51 } 52 } 53 54 return err 55 } 56 57 type containerInspector struct { 58 mode string 59 size bool 60 snapshotter snapshots.Snapshotter 61 entries []interface{} 62 } 63 64 // resourceTotal will return: 65 // - the Usage value of the resource referenced by ID 66 // - the cumulative Usage value of the resource, and all parents, recursively 67 // Typically, for a running container, this will equal the size of the read-write layer, plus the sum of the size of all layers in the base image 68 func resourceTotal(ctx context.Context, snapshotter snapshots.Snapshotter, resourceID string) (snapshots.Usage, snapshots.Usage, error) { 69 var first snapshots.Usage 70 var total snapshots.Usage 71 var info snapshots.Info 72 73 for next := resourceID; next != ""; next = info.Parent { 74 // Get the resource usage info 75 usage, err := snapshotter.Usage(ctx, next) 76 if err != nil { 77 return first, total, err 78 } 79 // In case that's the first one, store that 80 if next == resourceID { 81 first = usage 82 } 83 // And increment totals 84 total.Size += usage.Size 85 total.Inodes += usage.Inodes 86 87 // Now, get the parent, if any and iterate 88 info, err = snapshotter.Stat(ctx, next) 89 if err != nil { 90 return first, total, err 91 } 92 } 93 94 return first, total, nil 95 } 96 97 func (x *containerInspector) Handler(ctx context.Context, found containerwalker.Found) error { 98 ctx, cancel := context.WithTimeout(ctx, 5*time.Second) 99 defer cancel() 100 101 n, err := containerinspector.Inspect(ctx, found.Container) 102 if err != nil { 103 return err 104 } 105 switch x.mode { 106 case "native": 107 x.entries = append(x.entries, n) 108 case "dockercompat": 109 d, err := dockercompat.ContainerFromNative(n) 110 if err != nil { 111 return err 112 } 113 if x.size { 114 resourceUsage, allResourceUsage, err := resourceTotal(ctx, x.snapshotter, d.ID) 115 if err == nil { 116 d.SizeRw = &resourceUsage.Size 117 d.SizeRootFs = &allResourceUsage.Size 118 } 119 } 120 x.entries = append(x.entries, d) 121 return err 122 default: 123 return fmt.Errorf("unknown mode %q", x.mode) 124 } 125 return nil 126 }