Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support casing for environmental keys #18

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
build:
bash build.sh
bash build.sh

test:
go test .
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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 <upper|lower>` 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

```
Expand Down
50 changes: 34 additions & 16 deletions aws-env.go
Original file line number Diff line number Diff line change
@@ -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() {
Expand All @@ -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")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Case" should not be required and by default it should not change string-case as currently it breaks backward compatibility - it's now by default changing letters case to upper.

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 {
Expand All @@ -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)
Expand All @@ -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)
}
70 changes: 70 additions & 0 deletions aws-env_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
}