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 }