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  }