Skip to content

Commit

Permalink
add encoded flag to support base64 encoded control files (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
fishnix authored Jun 9, 2020
1 parent 97d9805 commit bfaf502
Show file tree
Hide file tree
Showing 10 changed files with 73 additions and 28 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Usage:
Available Commands:
help Help about any command
run Run executes the taks in the given control file
run Run executes the tasks in the given control file
show Reads and displays a control file on STDOUT
validate Validates the control file
version Displays version information
Expand All @@ -34,7 +34,8 @@ Use "deco [command] --help" for more information about a command.
### Input

`deco` takes a JSON file as input and defaults to `/var/run/secrets/deco.json`. This allows it to work
out of the box in docker swarm with swarm secrets.
out of the box in docker swarm with swarm secrets. The control file can be base64 encoded (standard encoding)
using the `--encoded` flag

The JSON control file has the format:

Expand Down
2 changes: 1 addition & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var (
cfgFile string
baseDir string
httpHeaders []string
encoded bool
)

// Version is the main version number
Expand Down Expand Up @@ -70,7 +71,6 @@ func init() {
cobra.OnInitialize(initConfig)
RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "deco config file -- _not_ the control file (default is $HOME/.deco.yaml)")
RootCmd.PersistentFlags().StringVarP(&baseDir, "dir", "d", "", "Base directory for filtered files/templates")
RootCmd.PersistentFlags().StringArrayVarP(&httpHeaders, "header", "H", []string{}, "Pass a custom header to server")
}

// initConfig reads in config file and ENV variables if set.
Expand Down
3 changes: 2 additions & 1 deletion cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ can be a local file or an http/https endpoint and can be absolute or relative.`,
},
Run: func(cmd *cobra.Command, args []string) {
var c control.Configuration
if err := c.Read(controlLocation, httpHeaders); err != nil {
if err := c.Read(controlLocation, httpHeaders, encoded); err != nil {
Logger.Println("[ERROR] Unable to validate control file.", err)
os.Exit(1)
}
Expand All @@ -66,6 +66,7 @@ can be a local file or an http/https endpoint and can be absolute or relative.`,
}

func init() {
runCmd.Flags().BoolVarP(&encoded, "encoded", "e", false, "Control file is base64 encoded")
runCmd.Flags().StringArrayVarP(&httpHeaders, "header", "H", []string{}, "Pass a custom header to server")
RootCmd.AddCommand(runCmd)
}
6 changes: 6 additions & 0 deletions cmd/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
package cmd

import (
"encoding/base64"
"errors"
"fmt"
"io/ioutil"
Expand Down Expand Up @@ -53,6 +54,10 @@ can be a local file or an http/https endpoint and can be absolute or relative.`,
}
defer r.Close()

if encoded {
r = ioutil.NopCloser(base64.NewDecoder(base64.StdEncoding, r))
}

raw, err := ioutil.ReadAll(r)
if err != nil {
Logger.Println("[ERROR] Unable to read control reader.", err)
Expand All @@ -65,6 +70,7 @@ can be a local file or an http/https endpoint and can be absolute or relative.`,
}

func init() {
showCmd.Flags().BoolVarP(&encoded, "encoded", "e", false, "Control file is base64 encoded")
showCmd.Flags().StringArrayVarP(&httpHeaders, "header", "H", []string{}, "Pass a custom header to server")
RootCmd.AddCommand(showCmd)
}
3 changes: 2 additions & 1 deletion cmd/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ can be a local file or an http/https endpoint and can be absolute or relative.`,
},
Run: func(cmd *cobra.Command, args []string) {
var c control.Configuration
if err := c.Read(controlLocation, httpHeaders); err != nil {
if err := c.Read(controlLocation, httpHeaders, encoded); err != nil {
Logger.Println("[ERROR] Unable to validate control file.", err)
os.Exit(1)
}
Expand All @@ -56,6 +56,7 @@ can be a local file or an http/https endpoint and can be absolute or relative.`,
}

