github.com/TarasLykhenko/helm@v3.0.0-beta.3+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  
    26  	"github.com/pkg/errors"
    27  	"github.com/spf13/cobra"
    28  
    29  	"helm.sh/helm/pkg/plugin"
    30  )
    31  
    32  // loadPlugins loads plugins into the command list.
    33  //
    34  // This follows a different pattern than the other commands because it has
    35  // to inspect its environment and then add commands to the base command
    36  // as it finds them.
    37  func loadPlugins(baseCmd *cobra.Command, out io.Writer) {
    38  
    39  	// If HELM_NO_PLUGINS is set to 1, do not load plugins.
    40  	if os.Getenv("HELM_NO_PLUGINS") == "1" {
    41  		return
    42  	}
    43  
    44  	found, err := findPlugins(settings.PluginsDirectory)
    45  	if err != nil {
    46  		fmt.Fprintf(os.Stderr, "failed to load plugins: %s", err)
    47  		return
    48  	}
    49  
    50  	processParent := func(cmd *cobra.Command, args []string) ([]string, error) {
    51  		k, u := manuallyProcessArgs(args)
    52  		if err := cmd.Parent().ParseFlags(k); err != nil {
    53  			return nil, err
    54  		}
    55  		return u, nil
    56  	}
    57  
    58  	// Now we create commands for all of these.
    59  	for _, plug := range found {
    60  		plug := plug
    61  		md := plug.Metadata
    62  		if md.Usage == "" {
    63  			md.Usage = fmt.Sprintf("the %q plugin", md.Name)
    64  		}
    65  
    66  		c := &cobra.Command{
    67  			Use:   md.Name,
    68  			Short: md.Usage,
    69  			Long:  md.Description,
    70  			RunE: func(cmd *cobra.Command, args []string) error {
    71  				u, err := processParent(cmd, args)
    72  				if err != nil {
    73  					return err
    74  				}
    75  
    76  				// Call setupEnv before PrepareCommand because
    77  				// PrepareCommand uses os.ExpandEnv and expects the
    78  				// setupEnv vars.
    79  				plugin.SetupPluginEnv(settings, md.Name, plug.Dir)
    80  				main, argv, prepCmdErr := plug.PrepareCommand(u)
    81  				if prepCmdErr != nil {
    82  					os.Stderr.WriteString(prepCmdErr.Error())
    83  					return errors.Errorf("plugin %q exited with error", md.Name)
    84  				}
    85  
    86  				prog := exec.Command(main, argv...)
    87  				prog.Env = os.Environ()
    88  				prog.Stdin = os.Stdin
    89  				prog.Stdout = out
    90  				prog.Stderr = os.Stderr
    91  				if err := prog.Run(); err != nil {
    92  					if eerr, ok := err.(*exec.ExitError); ok {
    93  						os.Stderr.Write(eerr.Stderr)
    94  						return errors.Errorf("plugin %q exited with error", md.Name)
    95  					}
    96  					return err
    97  				}
    98  				return nil
    99  			},
   100  			// This passes all the flags to the subcommand.
   101  			DisableFlagParsing: true,
   102  		}
   103  
   104  		// TODO: Make sure a command with this name does not already exist.
   105  		baseCmd.AddCommand(c)
   106  	}
   107  }
   108  
   109  // manuallyProcessArgs processes an arg array, removing special args.
   110  //
   111  // Returns two sets of args: known and unknown (in that order)
   112  func manuallyProcessArgs(args []string) ([]string, []string) {
   113  	known := []string{}
   114  	unknown := []string{}
   115  	kvargs := []string{"--context", "--namespace"}
   116  	knownArg := func(a string) bool {
   117  		for _, pre := range kvargs {
   118  			if strings.HasPrefix(a, pre+"=") {
   119  				return true
   120  			}
   121  		}
   122  		return false
   123  	}
   124  	for i := 0; i < len(args); i++ {
   125  		switch a := args[i]; a {
   126  		case "--debug":
   127  			known = append(known, a)
   128  		case "--context", "--namespace":
   129  			known = append(known, a, args[i+1])
   130  			i++
   131  		default:
   132  			if knownArg(a) {
   133  				known = append(known, a)
   134  				continue
   135  			}
   136  			unknown = append(unknown, a)
   137  		}
   138  	}
   139  	return known, unknown
   140  }
   141  
   142  // findPlugins returns a list of YAML files that describe plugins.
   143  func findPlugins(plugdirs string) ([]*plugin.Plugin, error) {
   144  	found := []*plugin.Plugin{}
   145  	// Let's get all UNIXy and allow path separators
   146  	for _, p := range filepath.SplitList(plugdirs) {
   147  		matches, err := plugin.LoadAll(p)
   148  		if err != nil {
   149  			return matches, err
   150  		}
   151  		found = append(found, matches...)
   152  	}
   153  	return found, nil
   154  }