Skip to content

Commit

Permalink
Add selective parameter expansion flags and implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Eugene Dementyev authored and ekini committed Jan 30, 2022
1 parent a5d470d commit af252c1
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 31 deletions.
37 changes: 37 additions & 0 deletions .github/workflows/build_test.yml
@@ -0,0 +1,37 @@
name: "Test the build"

on:
push:
pull_request:

jobs:
test:
name: test if ssm-parent can be built
runs-on: ubuntu-latest
steps:
-
name: checkout
uses: actions/checkout@v2
-
name: set up Go
uses: actions/setup-go@v1
with:
go-version: 1.17.x
-
name: cache modules
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
-
name: download dependencies
run: go mod download
-
name: build the app
run: go build
-
name: test the app
run: go test -v ./...

3 changes: 3 additions & 0 deletions cmd/dotenv.go
Expand Up @@ -27,6 +27,9 @@ var dotenvCmd = &cobra.Command{
viper.GetBool("expand"),
viper.GetBool("strict"),
viper.GetBool("recursive"),
viper.GetBool("expand-names"),
viper.GetBool("expand-paths"),
viper.GetStringSlice("expand-values"),
)
if err != nil {
log.WithError(err).Fatal("Can't get parameters")
Expand Down
3 changes: 3 additions & 0 deletions cmd/print.go
Expand Up @@ -25,6 +25,9 @@ var printCmd = &cobra.Command{
viper.GetBool("expand"),
viper.GetBool("strict"),
viper.GetBool("recursive"),
viper.GetBool("expand-names"),
viper.GetBool("expand-paths"),
viper.GetStringSlice("expand-values"),
)
if err != nil {
log.WithError(err).Fatal("Can't marshal json")
Expand Down
8 changes: 7 additions & 1 deletion cmd/root.go
Expand Up @@ -103,7 +103,10 @@ func init() {
cobra.OnInitialize(initSettings)
rootCmd.PersistentFlags().StringVarP(&config, "config", "c", "", "Path to the config file (optional). Allows to set transformations")
rootCmd.PersistentFlags().BoolP("debug", "d", false, "Turn on debug logging")
rootCmd.PersistentFlags().BoolP("expand", "e", false, "Expand arguments and values using shell-style syntax")
rootCmd.PersistentFlags().BoolP("expand", "e", false, "Expand all arguments and values using shell-style syntax")
rootCmd.PersistentFlags().BoolP("expand-names", "", false, "Expand SSM names using shell-style syntax. The '--expand' does the same, but this flag is more selective")
rootCmd.PersistentFlags().BoolP("expand-paths", "", false, "Expand SSM paths using shell-style syntax. The '--expand' does the same, but this flag is more selective")
rootCmd.PersistentFlags().StringSliceP("expand-values", "", []string{}, "Expand SSM values using shell-style syntax. The '--expand' does the same, but this flag is more selective. Can be specified multiple times.")
rootCmd.PersistentFlags().StringSliceP("path", "p", []string{}, "Path to a SSM parameter. Expects JSON in the value. Can be specified multiple times.")
rootCmd.PersistentFlags().StringSliceP("name", "n", []string{}, "Name of the SSM parameter to retrieve. Expects JSON in the value. Can be specified multiple times.")
rootCmd.PersistentFlags().StringSliceP("plain-path", "", []string{}, "Path to a SSM parameter. Expects actual parameter in the value. Can be specified multiple times.")
Expand All @@ -113,6 +116,9 @@ func init() {

viper.BindPFlag("debug", rootCmd.PersistentFlags().Lookup("debug"))
viper.BindPFlag("expand", rootCmd.PersistentFlags().Lookup("expand"))
viper.BindPFlag("expand-names", rootCmd.PersistentFlags().Lookup("expand-names"))
viper.BindPFlag("expand-paths", rootCmd.PersistentFlags().Lookup("expand-paths"))
viper.BindPFlag("expand-values", rootCmd.PersistentFlags().Lookup("expand-values"))
viper.BindPFlag("paths", rootCmd.PersistentFlags().Lookup("path"))
viper.BindPFlag("names", rootCmd.PersistentFlags().Lookup("name"))
viper.BindPFlag("plain-paths", rootCmd.PersistentFlags().Lookup("plain-path"))
Expand Down
3 changes: 3 additions & 0 deletions cmd/run.go
Expand Up @@ -30,6 +30,9 @@ var runCmd = &cobra.Command{
viper.GetBool("expand"),
viper.GetBool("strict"),
viper.GetBool("recursive"),
viper.GetBool("expand-names"),
viper.GetBool("expand-paths"),
viper.GetStringSlice("expand-values"),
)
if err != nil {
log.WithError(err).Fatal("Can't get parameters")
Expand Down
52 changes: 52 additions & 0 deletions ssm/expand.go
@@ -0,0 +1,52 @@
package ssm

import (
"fmt"
"strings"

"github.com/buildkite/interpolate"
)

// expandArgs expands arguments using env vars
func ExpandArgs(args []string) []string {
var expanded []string
for _, arg := range args {
arg = expandValue(arg)
expanded = append(expanded, arg)
}
return expanded
}

// expandValue interpolates values using env vars
func expandValue(val string) string {
e, err := interpolate.Interpolate(env, val)
if err == nil {
return strings.TrimSpace(string(e))
}
return val
}

// expandParameters expands values using shell-like syntax
func expandParameters(parameters map[string]string, expand bool, expandValues []string) error {

// if global expand is true then just it for all
if expand {
for key, value := range parameters {
parameters[key] = expandValue(value)
}
// can return early as we've done the job
return nil
}
// check if all values that we ask to expand present in the parameters
// otherwise, it's a configuration error
for _, val := range expandValues {
if _, ok := parameters[val]; !ok {
return fmt.Errorf("env var %s is present in the expand-values but doesn't exist in the environment", val)
} else {
// if the var is present we expand it
parameters[val] = expandValue(parameters[val])
}
}

return nil
}
64 changes: 64 additions & 0 deletions ssm/expand_test.go
@@ -0,0 +1,64 @@
package ssm

import "testing"

func TestExpandPresentNotPresent(t *testing.T) {
parameters := make(map[string]string)

if err := expandParameters(parameters, false, []string{"test"}); err == nil {
t.Errorf("expected error when supplying var which is not present in params, but got nil")
}

// set a value
parameters["test"] = "test value"

if err := expandParameters(parameters, false, []string{"test"}); err != nil {
t.Errorf("expected no error, but got: %s", err)
}
}

func TestExpandSelectiveExpansions(t *testing.T) {
t.Setenv("ENVIRONMENT", "teststaging")

parameters := map[string]string{
"DATABASE_NAME": "DB_$ENVIRONMENT", // should be expanded
"SOME_SECRET": "abc$abc", // should not be expanded
}

// don't want to expand all here, just specific vars
if err := expandParameters(parameters, false, []string{"DATABASE_NAME"}); err != nil {
t.Errorf("expected no error, but got: %s", err)
}

if parameters["DATABASE_NAME"] != "DB_teststaging" {
t.Errorf("DATABASE_NAME should be expanded to 'DB_teststaging', but got '%s'", parameters["DATABASE_NAME"])
}
if parameters["SOME_SECRET"] != "abc$abc" {
t.Errorf("SOME_SECRET should not be expanded and be 'abc$abc', but got '%s'", parameters["SOME_SECRET"])
}
}
func TestExpandExpansions(t *testing.T) {
t.Setenv("ENVIRONMENT", "teststaging")
t.Setenv("abc", "def")

parameters := map[string]string{
"DATABASE_NAME": "DB_$ENVIRONMENT", // should be expanded
"SOME_SECRET": "abc$abc", // should not be expanded
}
want := map[string]string{
"DATABASE_NAME": "DB_teststaging",
"SOME_SECRET": "abcdef",
}

// want to expand all
if err := expandParameters(parameters, true, []string{}); err != nil {
t.Errorf("expected no error, but got: %s", err)
}

for key := range want {
if parameters[key] != want[key] {
t.Errorf("%s should be expanded to '%s', but got '%s'", key, want[key], parameters[key])
}
}

}
16 changes: 9 additions & 7 deletions ssm/parameters.go
Expand Up @@ -230,16 +230,18 @@ func getAllParameters(names, paths, plainNames, plainPaths []string, strict, rec
}

// GetParameters returns all parameters by path/names, with optional env vars expansion
func GetParameters(names, paths, plainNames, plainPaths []string, transformationsList []transformations.Transformation, expand, strict, recursive bool) (parameters map[string]string, err error) {
func GetParameters(names, paths, plainNames, plainPaths []string, transformationsList []transformations.Transformation, expand, strict, recursive, expandNames, expandPaths bool, expandValues []string) (parameters map[string]string, err error) {
localNames := names
localPaths := paths
localPlainNames := plainNames
localPlainPaths := plainPaths

if expand {
if expand || expandNames {
localNames = ExpandArgs(names)
localPaths = ExpandArgs(paths)
localPlainNames = ExpandArgs(plainNames)
}
if expand || expandPaths {
localPaths = ExpandArgs(paths)
localPlainPaths = ExpandArgs(plainPaths)
}
allParameters, err := getAllParameters(localNames, localPaths, localPlainNames, localPlainPaths, strict, recursive)
Expand All @@ -253,11 +255,11 @@ func GetParameters(names, paths, plainNames, plainPaths []string, transformation
log.WithError(err).Fatal("Can't merge maps")
}
}
for key, value := range parameters {
if expand {
parameters[key] = ExpandValue(value)
}

if err := expandParameters(parameters, expand, expandValues); err != nil {
log.WithError(err).Fatal("Can't expand vars")
}

for _, transformation := range transformationsList {
parameters, err = transformation.Transform(parameters)
if err != nil {
Expand Down
23 changes: 0 additions & 23 deletions ssm/util.go
Expand Up @@ -2,9 +2,6 @@ package ssm

import (
"os"
"strings"

"github.com/buildkite/interpolate"
)

var env Env
Expand All @@ -25,26 +22,6 @@ func stringSliceDifference(a, b []string) []string {
return ab
}

// ExpandArgs expands arguments using env vars
func ExpandArgs(args []string) []string {
var expanded []string
for _, arg := range args {
arg = ExpandValue(arg)
expanded = append(expanded, arg)
}
return expanded
}

// ExpandValue interpolates values using env vars
func ExpandValue(val string) string {
e, err := interpolate.Interpolate(env, val)
if err == nil {
return strings.TrimSpace(string(e))
}
return val

}

// Env just adapts os.LookupEnv to this interface
type Env struct{}

Expand Down

0 comments on commit af252c1

Please sign in to comment.