github.com/cloudposse/helm@v2.2.3+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
    17  
    18  import (
    19  	"io/ioutil"
    20  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  
    24  	"github.com/ghodss/yaml"
    25  )
    26  
    27  // PluginFileName is the name of a plugin file.
    28  const PluginFileName = "plugin.yaml"
    29  
    30  // Metadata describes a plugin.
    31  //
    32  // This is the plugin equivalent of a chart.Metadata.
    33  type Metadata struct {
    34  	// Name is the name of the plugin
    35  	Name string `json:"name"`
    36  
    37  	// Version is a SemVer 2 version of the plugin.
    38  	Version string `json:"version"`
    39  
    40  	// Usage is the single-line usage text shown in help
    41  	Usage string `json:"usage"`
    42  
    43  	// Description is a long description shown in places like `helm help`
    44  	Description string `json:"description"`
    45  
    46  	// Command is the command, as a single string.
    47  	//
    48  	// The command will be passed through environment expansion, so env vars can
    49  	// be present in this command. Unless IgnoreFlags is set, this will
    50  	// also merge the flags passed from Helm.
    51  	//
    52  	// Note that command is not executed in a shell. To do so, we suggest
    53  	// pointing the command to a shell script.
    54  	Command string `json:"command"`
    55  
    56  	// IgnoreFlags ignores any flags passed in from Helm
    57  	//
    58  	// For example, if the plugin is invoked as `helm --debug myplugin`, if this
    59  	// is false, `--debug` will be appended to `--command`. If this is true,
    60  	// the `--debug` flag will be discarded.
    61  	IgnoreFlags bool `json:"ignoreFlags"`
    62  
    63  	// UseTunnel indicates that this command needs a tunnel.
    64  	// Setting this will cause a number of side effects, such as the
    65  	// automatic setting of HELM_HOST.
    66  	UseTunnel bool `json:"useTunnel"`
    67  }
    68  
    69  // Plugin represents a plugin.
    70  type Plugin struct {
    71  	// Metadata is a parsed representation of a plugin.yaml
    72  	Metadata *Metadata
    73  	// Dir is the string path to the directory that holds the plugin.
    74  	Dir string
    75  }
    76  
    77  // PrepareCommand takes a Plugin.Command and prepares it for execution.
    78  //
    79  // It merges extraArgs into any arguments supplied in the plugin. It
    80  // returns the name of the command and an args array.
    81  //
    82  // The result is suitable to pass to exec.Command.
    83  func (p *Plugin) PrepareCommand(extraArgs []string) (string, []string) {
    84  	parts := strings.Split(os.ExpandEnv(p.Metadata.Command), " ")
    85  	main := parts[0]
    86  	baseArgs := []string{}
    87  	if len(parts) > 1 {
    88  		baseArgs = parts[1:]
    89  	}
    90  	if !p.Metadata.IgnoreFlags {
    91  		baseArgs = append(baseArgs, extraArgs...)
    92  	}
    93  	return main, baseArgs
    94  }
    95  
    96  // LoadDir loads a plugin from the given directory.
    97  func LoadDir(dirname string) (*Plugin, error) {
    98  	data, err := ioutil.ReadFile(filepath.Join(dirname, PluginFileName))
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	plug := &Plugin{Dir: dirname}
   104  	if err := yaml.Unmarshal(data, &plug.Metadata); err != nil {
   105  		return nil, err
   106  	}
   107  	return plug, nil
   108  }
   109  
   110  // LoadAll loads all plugins found beneath the base directory.
   111  //
   112  // This scans only one directory level.
   113  func LoadAll(basedir string) ([]*Plugin, error) {
   114  	plugins := []*Plugin{}
   115  	// We want basedir/*/plugin.yaml
   116  	scanpath := filepath.Join(basedir, "*", PluginFileName)
   117  	matches, err := filepath.Glob(scanpath)
   118  	if err != nil {
   119  		return plugins, err
   120  	}
   121  
   122  	if matches == nil {
   123  		return plugins, nil
   124  	}
   125  
   126  	for _, yaml := range matches {
   127  		dir := filepath.Dir(yaml)
   128  		p, err := LoadDir(dir)
   129  		if err != nil {
   130  			return plugins, err
   131  		}
   132  		plugins = append(plugins, p)
   133  	}
   134  	return plugins, nil
   135  }