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