github.com/containerd/nerdctl/v2@v2.0.0-beta.5.0.20240520001846-b5758f54fa28/pkg/cmd/system/info.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 system
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io"
    23  	"sort"
    24  	"strings"
    25  	"text/template"
    26  
    27  	"github.com/containerd/containerd"
    28  	"github.com/containerd/log"
    29  	"github.com/containerd/nerdctl/v2/pkg/api/types"
    30  	"golang.org/x/text/cases"
    31  	"golang.org/x/text/language"
    32  
    33  	"github.com/containerd/containerd/api/services/introspection/v1"
    34  	"github.com/containerd/nerdctl/v2/pkg/formatter"
    35  	"github.com/containerd/nerdctl/v2/pkg/infoutil"
    36  	"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
    37  	"github.com/containerd/nerdctl/v2/pkg/inspecttypes/native"
    38  	"github.com/containerd/nerdctl/v2/pkg/rootlessutil"
    39  	"github.com/containerd/nerdctl/v2/pkg/strutil"
    40  	"github.com/docker/go-units"
    41  )
    42  
    43  func Info(ctx context.Context, client *containerd.Client, options types.SystemInfoOptions) error {
    44  	var (
    45  		tmpl *template.Template
    46  		err  error
    47  	)
    48  	if options.Format != "" {
    49  		tmpl, err = formatter.ParseTemplate(options.Format)
    50  		if err != nil {
    51  			return err
    52  		}
    53  	}
    54  
    55  	var (
    56  		infoNative *native.Info
    57  		infoCompat *dockercompat.Info
    58  	)
    59  	switch options.Mode {
    60  	case "native":
    61  		di, err := infoutil.NativeDaemonInfo(ctx, client)
    62  		if err != nil {
    63  			return err
    64  		}
    65  		infoNative, err = fulfillNativeInfo(di, options.GOptions)
    66  		if err != nil {
    67  			return err
    68  		}
    69  	case "dockercompat":
    70  		infoCompat, err = infoutil.Info(ctx, client, options.GOptions.Snapshotter, options.GOptions.CgroupManager)
    71  		if err != nil {
    72  			return err
    73  		}
    74  	default:
    75  		return fmt.Errorf("unknown mode %q", options.Mode)
    76  	}
    77  
    78  	if tmpl != nil {
    79  		var x interface{} = infoNative
    80  		if infoCompat != nil {
    81  			x = infoCompat
    82  		}
    83  		w := options.Stdout
    84  		if err := tmpl.Execute(w, x); err != nil {
    85  			return err
    86  		}
    87  		_, err = fmt.Fprintln(w)
    88  		return err
    89  	}
    90  
    91  	switch options.Mode {
    92  	case "native":
    93  		return prettyPrintInfoNative(options.Stdout, infoNative)
    94  	case "dockercompat":
    95  		return prettyPrintInfoDockerCompat(options.Stdout, options.Stderr, infoCompat, options.GOptions)
    96  	}
    97  	return nil
    98  }
    99  
   100  func fulfillNativeInfo(di *native.DaemonInfo, globalOptions types.GlobalCommandOptions) (*native.Info, error) {
   101  	info := &native.Info{
   102  		Daemon: di,
   103  	}
   104  	info.Namespace = globalOptions.Namespace
   105  	info.Snapshotter = globalOptions.Snapshotter
   106  	info.CgroupManager = globalOptions.CgroupManager
   107  	info.Rootless = rootlessutil.IsRootless()
   108  	return info, nil
   109  }
   110  
   111  func prettyPrintInfoNative(w io.Writer, info *native.Info) error {
   112  	fmt.Fprintf(w, "Namespace:          %s\n", info.Namespace)
   113  	fmt.Fprintf(w, "Snapshotter:        %s\n", info.Snapshotter)
   114  	fmt.Fprintf(w, "Cgroup Manager:     %s\n", info.CgroupManager)
   115  	fmt.Fprintf(w, "Rootless:           %v\n", info.Rootless)
   116  	fmt.Fprintf(w, "containerd Version: %s (%s)\n", info.Daemon.Version.Version, info.Daemon.Version.Revision)
   117  	fmt.Fprintf(w, "containerd UUID:    %s\n", info.Daemon.Server.UUID)
   118  	var disabledPlugins, enabledPlugins []*introspection.Plugin
   119  	for _, f := range info.Daemon.Plugins.Plugins {
   120  		if f.InitErr == nil {
   121  			enabledPlugins = append(enabledPlugins, f)
   122  		} else {
   123  			disabledPlugins = append(disabledPlugins, f)
   124  		}
   125  	}
   126  	sorter := func(x []*introspection.Plugin) func(int, int) bool {
   127  		return func(i, j int) bool {
   128  			return x[i].Type+"."+x[i].ID < x[j].Type+"."+x[j].ID
   129  		}
   130  	}
   131  	sort.Slice(enabledPlugins, sorter(enabledPlugins))
   132  	sort.Slice(disabledPlugins, sorter(disabledPlugins))
   133  	fmt.Fprintln(w, "containerd Plugins:")
   134  	for _, f := range enabledPlugins {
   135  		fmt.Fprintf(w, " - %s.%s\n", f.Type, f.ID)
   136  	}
   137  	fmt.Fprintf(w, "containerd Plugins (disabled):\n")
   138  	for _, f := range disabledPlugins {
   139  		fmt.Fprintf(w, " - %s.%s\n", f.Type, f.ID)
   140  	}
   141  	return nil
   142  }
   143  
   144  func prettyPrintInfoDockerCompat(stdout io.Writer, stderr io.Writer, info *dockercompat.Info, globalOptions types.GlobalCommandOptions) error {
   145  	w := stdout
   146  	debug := globalOptions.Debug
   147  	fmt.Fprintf(w, "Client:\n")
   148  	fmt.Fprintf(w, " Namespace:\t%s\n", globalOptions.Namespace)
   149  	fmt.Fprintf(w, " Debug Mode:\t%v\n", debug)
   150  	fmt.Fprintln(w)
   151  	fmt.Fprintf(w, "Server:\n")
   152  	fmt.Fprintf(w, " Server Version: %s\n", info.ServerVersion)
   153  	// Storage Driver is not really Server concept for nerdctl, but mimics `docker info` output
   154  	fmt.Fprintf(w, " Storage Driver: %s\n", info.Driver)
   155  	fmt.Fprintf(w, " Logging Driver: %s\n", info.LoggingDriver)
   156  	fmt.Fprintf(w, " Cgroup Driver: %s\n", info.CgroupDriver)
   157  	fmt.Fprintf(w, " Cgroup Version: %s\n", info.CgroupVersion)
   158  	fmt.Fprintf(w, " Plugins:\n")
   159  	fmt.Fprintf(w, "  Log: %s\n", strings.Join(info.Plugins.Log, " "))
   160  	fmt.Fprintf(w, "  Storage: %s\n", strings.Join(info.Plugins.Storage, " "))
   161  	fmt.Fprintf(w, " Security Options:\n")
   162  	for _, s := range info.SecurityOptions {
   163  		m, err := strutil.ParseCSVMap(s)
   164  		if err != nil {
   165  			log.L.WithError(err).Warnf("unparsable security option %q", s)
   166  			continue
   167  		}
   168  		name := m["name"]
   169  		if name == "" {
   170  			log.L.Warnf("unparsable security option %q", s)
   171  			continue
   172  		}
   173  		fmt.Fprintf(w, "  %s\n", name)
   174  		for k, v := range m {
   175  			if k == "name" {
   176  				continue
   177  			}
   178  			fmt.Fprintf(w, "   %s: %s\n", cases.Title(language.English).String(k), v)
   179  		}
   180  	}
   181  	fmt.Fprintf(w, " Kernel Version: %s\n", info.KernelVersion)
   182  	fmt.Fprintf(w, " Operating System: %s\n", info.OperatingSystem)
   183  	fmt.Fprintf(w, " OSType: %s\n", info.OSType)
   184  	fmt.Fprintf(w, " Architecture: %s\n", info.Architecture)
   185  	fmt.Fprintf(w, " CPUs: %d\n", info.NCPU)
   186  	fmt.Fprintf(w, " Total Memory: %s\n", units.BytesSize(float64(info.MemTotal)))
   187  	fmt.Fprintf(w, " Name: %s\n", info.Name)
   188  	fmt.Fprintf(w, " ID: %s\n", info.ID)
   189  
   190  	fmt.Fprintln(w)
   191  	if len(info.Warnings) > 0 {
   192  		fmt.Fprintln(stderr, strings.Join(info.Warnings, "\n"))
   193  	}
   194  	return nil
   195  }