diff --git a/Makefile b/Makefile index 6908907..63651de 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,5 @@ build: - bash build.sh \ No newline at end of file + bash build.sh + +test: + go test . \ No newline at end of file diff --git a/README.md b/README.md index 69cc4b8..46be898 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ $ export DB_USERNAME=$'Username' $ export DB_PASSWORD=$'SecretPassword' ``` +### Optional Flags +*Recursive* You can also pass the `--recursive` flag. When specified, aws-env will recursively fetch parameters starting from the base path specified in `AWS_ENV_PATH`. For the exported environment variables, any `/` characters from sub-paths will be converted to `_` characters. For example: @@ -47,7 +49,27 @@ $ aws ssm put-parameter --name /prod/my-app/db1/DB_PASSWORD --value "OtherSecret $ export db0_DB_PASSWORD=$'SecretPassword' $ export db1_DB_PASSWORD=$'OtherSecretPassword' ``` +*Case* +You can also pass `--case ` to convert the ENV **KEY** to upper or lower case +With the following parameters: +``` +$ aws ssm put-parameter --name /prod/my-app/db_password --value "SecretPassword" --type SecureString --key-id "alias/aws/ssm0" --region us-west-2 +``` + +`eval $(AWS_ENV_PATH=/prod/my-app/ AWS_REGION=us-west-2 ./aws-env --case upper)` will output: +``` +$ export DB_PASSWORD=$'SecretPassword' +``` + +``` +$ aws ssm put-parameter --name /prod/my-app/DB_PASSWORD --value "SecretPassword" --type SecureString --key-id "alias/aws/ssm0" --region us-west-2 +``` + +`eval $(AWS_ENV_PATH=/prod/my-app/ AWS_REGION=us-west-2 ./aws-env --case lower)` will output: +``` +$ export db_password=$'SecretPassword' +``` ## Example Dockerfile ``` diff --git a/aws-env.go b/aws-env.go index 2c24b2f..d9e5438 100644 --- a/aws-env.go +++ b/aws-env.go @@ -1,14 +1,20 @@ package main import ( + "flag" "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/ssm" "log" "os" "strings" - "flag" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/ssm" +) + +const ( + upper = "upper" + lower = "lower" ) func main() { @@ -17,13 +23,19 @@ func main() { return } - recursivePtr := flag.Bool("recursive", false, "recursively process parameters on path") - flag.Parse() + recursivePtr := flag.Bool("recursive", false, "recursively process parameters on path") + convertCase := flag.String("case", upper, "Converts ENV Key to upper or lower case") + flag.Parse() + + if *convertCase == upper || *convertCase == lower { + } else { + log.Fatal("Unsupported case option. Must be 'upper' or 'lower'") + } sess := CreateSession() client := CreateClient(sess) - ExportVariables(client, os.Getenv("AWS_ENV_PATH"), *recursivePtr, "") + ExportVariables(client, os.Getenv("AWS_ENV_PATH"), *recursivePtr, *convertCase, "") } func CreateSession() *session.Session { @@ -34,12 +46,12 @@ func CreateClient(sess *session.Session) *ssm.SSM { return ssm.New(sess) } -func ExportVariables(client *ssm.SSM, path string, recursive bool, nextToken string) { - input := &ssm.GetParametersByPathInput{ - Path: &path, - WithDecryption: aws.Bool(true), - Recursive: aws.Bool(recursive), - } +func ExportVariables(client *ssm.SSM, path string, recursive bool, convertCase string, nextToken string) { + input := &ssm.GetParametersByPathInput{ + Path: &path, + WithDecryption: aws.Bool(true), + Recursive: aws.Bool(recursive), + } if nextToken != "" { input.SetNextToken(nextToken) @@ -52,20 +64,26 @@ func ExportVariables(client *ssm.SSM, path string, recursive bool, nextToken str } for _, element := range output.Parameters { - PrintExportParameter(path, element) + PrintExportParameter(path, element, convertCase) } if output.NextToken != nil { - ExportVariables(client, path, recursive, *output.NextToken) + ExportVariables(client, path, recursive, convertCase, *output.NextToken) } } -func PrintExportParameter(path string, parameter *ssm.Parameter) { +func PrintExportParameter(path string, parameter *ssm.Parameter, convertCase string) { name := *parameter.Name value := *parameter.Value env := strings.Replace(strings.Trim(name[len(path):], "/"), "/", "_", -1) value = strings.Replace(value, "\n", "\\n", -1) + switch convertCase { + case upper: + env = strings.ToUpper(env) + case lower: + env = strings.ToLower(env) + } fmt.Printf("export %s=$'%s'\n", env, value) } diff --git a/aws-env_test.go b/aws-env_test.go new file mode 100644 index 0000000..7a84287 --- /dev/null +++ b/aws-env_test.go @@ -0,0 +1,70 @@ +package main + +import ( + "bytes" + "io" + "os" + "testing" + + "github.com/aws/aws-sdk-go/service/ssm" +) + +type testCaseSlice struct { + Name string + Value string + Path string + ConvertCase string + + ExpectedValue string +} + +func TestPrintExportParamater(t *testing.T) { + testCases := []testCaseSlice{ + { + Name: "/production/mysql_password", + Value: "passwerd", + Path: "/production", + ConvertCase: "upper", + + ExpectedValue: "export MYSQL_PASSWORD=$'passwerd'\n", + }, + { + + Name: "/production/mysql_password", + Value: "passwerd", + Path: "/production", + ConvertCase: "lower", + + ExpectedValue: "export mysql_password=$'passwerd'\n", + }, + } + for _, testCase := range testCases { + + fakeSSM := &ssm.Parameter{ + Name: &testCase.Name, + Value: &testCase.Value, + } + + old := os.Stdout // keep backup of the real stdout + r, w, _ := os.Pipe() + os.Stdout = w + + PrintExportParameter(testCase.Path, fakeSSM, testCase.ConvertCase) + outC := make(chan string) + // copy the output in a separate goroutine so printing can't block indefinitely + go func() { + var buf bytes.Buffer + io.Copy(&buf, r) + outC <- buf.String() + }() + + // back to normal state + w.Close() + os.Stdout = old // restoring the real stdout + out := <-outC + + if out != testCase.ExpectedValue { + t.Errorf("Action: %scase failed. We expected: %s. But we got: %s.", testCase.ConvertCase, testCase.ExpectedValue, out) + } + } +}