github.com/darkowlzz/helm@v2.5.1-0.20171213183701-6707fe0468d4+incompatible/pkg/plugin/plugin.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors All rights reserved.
     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 plugin // import "k8s.io/helm/pkg/plugin"
    17  
    18  import (
    19  	"io/ioutil"
    20  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  
    24  	helm_env "k8s.io/helm/pkg/helm/environment"
    25  
    26  	"github.com/ghodss/yaml"
    27  )
    28  
    29  const pluginFileName = "plugin.yaml"
    30  
    31  // Downloaders represents the plugins capability if it can retrieve
    32  // charts from special sources
    33  type Downloaders struct {
    34  	// Protocols are the list of schemes from the charts URL.
    35  	Protocols []string `json:"protocols"`
    36  	// Command is the executable path with which the plugin performs
    37  	// the actual download for the corresponding Protocols
    38  	Command string `json:"command"`
    39  }
    40  
    41  // Metadata describes a plugin.
    42  //
    43  // This is the plugin equivalent of a chart.Metadata.
    44  type Metadata struct {
    45  	// Name is the name of the plugin
    46  	Name string `json:"name"`
    47  
    48  	// Version is a SemVer 2 version of the plugin.
    49  	Version string `json:"version"`
    50  
    51  	// Usage is the single-line usage text shown in help
    52  	Usage string `json:"usage"`
    53  
    54  	// Description is a long description shown in places like `helm help`
    55  	Description string `json:"description"`
    56  
    57  	// Command is the command, as a single string.
    58  	//
    59  	// The command will be passed through environment expansion, so env vars can
    60  	// be present in this command. Unless IgnoreFlags is set, this will
    61  	// also merge the flags passed from Helm.
    62  	//
    63  	// Note that command is not executed in a shell. To do so, we suggest
    64  	// pointing the command to a shell script.
    65  	Command string `json:"command"`
    66  
    67  	// IgnoreFlags ignores any flags passed in from Helm
    68  	//
    69  	// For example, if the plugin is invoked as `helm --debug myplugin`, if this
    70  	// is false, `--debug` will be appended to `--command`. If this is true,
    71  	// the `--debug` flag will be discarded.
    72  	IgnoreFlags bool `json:"ignoreFlags"`
    73  
    74  	// UseTunnel indicates that this command needs a tunnel.
    75  	// Setting this will cause a number of side effects, such as the
    76  	// automatic setting of HELM_HOST.
    77  	UseTunnel bool `json:"useTunnel"`
    78  
    79  	// Hooks are commands that will run on events.
    80  	Hooks Hooks
    81  
    82  	// Downloaders field is used if the plugin supply downloader mechanism
    83  	// for special protocols.
    84  	Downloaders []Downloaders `json:"downloaders"`
    85  }
    86  
    87  // Plugin represents a plugin.
    88  type Plugin struct {
    89  	// Metadata is a parsed representation of a plugin.yaml
    90  	Metadata *Metadata
    91  	// Dir is the string path to the directory that holds the plugin.
    92  	Dir string
    93  }
    94  
    95  // PrepareCommand takes a Plugin.Command and prepares it for execution.
    96  //
    97  // It merges extraArgs into any arguments supplied in the plugin. It
    98  // returns the name of the command and an args array.
    99  //
   100  // The result is suitable to pass to exec.Command.
   101  func (p *Plugin) PrepareCommand(extraArgs []string) (string, []string) {
   102  	parts := strings.Split(os.ExpandEnv(p.Metadata.Command), " ")
   103  	main := parts[0]
   104  	baseArgs := []string{}
   105  	if len(parts) > 1 {
   106  		baseArgs = parts[1:]
   107  	}
   108  	if !p.Metadata.IgnoreFlags {
   109  		baseArgs = append(baseArgs, extraArgs...)
   110  	}
   111  	return main, baseArgs
   112  }
   113  
   114  // LoadDir loads a plugin from the given directory.
   115  func LoadDir(dirname string) (*Plugin, error) {
   116  	data, err := ioutil.ReadFile(filepath.Join(dirname, pluginFileName))
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	plug := &Plugin{Dir: dirname}
   122  	if err := yaml.Unmarshal(data, &plug.Metadata); err != nil {
   123  		return nil, err
   124  	}
   125  	return plug, nil
   126  }
   127  
   128  // LoadAll loads all plugins found beneath the base directory.
   129  //
   130  // This scans only one directory level.
   131  func LoadAll(basedir string) ([]*Plugin, error) {
   132  	plugins := []*Plugin{}
   133  	// We want basedir/*/plugin.yaml
   134  	scanpath := filepath.Join(basedir, "*", pluginFileName)
   135  	matches, err := filepath.Glob(scanpath)
   136  	if err != nil {
   137  		return plugins, err
   138  	}
   139  
   140  	if matches == nil {
   141  		return plugins, nil
   142  	}
   143  
   144  	for _, yaml := range matches {
   145  		dir := filepath.Dir(yaml)
   146  		p, err := LoadDir(dir)
   147  		if err != nil {
   148  			return plugins, err
   149  		}
   150  		plugins = append(plugins, p)
   151  	}
   152  	return plugins, nil
   153  }
   154  
   155  // FindPlugins returns a list of YAML files that describe plugins.
   156  func FindPlugins(plugdirs string) ([]*Plugin, error) {
   157  	found := []*Plugin{}
   158  	// Let's get all UNIXy and allow path separators
   159  	for _, p := range filepath.SplitList(plugdirs) {
   160  		matches, err := LoadAll(p)
   161  		if err != nil {
   162  			return matches, err
   163  		}
   164  		found = append(found, matches...)
   165  	}
   166  	return found, nil
   167  }
   168  
   169  // SetupPluginEnv prepares os.Env for plugins. It operates on os.Env because
   170  // the plugin subsystem itself needs access to the environment variables
   171  // created here.
   172  func SetupPluginEnv(settings helm_env.EnvSettings,
   173  	shortName, base string) {
   174  	for key, val := range map[string]string{
   175  		"HELM_PLUGIN_NAME": shortName,
   176  		"HELM_PLUGIN_DIR":  base,
   177  		"HELM_BIN":         os.Args[0],
   178  
   179  		// Set vars that may not have been set, and save client the
   180  		// trouble of re-parsing.
   181  		"HELM_PLUGIN": settings.PluginDirs(),
   182  		"HELM_HOME":   settings.Home.String(),
   183  
   184  		// Set vars that convey common information.
   185  		"HELM_PATH_REPOSITORY":       settings.Home.Repository(),
   186  		"HELM_PATH_REPOSITORY_FILE":  settings.Home.RepositoryFile(),
   187  		"HELM_PATH_CACHE":            settings.Home.Cache(),
   188  		"HELM_PATH_LOCAL_REPOSITORY": settings.Home.LocalRepository(),
   189  		"HELM_PATH_STARTER":          settings.Home.Starters(),
   190  
   191  		"TILLER_HOST":      settings.TillerHost,
   192  		"TILLER_NAMESPACE": settings.TillerNamespace,
   193  	} {
   194  		os.Setenv(key, val)
   195  	}
   196  
   197  	if settings.Debug {
   198  		os.Setenv("HELM_DEBUG", "1")
   199  	}
   200  }