func init() {
validateCmd.Flags().BoolVarP(&encoded, "encoded", "e", false, "Control file is base64 encoded")
validateCmd.Flags().StringArrayVarP(&httpHeaders, "header", "H", []string{}, "Pass a custom header to server")
RootCmd.AddCommand(validateCmd)
}
7 changes: 6 additions & 1 deletion control/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,17 @@ func Get(location string, headers []string) (io.ReadCloser, error) {
}

// Read reads in the configuration and returns the object
func (c *Configuration) Read(location string, headers []string) error {
func (c *Configuration) Read(location string, headers []string, encoded bool) error {
r, err := Get(location, headers)
if err != nil {
return err
}
defer r.Close()

if encoded {
return json.NewDecoder(base64.NewDecoder(base64.StdEncoding, r)).Decode(c)
}

return json.NewDecoder(r).Decode(c)
}

Expand Down
54 changes: 34 additions & 20 deletions control/control_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package control_test

import (
"encoding/base64"
"fmt"
"io/ioutil"
"log"
Expand Down Expand Up @@ -45,40 +46,53 @@ var testDecoStruct = control.Configuration{
}

func TestReadFile(t *testing.T) {
testFile := createTemporaryConfigFile()
defer os.Remove(testFile.Name())

filename := testFile.Name()
var actual control.Configuration
actual.Read(filename, []string{})
for _, e := range []bool{true, false} {
testFile := createTemporaryConfigFile(e)
defer os.Remove(testFile.Name())

filename := testFile.Name()
var actual control.Configuration
if err := actual.Read(filename, []string{}, e); err != nil {
t.Errorf("failed to read config: %s", err)
}

for filterFile, filterMap := range testDecoStruct.Filters {
if actualFilterMap := actual.Filters[filterFile]; actualFilterMap != nil {
for find, replace := range filterMap {
if actualFilterMap[find] != replace {
t.Errorf("control.Read(%s) for key '%s', got replacement '%s', expected '%s'", filename, find, actualFilterMap[find], replace)
for filterFile, filterMap := range testDecoStruct.Filters {
if actualFilterMap := actual.Filters[filterFile]; actualFilterMap != nil {
for find, replace := range filterMap {
if actualFilterMap[find] != replace {
t.Errorf("control.Read(%s) for key '%s', got replacement '%s', expected '%s'", filename, find, actualFilterMap[find], replace)
}
}
} else {
t.Errorf("control.Read(%s) returned nil filter map for file.", filename)
}
} else {
t.Errorf("control.Read(%s) returned nil filter map for file.", filename)
}
}
}

func createTemporaryConfigFile() *os.File {
func createTemporaryConfigFile(encoded bool) *os.File {
var tmpfile *os.File
content := []byte(testDecoString)

tmpfile, err := ioutil.TempFile("", "decotest")
if err != nil {
log.Fatal(err)
}

if _, err := tmpfile.Write(content); err != nil {
log.Fatal(err)
}
if encoded {
encoder := base64.NewEncoder(base64.StdEncoding, tmpfile)
encoder.Write(content)
if err := encoder.Close(); err != nil {
log.Fatal(err)
}
} else {
if _, err := tmpfile.Write(content); err != nil {
log.Fatal(err)
}

if err := tmpfile.Close(); err != nil {
log.Fatal(err)
if err := tmpfile.Close(); err != nil {
log.Fatal(err)
}
}

return tmpfile
Expand All @@ -103,7 +117,7 @@ func TestReadURL(t *testing.T) {
defer ts.Close()

var actual control.Configuration
err := actual.Read(ts.URL, []string{"foo=bar", "biz=baz"})
err := actual.Read(ts.URL, []string{"foo=bar", "biz=baz"}, false)
if err != nil {
t.Errorf("Expected to successfully read for test URL")
}
Expand Down
18 changes: 17 additions & 1 deletion control/ssm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,24 @@ import (
"github.com/aws/aws-sdk-go/service/ssm/ssmiface"
)

var testDecoString string = `
{
"filters": {
"test/file1": {
"string1": "value1",
"string2": "value2",
"string3": "value3"
},
"test/file2": {
"string1": "othervalue1"
}
}
}
`

var testParam = ssm.Parameter{
ARN: aws.String("arn:aws:ssm:us-east-1:846761448161:parameter/spinup/testapi/dev/deco.json"),
ARN: aws.String("arn:aws:ssm:us-east-1:846761448161:parameter/spinup/testapi/dev/deco.json"),
Value: aws.String(testDecoString),
}

func (m *mockSSMClient) GetParameter(input *ssm.GetParameterInput) (*ssm.GetParameterOutput, error) {
Expand Down
2 changes: 1 addition & 1 deletion deco/version.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package deco

// Version is the main version number that is being run at the moment.
const Version = "0.4.1"
const Version = "0.5.0"

// VersionPrerelease is a pre-release marker for the version. If this is ""
// then it means that it is a final release. Otherwise, this is a pre-release
Expand Down
1 change: 1 addition & 0 deletions example/deco.json.b64
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ewogICAgImZpbHRlcnMiOiB7CiAgICAgICAgImV4YW1wbGUvY29uZi9leGFtcGxlLnR4dCI6IHsKICAgICAgICAgICAgImhlbGxvV29ybGQiOiAiSGVsbG8gV29ybGQhIiwKICAgICAgICAgICAgImJhc2U2NFN0cmluZyI6ICJTR1ZzYkc4Z1YyOXliR1E9IiwKICAgICAgICAgICAgImxvbmdCYXNlNjRFbmNvZGVkU3RyaW5nIjogIlBIQStURzl5WlcwZ2FYQnpkVzBnWkc5c2IzSWdjMmwwSUdGdFpYUXNJR052Ym5ObFkzUmxkSFZ5SUdGa2FYQnBjMk5wYm1jZ1pXeHBkQzRnUm5WelkyVWdjMlZrSUcxcElHRWdiMlJwYnlCd2FHRnlaWFJ5WVNCdGIyeHNhWE11SUUxaGRYSnBjeUJwYlhCbGNtUnBaWFFzSUhKcGMzVnpJR2xrSUcxaGRIUnBjeUJ6YjJSaGJHVnpMQ0J5YVhOMWN5QnpaVzBnZG1WdVpXNWhkR2x6SUcxcExDQnBiaUIwWlcxd2IzSWdaV3hwZENCeWFYTjFjeUJsWjJWMElHRjFaM1ZsTGlCTmIzSmlhU0J5ZFhSeWRXMGdaWFZwYzIxdlpDQnRZWFZ5YVhNc0lHbHVJR2x0Y0dWeVpHbGxkQ0IwYjNKMGIzSWdkSEpwYzNScGNYVmxJR0V1SUUxaFpXTmxibUZ6SUhWc2RISnBZMlZ6SUdSMWFTQnBaQ0JqZFhKemRYTWdabkpwYm1kcGJHeGhMaUJPWVcwZ2JHRnZjbVZsZEN3Z1lYVm5kV1VnY0hKbGRHbDFiU0IxYkhSeWFXTmxjeUIwWlcxd2IzSXNJRzFoWjI1aElHMWhkWEpwY3lCbFoyVnpkR0Z6SUdWNExDQnViMjRnZEhKcGMzUnBjWFZsSUd4bFkzUjFjeUIxY201aElHRjBJRzFoWjI1aExpQk5ZV1ZqWlc1aGN5Qm1aWFZuYVdGMElHVnNhWFFnZG1sMFlXVWdibVZ4ZFdVZ1kzVnljM1Z6SUdOdmJuTmxZM1JsZEhWeUxpQkJaVzVsWVc0Z1pYVWdZV3hwY1hWbGRDQmhiblJsTENCelpXUWdjM1Z6WTJsd2FYUWdiV2t1SUU1MWJHeGhiU0IyWVhKcGRYTWdkbVZzSUc1bGNYVmxJR2x1SUhOaFoybDBkR2x6TGlCRWIyNWxZeUJwWkNCa2FXRnRJSFpsYUdsamRXeGhMQ0JzWVdOcGJtbGhJRzVwWW1nZ2JtVmpMQ0J0WVhocGJYVnpJSFJ2Y25SdmNpNGdUVzl5WW1rZ2FXUWdiRzl5WlcwZ2RIVnljR2x6TGlCTmIzSmlhU0JtWlhKdFpXNTBkVzBnWVdNZ2NtbHpkWE1nY1hWcGN5QmliR0Z1WkdsMExpQk9kV3hzWVNCaFkyTjFiWE5oYml3Z2JtbHpiQ0JoSUhCb1lYSmxkSEpoSUhCbGJHeGxiblJsYzNGMVpTd2dZWEpqZFNCdFlYTnpZU0JoYkdseGRXVjBJSE5oY0dsbGJpd2djMlZrSUhSeWFYTjBhWEYxWlNCc1pXTjBkWE1nYzJWdElIWmxiQ0J2WkdsdkxpQkpiblJsWjJWeUlIVnNiR0Z0WTI5eWNHVnlJR1poWTJsc2FYTnBjeUJ0WVhOellTQjJhWFJoWlNCemRYTmphWEJwZEM0OEwzQStJQTBLUEhBK1VYVnBjM0YxWlNCamIyNXpaWEYxWVhRZ1pYSnZjeUJsWjJWMElHRjFaM1ZsSUdOdmJXMXZaRzhnWTNWeWMzVnpMaUJKYmlCMGNtbHpkR2x4ZFdVZ1ptbHVhV0oxY3lCamIyNTJZV3hzYVhNdUlGTmxaQ0J0WVhSMGFYTWdkSEpwYzNScGNYVmxJR1Z6ZENCbFoyVjBJR2xoWTNWc2FYTXVJRTFoWldObGJtRnpJR3gxWTNSMWN5QmxaMlZ6ZEdGeklHeGhZM1Z6SUc1dmJpQmxiR1Z0Wlc1MGRXMHVJRVoxYzJObElHVm5aWE4wWVhNc0lHUjFhU0J6YVhRZ1lXMWxkQ0J6ZFhOamFYQnBkQ0J3ZFd4MmFXNWhjaXdnYkc5eVpXMGdiblZ1WXlCdFlXeGxjM1ZoWkdFZ1pYSnZjeXdnWldkbGRDQmpiMjV6WldOMFpYUjFjaUJsY205eklHNXBjMmtnZFhRZ2JXRjFjbWx6TGlCRGRYSmhZbWwwZFhJZ2NHeGhZMlZ5WVhRZ2NHOXlkR0VnY0c5eWRIUnBkRzl5TGlCT1lXMGdZV01nYldGbmJtRWdjMlZrSUdwMWMzUnZJR1ZuWlhOMFlYTWdjbWh2Ym1OMWN5QnBaQ0J6WldRZ2JHbG5kV3hoTGlCRGNtRnpJR2R5WVhacFpHRWdkR1ZzYkhWeklITjFjMk5wY0dsMElHNWxjWFZsSUhabGFHbGpkV3hoSUhCdmNuUmhMand2Y0Q0Z0RRbzhjRDVUWldRZ2JHRnZjbVZsZENCc2IzSmxiU0JsZENCelpXMXdaWElnZFd4MGNtbGphV1Z6TGlCUmRXbHpjWFZsSUhKcGMzVnpJSFJsYkd4MWN5d2djMlZ0Y0dWeUlHRWdZbWxpWlc1a2RXMGdhV1FzSUdOdmJtZDFaU0J1WldNZ2JtbHpiQzRnVm1WemRHbGlkV3gxYlNCMlpXd2diR2xpWlhKdklHbGtJR1Z1YVcwZ2RHbHVZMmxrZFc1MElIWnBkbVZ5Y21FdUlGRjFhWE54ZFdVZ2NHVnNiR1Z1ZEdWemNYVmxJSFZzZEhKcFkyVnpJRzVwWW1nZ2MybDBJR0Z0WlhRZ2JHRmphVzVwWVM0Z1NXNGdZWFFnYm5Wc2JHRWdhblZ6ZEc4dUlFNTFiR3hoYlNCemFYUWdZVzFsZENCaFkyTjFiWE5oYmlCelpXMHNJSFYwSUdWbVptbGphWFIxY2lCc2IzSmxiUzRnVUhKdmFXNGdjMlZ0Y0dWeUlIVnlibUVnWldkbGRDQndhR0Z5WlhSeVlTQmxiR1ZwWm1WdVpDNGdUV0ZsWTJWdVlYTWdaV1ptYVdOcGRIVnlJSE5qWld4bGNtbHpjWFZsSUhOalpXeGxjbWx6Y1hWbExpQk9ZVzBnY0doaGNtVjBjbUVnWlc1cGJTQmhkQ0JwYm5SbGNtUjFiU0IyZFd4d2RYUmhkR1V1SUZabGMzUnBZblZzZFcwZ2NIVnNkbWx1WVhJZ1pHOXNiM0lnYm1WaklHeGhZM1Z6SUdSaGNHbGlkWE1zSUc1dmJpQmpiMjVrYVcxbGJuUjFiU0JoY21OMUlHWmhkV05wWW5Wekxqd3ZjRDRnRFFvOGNENVZkQ0J0WVhOellTQnRZWE56WVN3Z2IzSnVZWEpsSUhacGRtVnljbUVnYVdGamRXeHBjeUJ6WldRc0lHTjFjbk4xY3lCbGJHVnBabVZ1WkNCcGNITjFiUzRnVTNWemNHVnVaR2x6YzJVZ2IzSnVZWEpsSUcxaFoyNWhJR2x3YzNWdExDQnpaV1FnYzJWdGNHVnlJSFJsYkd4MWN5QmxaMlZ6ZEdGeklHRXVJRkJ5YjJsdUlITnBkQ0JoYldWMElHRmpZM1Z0YzJGdUlHeGxieTRnVFdGMWNtbHpJR3hoWTJsdWFXRXNJRzUxYm1NZ2FXNGdkbVZvYVdOMWJHRWdhVzUwWlhKa2RXMHNJR1Z6ZENCdGFTQjJiMngxZEhCaGRDQmxjM1FzSUdsdWRHVnlaSFZ0SUdacGJtbGlkWE1nWVc1MFpTQnVhWE5zSUhabGJDQnNhV2QxYkdFdUlGWnBkbUZ0ZFhNZ2JHRmphVzVwWVNCbGJtbHRJR0YwSUhOaGNHbGxiaUIxYkd4aGJXTnZjbkJsY2lCMllYSnBkWE11SUZObFpDQnBiaUIyWlhOMGFXSjFiSFZ0SUdSMWFTNGdSSFZwY3lCc2IzSmxiU0JrZFdrc0lIVnNkSEpwWTJsbGN5QmhkQ0J1YVhOcElITnBkQ0JoYldWMExDQjJkV3h3ZFhSaGRHVWdZbXhoYm1ScGRDQnNZV04xY3k0Z1RXOXlZbWtnYzJsMElHRnRaWFFnWld4cGRDQnZaR2x2TGlCSmJpQmxabVpwWTJsMGRYSWdaV3hwZENCeGRXbHpJRzFwSUcxaGVHbHRkWE1nYzJWdGNHVnlJR2x1SUdsa0lHMWhaMjVoTGlCUWNtOXBiaUJsZFNCemRYTmphWEJwZENCMFpXeHNkWE1zSUdGMElHMXZiR1Z6ZEdsbElIUnZjblJ2Y2k0OEwzQStJQTBLUEhBK1EzSmhjeUJ0WVhocGJYVnpJR1Y0SUc1dmJpQnFkWE4wYnlCaWFXSmxibVIxYlN3Z2NuVjBjblZ0SUhCb1lYSmxkSEpoSUdGeVkzVWdjSEpsZEdsMWJTNGdVR2hoYzJWc2JIVnpJR3hoWTJsdWFXRWdkV3gwY21samFXVnpJR3hwWjNWc1lTQmxkU0JoYkdseGRXVjBMaUJPZFd4c1lTQmxkU0JzWVdOMWN5QjBiM0owYjNJdUlFNTFiR3hoYlNCbmNtRjJhV1JoTENCbGJtbHRJSFYwSUhaMWJIQjFkR0YwWlNCc1lXOXlaV1YwTENCa2RXa2dibWxpYUNCMGFXNWphV1IxYm5RZ2RYSnVZU3dnZG1Wc0lIQmxiR3hsYm5SbGMzRjFaU0JsZUNCc2FXZDFiR0VnWkdGd2FXSjFjeUJoZFdkMVpTNGdSSFZwY3lCd2IzSjBZU3dnYkdsbmRXeGhJRzVsWXlCdFlXeGxjM1ZoWkdFZ2RXeDBjbWxqWlhNc0lHVnNhWFFnYldrZ1pXeGxhV1psYm1RZ1lXNTBaU3dnWVdNZ2FHVnVaSEpsY21sMElHcDFjM1J2SUhSdmNuUnZjaUJ1WldNZ2JuVnVZeTRnVm1sMllXMTFjeUJsZENCMlpXeHBkQ0IyWld3Z2MyVnRJSE52Ykd4cFkybDBkV1JwYmlCMlpYTjBhV0oxYkhWdElHbGtJR2xrSUdWNExpQlFhR0Z6Wld4c2RYTWdaRzlzYjNJZ2JHbGlaWEp2TENCMmFYWmxjbkpoSUhCbGJHeGxiblJsYzNGMVpTQjFjbTVoSUdWMUxDQnlkWFJ5ZFcwZ1lXeHBjWFZsZENCdWFYTnNMaUJFZFdseklHWmhkV05wWW5WeklIUnZjblJ2Y2lCaGNtTjFMaUJWZENCd1pXeHNaVzUwWlhOeGRXVWdaR2xoYlNCaGRDQnNaV04wZFhNZ1ptRjFZMmxpZFhNZ2JXRjBkR2x6TGlCUVpXeHNaVzUwWlhOeGRXVWdZV3hwY1hWaGJTQnBiaUJoYm5SbElHVm5aWFFnYzJGbmFYUjBhWE11UEM5d1BpQU5DZz09IiwKICAgICAgICAgICAgImNvbmRpdGlvbmFsbHlEb0FUaGluZyI6ICJ0cnVlIiwKICAgICAgICAgICAgImNvbmRpdGlvbmFsbHlEb250RG9BVGhpbmciOiAiZmFsc2UiCiAgICAgICAgfSwKICAgICAgICAiZXhhbXBsZS9jb25mL3NlcnZlci54bWwiOiB7CiAgICAgICAgICAgICJodHRwUG9ydCI6ICI4MDgwIiwKICAgICAgICAgICAgImh0dHBzUG9ydCI6ICI4NDQzIiwKICAgICAgICAgICAgInJlZGlyZWN0UG9ydCI6ICI4NDQzIgogICAgICAgIH0sCiAgICAgICAgImV4YW1wbGUvY29uZi9jb250ZXh0LnhtbCI6IHsKICAgICAgICAgICAgImRpc2FibGVTZXNzaW9uUGVyc2lzdCI6ICJ0cnVlIgogICAgICAgIH0KICAgIH0KfQ==

0 comments on commit bfaf502

Please sign in to comment.