Skip to content

Commit

Permalink
Add selective parameter expansion flags and implementation
Browse files Browse the repository at this point in the history
Eugene Dementyev authored and ekini committed Jan 30, 2022
1 parent a5d470d commit af252c1
Showing 9 changed files with 178 additions and 31 deletions.
37 changes: 37 additions & 0 deletions .github/workflows/build_test.yml
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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")
3 changes: 3 additions & 0 deletions cmd/print.go
Original file line number Diff line number Diff line change
@@ -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")
8 changes: 7 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
@@ -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.")
@@ -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"))
3 changes: 3 additions & 0 deletions cmd/run.go
Original file line number Diff line number Diff line change
@@ -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")
52 changes: 52 additions & 0 deletions ssm/expand.go
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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)
@@ -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 {
23 changes: 0 additions & 23 deletions ssm/util.go
Original file line number Diff line number Diff line change
@@ -2,9 +2,6 @@ package ssm

import (
"os"
"strings"

"github.com/buildkite/interpolate"
)

var env Env
@@ -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{}

0 comments on commit af252c1

Please sign in to comment.