github.com/mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/tools/plugin.go (about)

     1  /*
     2  Copyright 2018 Mirantis
     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 tools
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"strings"
    23  
    24  	"github.com/ghodss/yaml"
    25  	"github.com/spf13/cobra"
    26  	"github.com/spf13/pflag"
    27  )
    28  
    29  // The Plugin / PluginFlag / Plugins types are based on
    30  // pkg/kubectl/plugins/plugins.go from Kubernetes.
    31  
    32  // Plugin holds everything needed to register a plugin as a
    33  // command. Usually comes from a descriptor file.
    34  type Plugin struct {
    35  	// Name is the name of the plugin.
    36  	Name string `json:"name"`
    37  	// ShortDesc is the short description of the plugin.
    38  	ShortDesc string `json:"shortDesc"`
    39  	// LongDesc is the detailed description of the plugin.
    40  	LongDesc string `json:"longDesc,omitempty"`
    41  	// Example is an example of plugin usage.
    42  	Example string `json:"example,omitempty"`
    43  	// Command is the command that needs to be run by kubectl
    44  	// to execute the plugin.
    45  	Command string `json:"command,omitempty"`
    46  	// Flags describes the flags for this plugin.
    47  	Flags []PluginFlag `json:"flags,omitempty"`
    48  	// Tree lists the subcommands of this plugin.
    49  	Tree []*Plugin `json:"tree,omitempty"`
    50  }
    51  
    52  // PluginFlag describes a single flag supported by a given plugin.
    53  type PluginFlag struct {
    54  	// Name is the name of the flag
    55  	Name string `json:"name"`
    56  	// Shorthand is the shorthand for the flag, e.g. -o
    57  	Shorthand string `json:"shorthand,omitempty"`
    58  	// Desc is the description of the flag
    59  	Desc string `json:"desc"`
    60  	// DefValue is the default value of the flag.
    61  	DefValue string `json:"defValue,omitempty"`
    62  }
    63  
    64  func pluginFromCobraCommand(cmd *cobra.Command) Plugin {
    65  	p := Plugin{
    66  		Name:      cmd.Name(),
    67  		ShortDesc: cmd.Short,
    68  		LongDesc:  cmd.Long,
    69  		Example:   cmd.Example,
    70  	}
    71  
    72  	if cmds := cmd.Commands(); len(cmds) == 0 {
    73  		// only process flags for commands w/o subcommands as
    74  		// a cheap way of avoiding to process the global flags
    75  		// that are passed from kubectl via env in any case
    76  		cmd.Flags().VisitAll(func(flag *pflag.Flag) {
    77  			if !flag.Hidden {
    78  				p.Flags = append(p.Flags, PluginFlag{
    79  					Name:      flag.Name,
    80  					Shorthand: flag.Shorthand,
    81  					Desc:      flag.Usage,
    82  					DefValue:  flag.DefValue,
    83  				})
    84  			}
    85  		})
    86  
    87  		p.Command = "./" + cmd.CommandPath()
    88  	} else {
    89  		for _, c := range cmds {
    90  			subPlugin := pluginFromCobraCommand(c)
    91  			p.Tree = append(p.Tree, &subPlugin)
    92  		}
    93  	}
    94  
    95  	return p
    96  }
    97  
    98  func pluginYamlFromCobraCommand(cmd *cobra.Command) []byte {
    99  	out, err := yaml.Marshal(pluginFromCobraCommand(cmd))
   100  	if err != nil {
   101  		// this should never happen under the normal circumstances
   102  		panic("plugin marshalling failed")
   103  	}
   104  	return out
   105  }
   106  
   107  // InPlugin returns true if virtletctl is running as a kubectl plugin.
   108  func InPlugin() bool {
   109  	_, inPlugin := os.LookupEnv("KUBECTL_PLUGINS_CALLER")
   110  	return inPlugin
   111  }
   112  
   113  // SetLocalPluginFlags sets command flags from kubectl plugin
   114  // environment variables in case if virtletctl is running as a kubectl
   115  // plugin.
   116  func SetLocalPluginFlags(cmd *cobra.Command) error {
   117  	if !InPlugin() {
   118  		return nil
   119  	}
   120  	var errs []string
   121  	cmd.Flags().VisitAll(func(flag *pflag.Flag) {
   122  		v, found := os.LookupEnv(flagToEnvName(flag.Name, "KUBECTL_PLUGINS_LOCAL_FLAG_"))
   123  		if !found {
   124  			return
   125  		}
   126  		err := cmd.Flags().Set(flag.Name, v)
   127  		if err != nil {
   128  			errs = append(errs, fmt.Sprintf("%s: %v", flag.Name, err))
   129  		}
   130  	})
   131  	if len(errs) != 0 {
   132  		return fmt.Errorf("errors parsing flags:\n%s", strings.Join(errs, "\n"))
   133  	}
   134  	return nil
   135  }