github.com/containerd/Containerd@v1.4.13/cmd/ctr/commands/plugins/plugins.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 plugins
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"sort"
    23  	"strings"
    24  	"text/tabwriter"
    25  
    26  	"github.com/containerd/containerd/api/types"
    27  	"github.com/containerd/containerd/cmd/ctr/commands"
    28  	"github.com/containerd/containerd/platforms"
    29  	v1 "github.com/opencontainers/image-spec/specs-go/v1"
    30  	"github.com/urfave/cli"
    31  	"google.golang.org/grpc/codes"
    32  )
    33  
    34  // Command is a cli command that outputs plugin information
    35  var Command = cli.Command{
    36  	Name:    "plugins",
    37  	Aliases: []string{"plugin"},
    38  	Usage:   "provides information about containerd plugins",
    39  	Subcommands: []cli.Command{
    40  		listCommand,
    41  	},
    42  }
    43  
    44  var listCommand = cli.Command{
    45  	Name:    "list",
    46  	Aliases: []string{"ls"},
    47  	Usage:   "lists containerd plugins",
    48  	Flags: []cli.Flag{
    49  		cli.BoolFlag{
    50  			Name:  "quiet,q",
    51  			Usage: "print only the plugin ids",
    52  		},
    53  		cli.BoolFlag{
    54  			Name:  "detailed,d",
    55  			Usage: "print detailed information about each plugin",
    56  		},
    57  	},
    58  	Action: func(context *cli.Context) error {
    59  		var (
    60  			quiet    = context.Bool("quiet")
    61  			detailed = context.Bool("detailed")
    62  		)
    63  		client, ctx, cancel, err := commands.NewClient(context)
    64  		if err != nil {
    65  			return err
    66  		}
    67  		defer cancel()
    68  		ps := client.IntrospectionService()
    69  		response, err := ps.Plugins(ctx, context.Args())
    70  		if err != nil {
    71  			return err
    72  		}
    73  		if quiet {
    74  			for _, plugin := range response.Plugins {
    75  				fmt.Println(plugin.ID)
    76  			}
    77  			return nil
    78  		}
    79  		w := tabwriter.NewWriter(os.Stdout, 4, 8, 4, ' ', 0)
    80  		if detailed {
    81  			first := true
    82  			for _, plugin := range response.Plugins {
    83  				if !first {
    84  					fmt.Fprintln(w, "\t\t\t")
    85  				}
    86  				first = false
    87  				fmt.Fprintln(w, "Type:\t", plugin.Type)
    88  				fmt.Fprintln(w, "ID:\t", plugin.ID)
    89  				if len(plugin.Requires) > 0 {
    90  					fmt.Fprintln(w, "Requires:\t")
    91  					for _, r := range plugin.Requires {
    92  						fmt.Fprintln(w, "\t", r)
    93  					}
    94  				}
    95  				if len(plugin.Platforms) > 0 {
    96  					fmt.Fprintln(w, "Platforms:\t", prettyPlatforms(plugin.Platforms))
    97  				}
    98  
    99  				if len(plugin.Exports) > 0 {
   100  					fmt.Fprintln(w, "Exports:\t")
   101  					for k, v := range plugin.Exports {
   102  						fmt.Fprintln(w, "\t", k, "\t", v)
   103  					}
   104  				}
   105  
   106  				if len(plugin.Capabilities) > 0 {
   107  					fmt.Fprintln(w, "Capabilities:\t", strings.Join(plugin.Capabilities, ","))
   108  				}
   109  
   110  				if plugin.InitErr != nil {
   111  					fmt.Fprintln(w, "Error:\t")
   112  					fmt.Fprintln(w, "\t Code:\t", codes.Code(plugin.InitErr.Code))
   113  					fmt.Fprintln(w, "\t Message:\t", plugin.InitErr.Message)
   114  				}
   115  			}
   116  			return w.Flush()
   117  		}
   118  
   119  		fmt.Fprintln(w, "TYPE\tID\tPLATFORMS\tSTATUS\t")
   120  		for _, plugin := range response.Plugins {
   121  			status := "ok"
   122  
   123  			if plugin.InitErr != nil {
   124  				status = "error"
   125  			}
   126  
   127  			var platformColumn = "-"
   128  			if len(plugin.Platforms) > 0 {
   129  				platformColumn = prettyPlatforms(plugin.Platforms)
   130  			}
   131  			if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t\n",
   132  				plugin.Type,
   133  				plugin.ID,
   134  				platformColumn,
   135  				status,
   136  			); err != nil {
   137  				return err
   138  			}
   139  		}
   140  		return w.Flush()
   141  	},
   142  }
   143  
   144  func prettyPlatforms(pspb []types.Platform) string {
   145  	psm := map[string]struct{}{}
   146  	for _, p := range pspb {
   147  		psm[platforms.Format(v1.Platform{
   148  			OS:           p.OS,
   149  			Architecture: p.Architecture,
   150  			Variant:      p.Variant,
   151  		})] = struct{}{}
   152  	}
   153  	var ps []string
   154  	for p := range psm {
   155  		ps = append(ps, p)
   156  	}
   157  	sort.Stable(sort.StringSlice(ps))
   158  	return strings.Join(ps, ",")
   159  }