github.com/souleb/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 }