func printProviderVersions(providerPath string) (string, error) {
	var providerVersions bytes.Buffer
	providers, err := prov.GetAllProviders(providerPath)
	if err != nil {
		return "", err
	}
	for _, provider := range providers {
		version, err := exec.Command(filepath.Join(providerPath, provider), "--version").Output()
		if err != nil {
			providerVersions.WriteString(fmt.Sprintf("%s: unknown version\n", provider))
			continue
		}
		versionString := fmt.Sprintf("%s", version)
		versionString = strings.TrimSpace(versionString)
		providerVersions.WriteString(fmt.Sprintf("%s version %s\n", provider, versionString))
	}
	return providerVersions.String(), nil
}
printProviderVersions returns a string of all provider versions
package command
import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"syscall"
	"github.com/codegangsta/cli"
	prov "github.com/cyberark/summon/provider"
	"github.com/cyberark/summon/secretsyml"
)
type ActionConfig struct {
	Args                 []string
	Provider             string
	Filepath             string
	YamlInline           string
	Subs                 map[string]string
	Ignores              []string
	IgnoreAll            bool
	Environment          string
	RecurseUp            bool
	ShowProviderVersions bool
}
const ENV_FILE_MAGIC = "@SUMMONENVFILE"
const SUMMON_ENV_KEY_NAME = "SUMMON_ENV"
var Action = func(c *cli.Context) {
	if !c.Args().Present() && !c.Bool("all-provider-versions") {
		fmt.Println("Enter a subprocess to run!")
		os.Exit(127)
	}
	provider, err := prov.Resolve(c.String("provider"))
	if err != nil && !c.Bool("all-provider-versions") {
		fmt.Println(err.Error())
		os.Exit(127)
	}
	err = runAction(&ActionConfig{
		Args:                 c.Args(),
		Provider:             provider,
		Environment:          c.String("environment"),
		Filepath:             c.String("f"),
		YamlInline:           c.String("yaml"),
		Ignores:              c.StringSlice("ignore"),
		IgnoreAll:            c.Bool("ignore-all"),
		RecurseUp:            c.Bool("up"),
		ShowProviderVersions: c.Bool("all-provider-versions"),
		Subs:                 convertSubsToMap(c.StringSlice("D")),
	})
	code, err := returnStatusOfError(err)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(127)
	}
	os.Exit(code)
}
func runAction(ac *ActionConfig) error {
	var (
		secrets secretsyml.SecretsMap
		err     error
	)
	if ac.ShowProviderVersions {
		output, err := printProviderVersions(prov.DefaultPath)
		if err != nil {
			return err
		}
		fmt.Print(output)
		return nil
	}
	if ac.RecurseUp {
		currentDir, err := os.Getwd()
		ac.Filepath, err = findInParentTree(ac.Filepath, currentDir)
		if err != nil {
			return err
		}
	}
	switch ac.YamlInline {
	case "":
		secrets, err = secretsyml.ParseFromFile(ac.Filepath, ac.Environment, ac.Subs)
	default:
		secrets, err = secretsyml.ParseFromString(ac.YamlInline, ac.Environment, ac.Subs)
	}
	if err != nil {
		return err
	}
	env := make(map[string]string)
	tempFactory := NewTempFactory("")
	defer tempFactory.Cleanup()
	type Result struct {
		key   string
		value string
		error
	}
	results := make(chan Result, len(secrets))
	var wg sync.WaitGroup
	for key, spec := range secrets {
		wg.Add(1)
		go func(key string, spec secretsyml.SecretSpec) {
			var value string
			if spec.IsVar() {
				value, err = prov.Call(ac.Provider, spec.Path)
				if err != nil {
					results <- Result{key, "", err}
					wg.Done()
					return
				}
			} else {
				value = spec.Path
			}
			if value == "" && spec.DefaultValue != "" {
				value = spec.DefaultValue
			}
			k, v := formatForEnv(key, value, spec, &tempFactory)
			results <- Result{k, v, nil}
			wg.Done()
		}(key, spec)
	}
	wg.Wait()
	close(results)
EnvLoop:
	for envvar := range results {
		if envvar.error == nil {
			env[envvar.key] = envvar.value
		} else {
			if ac.IgnoreAll {
				continue EnvLoop
			}
			for i := range ac.Ignores {
				if ac.Ignores[i] == fmt.Sprintf("%s=%s", envvar.key, envvar.value) {
					continue EnvLoop
				}
			}
			return fmt.Errorf("Error fetching variable %v: %v", envvar.key, envvar.error.Error())
		}
	}
	if ac.Environment != "" {
		env[SUMMON_ENV_KEY_NAME] = ac.Environment
	}
	setupEnvFile(ac.Args, env, &tempFactory)
	var e []string
	for k, v := range env {
		e = append(e, fmt.Sprintf("%s=%s", k, v))
	}
	return runSubcommand(ac.Args, append(os.Environ(), e...))
}
func formatForEnv(key string, value string, spec secretsyml.SecretSpec, tempFactory *TempFactory) (string, string) {
	if spec.IsFile() {
		fname := tempFactory.Push(value)
		value = fname
	}
	return key, value
}
func joinEnv(env map[string]string) string {
	var envs []string
	for k, v := range env {
		envs = append(envs, fmt.Sprintf("%s=%s", k, v))
	}
	sort.Strings(envs)
	return strings.Join(envs, "\n") + "\n"
}
func findInParentTree(secretsFile string, leafDir string) (string, error) {
	if filepath.IsAbs(secretsFile) {
		return "", fmt.Errorf(
			"file specified (%s) is an absolute path: will not recurse up", secretsFile)
	}
	for {
		joinedPath := filepath.Join(leafDir, secretsFile)
		_, err := os.Stat(joinedPath)
		if err != nil {
			if os.IsNotExist(err) {
				upOne := filepath.Dir(leafDir)
				if upOne == leafDir {
					return "", fmt.Errorf(
						"unable to locate file specified (%s): reached root of file system", secretsFile)
				}
				leafDir = upOne
				continue
			}
			return "", fmt.Errorf("unable to locate file specified (%s): %s", secretsFile, err)
		}
		return joinedPath, nil
	}
}
func setupEnvFile(args []string, env map[string]string, tempFactory *TempFactory) string {
	var envFile = ""
	for i, arg := range args {
		idx := strings.Index(arg, ENV_FILE_MAGIC)
		if idx >= 0 {
			if envFile == "" {
				envFile = tempFactory.Push(joinEnv(env))
			}
			args[i] = strings.Replace(arg, ENV_FILE_MAGIC, envFile, -1)
		}
	}
	return envFile
}
func convertSubsToMap(subs []string) map[string]string {
	out := make(map[string]string)
	for _, sub := range subs {
		s := strings.SplitN(sub, "=", 2)
		key, val := s[0], s[1]
		out[key] = val
	}
	return out
}
func returnStatusOfError(err error) (int, error) {
	if eerr, ok := err.(*exec.ExitError); ok {
		if ws, ok := eerr.Sys().(syscall.WaitStatus); ok {
			if ws.Exited() {
				return ws.ExitStatus(), nil
			}
		}
	}
	return 0, err
}