Skip to content

Commit

Permalink
Change interpolation engine from sh to native
Browse files Browse the repository at this point in the history
Enable args expansion for `print` command.
  • Loading branch information
Eugene Dementiev committed Jun 5, 2018
1 parent 1306d02 commit 2e39c86
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 146 deletions.
95 changes: 4 additions & 91 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions cmd/print.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand All @@ -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")
Expand Down
2 changes: 2 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var (
names []string
recursive bool
strict bool
expand bool
)

// rootCmd represents the base command when called without any subcommands
Expand All @@ -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")
}
47 changes: 5 additions & 42 deletions cmd/run.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package cmd

import (
"fmt"
"os"
"os/exec"
"os/signal"
"strings"

"github.com/springload/ssm-parent/ssm"

Expand All @@ -14,8 +12,6 @@ import (
"github.com/spf13/cobra"
)

var expand bool

// runCmd represents the run command
var runCmd = &cobra.Command{
Use: "run command",
Expand All @@ -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")
}
Expand All @@ -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])
Expand All @@ -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:]
}
Expand All @@ -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)
}
28 changes: 17 additions & 11 deletions ssm/parameters.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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))
Expand All @@ -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
Expand All @@ -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))
Expand All @@ -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 {
Expand Down
33 changes: 33 additions & 0 deletions ssm/util.go
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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)
}

0 comments on commit 2e39c86

Please sign in to comment.