From 2e39c862abe3fb09c6c9b7f12594686f7deabbc4 Mon Sep 17 00:00:00 2001 From: Eugene Dementiev Date: Tue, 5 Jun 2018 14:40:42 +1200 Subject: [PATCH] Change interpolation engine from sh to native Enable args expansion for `print` command. --- Gopkg.lock | 95 ++--------------------------------------------- cmd/print.go | 9 ++++- cmd/root.go | 2 + cmd/run.go | 47 +++-------------------- ssm/parameters.go | 28 ++++++++------ ssm/util.go | 33 ++++++++++++++++ 6 files changed, 68 insertions(+), 146 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 41b1023..aa51ef0 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -43,10 +43,10 @@ version = "v1.13.32" [[projects]] - name = "github.com/fsnotify/fsnotify" + branch = "master" + name = "github.com/buildkite/interpolate" packages = ["."] - revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" - version = "v1.4.7" + revision = "c1c376f870d259871fcc9bf3bcfd4889b999ed1a" [[projects]] name = "github.com/go-ini/ini" @@ -66,23 +66,6 @@ packages = ["."] revision = "b7773ae218740a7be65057fc60b366a49b538a44" -[[projects]] - branch = "master" - name = "github.com/hashicorp/hcl" - packages = [ - ".", - "hcl/ast", - "hcl/parser", - "hcl/printer", - "hcl/scanner", - "hcl/strconv", - "hcl/token", - "json/parser", - "json/scanner", - "json/token" - ] - revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168" - [[projects]] name = "github.com/imdario/mergo" packages = ["."] @@ -100,97 +83,27 @@ packages = ["."] revision = "0b12d6b5" -[[projects]] - name = "github.com/magiconair/properties" - packages = ["."] - revision = "c3beff4c2358b44d0493c7dda585e7db7ff28ae6" - version = "v1.7.6" - -[[projects]] - branch = "master" - name = "github.com/mitchellh/mapstructure" - packages = ["."] - revision = "00c29f56e2386353d58c599509e8dc3801b0d716" - -[[projects]] - name = "github.com/pelletier/go-toml" - packages = ["."] - revision = "acdc4509485b587f5e675510c4f2c63e90ff68a8" - version = "v1.1.0" - [[projects]] name = "github.com/pkg/errors" packages = ["."] revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" -[[projects]] - name = "github.com/spf13/afero" - packages = [ - ".", - "mem" - ] - revision = "63644898a8da0bc22138abf860edaf5277b6102e" - version = "v1.1.0" - -[[projects]] - name = "github.com/spf13/cast" - packages = ["."] - revision = "8965335b8c7107321228e3e3702cab9832751bac" - version = "v1.2.0" - [[projects]] name = "github.com/spf13/cobra" packages = ["."] revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4" version = "v0.0.2" -[[projects]] - branch = "master" - name = "github.com/spf13/jwalterweatherman" - packages = ["."] - revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394" - [[projects]] name = "github.com/spf13/pflag" packages = ["."] revision = "583c0c0531f06d5278b7d917446061adc344b5cd" version = "v1.0.1" -[[projects]] - name = "github.com/spf13/viper" - packages = ["."] - revision = "b5e8006cbee93ec955a89ab31e0e3ce3204f3736" - version = "v1.0.2" - -[[projects]] - branch = "master" - name = "golang.org/x/sys" - packages = ["unix"] - revision = "ecfd8b563e1320e7282dccf28ad48ec2c68aaebf" - -[[projects]] - name = "golang.org/x/text" - packages = [ - "internal/gen", - "internal/triegen", - "internal/ucd", - "transform", - "unicode/cldr", - "unicode/norm" - ] - revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" - version = "v0.3.0" - -[[projects]] - name = "gopkg.in/yaml.v2" - packages = ["."] - revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" - version = "v2.2.1" - [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "d116c7928bd47d91cf55910ab1c6a16bcfd0dc06cc009efa2e0cf2f74604f5b3" + inputs-digest = "5d4badb3db9f275e024257609613168a80c1f9bc146f93b98d5a11f30b19508e" solver-name = "gps-cdcl" solver-version = 1 diff --git a/cmd/print.go b/cmd/print.go index 45b16bc..d3b4030 100644 --- a/cmd/print.go +++ b/cmd/print.go @@ -16,8 +16,8 @@ var printCmd = &cobra.Command{ Short: "Prints the specified parameters.", Run: func(cmd *cobra.Command, args []string) { - megamap := make(map[string]interface{}) - parameters, err := ssm.GetParameters(names, paths, strict, recursive) + megamap := make(map[string]string) + parameters, err := ssm.GetParameters(names, paths, expand, strict, recursive) if err != nil { log.WithError(err).Fatal("Can't get parameters") } @@ -27,6 +27,11 @@ var printCmd = &cobra.Command{ log.WithError(err).Fatal("Can't merge maps") } } + for key, value := range megamap { + if expand { + megamap[key] = ssm.ExpandValue(value) + } + } marshalled, err := json.MarshalIndent(megamap, "", " ") if err != nil { log.WithError(err).Fatal("Can't marshal json") diff --git a/cmd/root.go b/cmd/root.go index ddba146..7f7254e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -12,6 +12,7 @@ var ( names []string recursive bool strict bool + expand bool ) // rootCmd represents the base command when called without any subcommands @@ -38,4 +39,5 @@ func init() { rootCmd.PersistentFlags().StringArrayVarP(&names, "name", "n", []string{}, "Name of the SSM parameter to retrieve. Can be specified multiple times.") rootCmd.PersistentFlags().BoolVarP(&recursive, "recursive", "r", false, "Walk through the provided SSM paths recursively.") rootCmd.PersistentFlags().BoolVarP(&strict, "strict", "s", false, "Strict mode. Fail if found less parameters than number of names.") + rootCmd.PersistentFlags().BoolVarP(&expand, "expand", "e", false, "Expand arguments and values using /bin/sh") } diff --git a/cmd/run.go b/cmd/run.go index aaff7de..0ef22ec 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -1,11 +1,9 @@ package cmd import ( - "fmt" "os" "os/exec" "os/signal" - "strings" "github.com/springload/ssm-parent/ssm" @@ -14,8 +12,6 @@ import ( "github.com/spf13/cobra" ) -var expand bool - // runCmd represents the run command var runCmd = &cobra.Command{ Use: "run command", @@ -24,14 +20,8 @@ var runCmd = &cobra.Command{ Run: func(cobraCmd *cobra.Command, args []string) { var cmdArgs []string - megamap := make(map[string]interface{}) - localNames := names - localPaths := paths - if expand { - localNames = expandArgs(names) - localPaths = expandArgs(paths) - } - parameters, err := ssm.GetParameters(localNames, localPaths, strict, recursive) + megamap := make(map[string]string) + parameters, err := ssm.GetParameters(names, paths, expand, strict, recursive) if err != nil { log.WithError(err).Fatal("Can't get parameters") } @@ -43,11 +33,9 @@ var runCmd = &cobra.Command{ } for key, value := range megamap { if expand { - if stringValue, ok := value.(string); ok { - value = expandValue(stringValue) - } + value = ssm.ExpandValue(value) } - os.Setenv(key, fmt.Sprintf("%v", value)) + os.Setenv(key, value) } command, err := exec.LookPath(args[0]) @@ -59,7 +47,7 @@ var runCmd = &cobra.Command{ c := make(chan os.Signal, 1) signal.Notify(c) if expand { - cmdArgs = expandArgs(args[1:]) + cmdArgs = ssm.ExpandArgs(args[1:]) } else { cmdArgs = args[1:] } @@ -86,31 +74,6 @@ var runCmd = &cobra.Command{ }, } -// expandArgs leverages on shell and echo to expand -// possible args mainly env vars. -// taken from https://github.com/abiosoft/parent -func expandArgs(args []string) []string { - var expanded []string - for _, arg := range args { - arg = expandValue(arg) - expanded = append(expanded, arg) - } - return expanded -} - -func expandValue(val string) string { - e, err := exec.Command("/bin/sh", "-c", fmt.Sprintf("echo %s", val)).Output() - // error is not expected. - // in the rare case that this errors - // the original arg is still used. - if err == nil { - return strings.TrimSpace(string(e)) - } - return val - -} - func init() { - runCmd.Flags().BoolVarP(&expand, "expand", "e", false, "Expand arguments and values using /bin/sh") rootCmd.AddCommand(runCmd) } diff --git a/ssm/parameters.go b/ssm/parameters.go index 52df86b..8fc4ff5 100644 --- a/ssm/parameters.go +++ b/ssm/parameters.go @@ -30,7 +30,7 @@ func makeSession() error { return nil } -func getJsonSSMParametersByPaths(paths []string, strict, recursive bool) (parameters []map[string]interface{}, err error) { +func getJsonSSMParametersByPaths(paths []string, strict, recursive bool) (parameters []map[string]string, err error) { err = makeSession() if err != nil { log.WithError(err).Fatal("Can't create session") // fail early here @@ -46,7 +46,7 @@ func getJsonSSMParametersByPaths(paths []string, strict, recursive bool) (parame err = multierror.Append(err, fmt.Errorf("Can't get parameters from path '%s': %s", path, innerErr)) } for _, parameter := range response.Parameters { - value := make(map[string]interface{}) + value := make(map[string]string) innerErr := json.Unmarshal([]byte(*parameter.Value), &value) if innerErr != nil { err = multierror.Append(err, fmt.Errorf("Can't unmarshal json from '%s': %s", *parameter.Name, innerErr)) @@ -57,7 +57,7 @@ func getJsonSSMParametersByPaths(paths []string, strict, recursive bool) (parame return } -func getJsonSSMParameters(names []string, strict bool) (parameters []map[string]interface{}, err error) { +func getJsonSSMParameters(names []string, strict bool) (parameters []map[string]string, err error) { err = makeSession() if err != nil { log.WithError(err).Fatal("Can't create session") // fail early here @@ -83,7 +83,7 @@ func getJsonSSMParameters(names []string, strict bool) (parameters []map[string] } } for _, parameter := range response.Parameters { - value := make(map[string]interface{}) + value := make(map[string]string) innerErr := json.Unmarshal([]byte(*parameter.Value), &value) if innerErr != nil { err = multierror.Append(err, fmt.Errorf("Can't unmarshal json from '%s': %s", *parameter.Name, innerErr)) @@ -93,23 +93,29 @@ func getJsonSSMParameters(names []string, strict bool) (parameters []map[string] return } -func GetParameters(names, paths []string, strict, recursive bool) (parameters []map[string]interface{}, err error) { - if len(names) > 0 { - parametersFromNames, err := getJsonSSMParameters(names, strict) +func GetParameters(names, paths []string, expand, strict, recursive bool) (parameters []map[string]string, err error) { + localNames := names + localPaths := paths + if expand { + localNames = ExpandArgs(names) + localPaths = ExpandArgs(paths) + } + if len(localNames) > 0 { + parametersFromNames, err := getJsonSSMParameters(localNames, strict) if err != nil { log.WithError(err).WithFields( - log.Fields{"names": names}, + log.Fields{"names": localNames}, ).Fatal("Can't get parameters by names") } for _, parameter := range parametersFromNames { parameters = append(parameters, parameter) } } - if len(paths) > 0 { - parametersFromPaths, err := getJsonSSMParametersByPaths(paths, strict, recursive) + if len(localPaths) > 0 { + parametersFromPaths, err := getJsonSSMParametersByPaths(localPaths, strict, recursive) if err != nil { log.WithError(err).WithFields( - log.Fields{"paths": paths}, + log.Fields{"paths": localPaths}, ).Fatal("Can't get parameters by paths") } for _, parameter := range parametersFromPaths { diff --git a/ssm/util.go b/ssm/util.go index 157e106..7aefc73 100644 --- a/ssm/util.go +++ b/ssm/util.go @@ -1,5 +1,14 @@ package ssm +import ( + "os" + "strings" + + "github.com/buildkite/interpolate" +) + +var env Env + // difference returns the elements in a that aren't in b // the second argument is slice of string pointers to suit AWS SDK func stringSliceDifference(a, b []string) []string { @@ -15,3 +24,27 @@ func stringSliceDifference(a, b []string) []string { } return ab } + +func ExpandArgs(args []string) []string { + var expanded []string + for _, arg := range args { + arg = ExpandValue(arg) + expanded = append(expanded, arg) + } + return expanded +} +func ExpandValue(val string) string { + e, err := interpolate.Interpolate(env, val) + if err == nil { + return strings.TrimSpace(string(e)) + } + return val + +} + +// just adapt os.LookupEnv to this interface +type Env struct{} + +func (e Env) Get(key string) (string, bool) { + return os.LookupEnv(key) +}