github.com/Beeketing/helm@v2.12.1+incompatible/cmd/helm/load_plugins.go (about)

     1  /*
     2  Copyright The Helm Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package main
    17  
    18  import (
    19  	"fmt"
    20  	"io"
    21  	"os"
    22  	"os/exec"
    23  	"path/filepath"
    24  	"strings"
    25  	"syscall"
    26  
    27  	"github.com/spf13/cobra"
    28  
    29  	"k8s.io/helm/pkg/plugin"
    30  )
    31  
    32  type pluginError struct {
    33  	error
    34  	code int
    35  }
    36  
    37  // loadPlugins loads plugins into the command list.
    38  //
    39  // This follows a different pattern than the other commands because it has
    40  // to inspect its environment and then add commands to the base command
    41  // as it finds them.
    42  func loadPlugins(baseCmd *cobra.Command, out io.Writer) {
    43  
    44  	// If HELM_NO_PLUGINS is set to 1, do not load plugins.
    45  	if os.Getenv("HELM_NO_PLUGINS") == "1" {
    46  		return
    47  	}
    48  
    49  	// debug("HELM_PLUGIN_DIRS=%s", settings.PluginDirs())
    50  	found, err := findPlugins(settings.PluginDirs())
    51  	if err != nil {
    52  		fmt.Fprintf(os.Stderr, "failed to load plugins: %s", err)
    53  		return
    54  	}
    55  
    56  	processParent := func(cmd *cobra.Command, args []string) ([]string, error) {
    57  		k, u := manuallyProcessArgs(args)
    58  		if err := cmd.Parent().ParseFlags(k); err != nil {
    59  			return nil, err
    60  		}
    61  		return u, nil
    62  	}
    63  
    64  	// Now we create commands for all of these.
    65  	for _, plug := range found {
    66  		plug := plug
    67  		md := plug.Metadata
    68  		if md.Usage == "" {
    69  			md.Usage = fmt.Sprintf("the %q plugin", md.Name)
    70  		}
    71  
    72  		c := &cobra.Command{
    73  			Use:   md.Name,
    74  			Short: md.Usage,
    75  			Long:  md.Description,
    76  			RunE: func(cmd *cobra.Command, args []string) error {
    77  				u, err := processParent(cmd, args)
    78  				if err != nil {
    79  					return err
    80  				}
    81  
    82  				// Call setupEnv before PrepareCommand because
    83  				// PrepareCommand uses os.ExpandEnv and expects the
    84  				// setupEnv vars.
    85  				plugin.SetupPluginEnv(settings, md.Name, plug.Dir)
    86  				main, argv := plug.PrepareCommand(u)
    87  
    88  				prog := exec.Command(main, argv...)
    89  				prog.Env = os.Environ()
    90  				prog.Stdin = os.Stdin
    91  				prog.Stdout = out
    92  				prog.Stderr = os.Stderr
    93  				if err := prog.Run(); err != nil {
    94  					if eerr, ok := err.(*exec.ExitError); ok {
    95  						os.Stderr.Write(eerr.Stderr)
    96  						status := eerr.Sys().(syscall.WaitStatus)
    97  						return pluginError{
    98  							error: fmt.Errorf("plugin %q exited with error", md.Name),
    99  							code:  status.ExitStatus(),
   100  						}
   101  					}
   102  					return err
   103  				}
   104  				return nil
   105  			},
   106  			// This passes all the flags to the subcommand.
   107  			DisableFlagParsing: true,
   108  		}
   109  
   110  		if md.UseTunnel {
   111  			c.PreRunE = func(cmd *cobra.Command, args []string) error {
   112  				// Parse the parent flag, but not the local flags.
   113  				if _, err := processParent(cmd, args); err != nil {
   114  					return err
   115  				}
   116  				return setupConnection()
   117  			}
   118  		}
   119  
   120  		// TODO: Make sure a command with this name does not already exist.
   121  		baseCmd.AddCommand(c)
   122  	}
   123  }
   124  
   125  // manuallyProcessArgs processes an arg array, removing special args.
   126  //
   127  // Returns two sets of args: known and unknown (in that order)
   128  func manuallyProcessArgs(args []string) ([]string, []string) {
   129  	known := []string{}
   130  	unknown := []string{}
   131  	kvargs := []string{"--host", "--kube-context", "--home", "--tiller-namespace"}
   132  	knownArg := func(a string) bool {
   133  		for _, pre := range kvargs {
   134  			if strings.HasPrefix(a, pre+"=") {
   135  				return true
   136  			}
   137  		}
   138  		return false
   139  	}
   140  	for i := 0; i < len(args); i++ {
   141  		switch a := args[i]; a {
   142  		case "--debug":
   143  			known = append(known, a)
   144  		case "--host", "--kube-context", "--home", "--tiller-namespace":
   145  			known = append(known, a, args[i+1])
   146  			i++
   147  		default:
   148  			if knownArg(a) {
   149  				known = append(known, a)
   150  				continue
   151  			}
   152  			unknown = append(unknown, a)
   153  		}
   154  	}
   155  	return known, unknown
   156  }
   157  
   158  // findPlugins returns a list of YAML files that describe plugins.
   159  func findPlugins(plugdirs string) ([]*plugin.Plugin, error) {
   160  	found := []*plugin.Plugin{}
   161  	// Let's get all UNIXy and allow path separators
   162  	for _, p := range filepath.SplitList(plugdirs) {
   163  		matches, err := plugin.LoadAll(p)
   164  		if err != nil {
   165  			return matches, err
   166  		}
   167  		found = append(found, matches...)
   168  	}
   169  	return found, nil
   170  }