-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Miles Maddox
committed
Aug 27, 2018
1 parent
3ef350e
commit e1df239
Showing
12 changed files
with
508 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
scratch |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package cmd | ||
|
||
import ( | ||
"github.com/justmiles/ssm-parameter-store/lib" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func init() { | ||
rootCmd.AddCommand(diffCmd) | ||
} | ||
|
||
// process the list command | ||
var diffCmd = &cobra.Command{ | ||
Use: "diff", | ||
Short: "diff SSM Parameters with those on disk", | ||
Run: func(cmd *cobra.Command, args []string) { | ||
ssmParameterStore.CMDDiff(paths, format, directory) | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package cmd | ||
|
||
import ( | ||
"github.com/justmiles/ssm-parameter-store/lib" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var () | ||
|
||
func init() { | ||
rootCmd.AddCommand(pullCmd) | ||
} | ||
|
||
// process the list command | ||
var pullCmd = &cobra.Command{ | ||
Use: "pull", | ||
Short: "pull SSM Parameters", | ||
Run: func(cmd *cobra.Command, args []string) { | ||
ssmParameterStore.CMDPull(paths, format, directory) | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package cmd | ||
|
||
import ( | ||
"github.com/justmiles/ssm-parameter-store/lib" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var () | ||
|
||
func init() { | ||
rootCmd.AddCommand(pushCmd) | ||
} | ||
|
||
// process the list command | ||
var pushCmd = &cobra.Command{ | ||
Use: "push", | ||
Short: "push SSM Parameters", | ||
Run: func(cmd *cobra.Command, args []string) { | ||
ssmParameterStore.CMDPush(paths, format, directory) | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
var ( | ||
directory, format string | ||
paths []string | ||
) | ||
|
||
func init() { | ||
dir, err := os.Getwd() | ||
check(err) | ||
rootCmd.PersistentFlags().StringVarP(&format, "format", "f", "yaml", "format type") | ||
rootCmd.PersistentFlags().StringVarP(&directory, "directory", "d", dir, "output directory") | ||
rootCmd.PersistentFlags().StringSliceVarP(&paths, "path", "p", []string{"/"}, "path") | ||
} | ||
|
||
// Configure the root command | ||
var rootCmd = &cobra.Command{ | ||
Use: "ssm-parameter-store", | ||
Short: "Sync SSM Parameter Store", | ||
Version: "0.0.1", | ||
Run: func(cmd *cobra.Command, args []string) { | ||
cmd.Help() | ||
}, | ||
} | ||
|
||
// Execute validates input the Cobra CLI | ||
func Execute() { | ||
if err := rootCmd.Execute(); err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
// Log errors if exist and exit | ||
func check(err error) { | ||
if err != nil { | ||
fmt.Printf("ERROR\t%s", err.Error()) | ||
os.Exit(1) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package ssmParameterStore | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/ssm" | ||
"github.com/fatih/color" | ||
) | ||
|
||
// Diff represents data to be added and removed | ||
type Diff struct { | ||
additions []*ssm.PutParameterInput | ||
deletes []*ssm.DeleteParametersInput | ||
asVisual []string | ||
} | ||
|
||
func (diff Diff) String() string { | ||
return strings.Join(diff.asVisual, "\n") + "\n" | ||
} | ||
|
||
func (diff Diff) commit() error { | ||
for _, addition := range diff.additions { | ||
_, err := svc.PutParameter(addition) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
for _, deleteInput := range diff.deletes { | ||
_, err := svc.DeleteParameters(deleteInput) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// AppendDeleteChange appends a line to delete | ||
func (diff *Diff) AppendDeleteChange(path string) error { | ||
diff.asVisual = append(diff.asVisual, color.RedString(fmt.Sprintf("-\t%s", path))) | ||
|
||
if diff.deletes == nil { | ||
diff.deletes = append(diff.deletes, &ssm.DeleteParametersInput{}) | ||
} | ||
|
||
for i, input := range diff.deletes { | ||
if len(input.Names) < 10 { | ||
diff.deletes[i].Names = append(input.Names, aws.String(path)) | ||
} else if len(diff.deletes) < (i + 2) { | ||
diff.deletes = append(diff.deletes, &ssm.DeleteParametersInput{ | ||
Names: aws.StringSlice([]string{path}), | ||
}) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// AppendAddChange appends a line to delete | ||
func (diff *Diff) AppendAddChange(path, desiredValue, currentValue string) error { | ||
if currentValue != "" { | ||
diff.asVisual = append(diff.asVisual, color.YellowString(fmt.Sprintf("~\t%s\t%s --> %s", path, currentValue, desiredValue))) | ||
} else { | ||
diff.asVisual = append(diff.asVisual, color.GreenString(fmt.Sprintf("+\t%s\t%s", path, desiredValue))) | ||
} | ||
|
||
diff.additions = append(diff.additions, &ssm.PutParameterInput{ | ||
// KeyId: "" | ||
Name: aws.String(path), | ||
Overwrite: aws.Bool(true), | ||
Type: aws.String("String"), | ||
Value: aws.String(desiredValue), | ||
}) | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package ssmParameterStore | ||
|
||
import ( | ||
"encoding/json" | ||
|
||
yaml "gopkg.in/yaml.v2" | ||
) | ||
|
||
// ParameterState struct represents a parameter store's value based on path | ||
type ParameterState struct { | ||
EncryptionKey *string `json:"EncryptionKey,omitempty" yaml:"EncryptionKey,omitempty"` | ||
EncryptedKeys []string `json:"EncryptedKeys,omitempty" yaml:"EncryptedKeys,omitempty"` | ||
Parameters map[string]string `json:"Parameters,omitempty" yaml:"Parameters,omitempty"` | ||
} | ||
|
||
func (p *ParameterState) json() ([]byte, error) { | ||
return json.MarshalIndent(p, "", " ") | ||
} | ||
|
||
func (p *ParameterState) yaml() ([]byte, error) { | ||
return yaml.Marshal(p) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
package ssmParameterStore | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
|
||
"github.com/aws/aws-sdk-go/service/ssm" | ||
yaml "gopkg.in/yaml.v2" | ||
) | ||
|
||
// ParameterStates is the format written to or read from disk | ||
type ParameterStates map[string]*ParameterState | ||
|
||
func (p *ParameterStates) json() ([]byte, error) { | ||
return json.MarshalIndent(p, "", " ") | ||
} | ||
|
||
func (p *ParameterStates) yaml() ([]byte, error) { | ||
return yaml.Marshal(p) | ||
} | ||
|
||
func (p *ParameterStates) toDisk(directory string) error { | ||
for key, ps := range *p { | ||
path, file := pathAndKey(&key) | ||
fullpath := directory + path | ||
fullname := fmt.Sprintf("%s/%s.%s", fullpath, file, "yaml") | ||
fmt.Printf("Writing %s.yaml to %s\n", file, fullpath) | ||
err := os.MkdirAll(fullpath, os.ModePerm) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
contents, err := ps.yaml() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = ioutil.WriteFile(fullname, contents, 0644) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
} | ||
|
||
return nil | ||
} | ||
|
||
func (p *ParameterStates) diff(current ParameterStates) (diffs Diff, err error) { | ||
|
||
for path, ps := range *p { | ||
for key, value := range ps.Parameters { | ||
|
||
// Add because the path does not exist in current | ||
if current[path] == nil { | ||
diffs.AppendAddChange(fmt.Sprintf("%s/%s", path, key), value, "") | ||
|
||
// Add because the key does not exist in current | ||
} else if current[path].Parameters[key] == "" { | ||
diffs.AppendAddChange(fmt.Sprintf("%s/%s", path, key), value, "") | ||
|
||
// Add because the key is not up to date in current | ||
} else if value != current[path].Parameters[key] { | ||
diffs.AppendAddChange(fmt.Sprintf("%s/%s", path, key), value, current[path].Parameters[key]) | ||
|
||
} | ||
} | ||
} | ||
|
||
for path, ps := range current { | ||
for key := range ps.Parameters { | ||
|
||
// Delete because desired path does not exist | ||
if (*p)[path] == nil { | ||
diffs.AppendDeleteChange(fmt.Sprintf("%s/%s", path, key)) | ||
|
||
// Delete because desired key does not exist | ||
} else if (*p)[path].Parameters[key] == "" { | ||
diffs.AppendDeleteChange(fmt.Sprintf("%s/%s", path, key)) | ||
} | ||
} | ||
} | ||
|
||
return | ||
} | ||
|
||
func (p *ParameterStates) buildFromSSMParameters(paths []string) { | ||
|
||
var ssmParams []ssm.Parameter | ||
for _, path := range paths { | ||
ssmParams = append(ssmParams, getSSMParameters(path)...) | ||
} | ||
|
||
if *p == nil { | ||
*p = make(ParameterStates) | ||
} | ||
|
||
for _, parameter := range ssmParams { | ||
path, key := pathAndKey(parameter.Name) | ||
if _, ok := (*p)[path]; !ok { | ||
(*p)[path] = &ParameterState{ | ||
Parameters: make(map[string]string), | ||
} | ||
} | ||
(*p)[path].Parameters[key] = *parameter.Value | ||
if *parameter.Type == "SecureString" { | ||
(*p)[path].EncryptedKeys = append((*p)[path].EncryptedKeys, key) | ||
} | ||
} | ||
} | ||
|
||
func (p *ParameterStates) convertFromSSMParameters(parameters []ssm.Parameter) { | ||
if *p == nil { | ||
*p = make(ParameterStates) | ||
} | ||
|
||
for _, parameter := range parameters { | ||
path, key := pathAndKey(parameter.Name) | ||
if _, ok := (*p)[path]; !ok { | ||
(*p)[path] = &ParameterState{ | ||
Parameters: make(map[string]string), | ||
} | ||
} | ||
(*p)[path].Parameters[key] = *parameter.Value | ||
if *parameter.Type == "SecureString" { | ||
(*p)[path].EncryptedKeys = append((*p)[path].EncryptedKeys, key) | ||
} | ||
} | ||
} | ||
|
||
// NewParameterStatesFromSSM reads the current parameter store in AWS returns a ParameterStates | ||
func NewParameterStatesFromSSM(paths []string) (p ParameterStates) { | ||
p.buildFromSSMParameters(paths) | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package ssmParameterStore | ||
|
||
import "fmt" | ||
|
||
// CMDPull handles the command line's ssm-parameter-store get | ||
func CMDPull(paths []string, format string, directory string) { | ||
p := NewParameterStatesFromSSM(paths) | ||
p.toDisk(directory) | ||
} | ||
|
||
// CMDPush executes ssm-parameter-store push from the CLI | ||
func CMDPush(paths []string, format string, directory string) { | ||
desired := NewParameterStatesFromDisk(paths, format, directory) | ||
current := NewParameterStatesFromSSM(paths) | ||
diff, err := desired.diff(current) | ||
Check(err) | ||
fmt.Print(diff) | ||
err = diff.commit() | ||
Check(err) | ||
} | ||
|
||
// CMDDiff executes ssm-parameter-store diff from the CLI | ||
func CMDDiff(paths []string, format string, directory string) { | ||
desired := NewParameterStatesFromDisk(paths, format, directory) | ||
current := NewParameterStatesFromSSM(paths) | ||
diff, err := desired.diff(current) | ||
Check(err) | ||
fmt.Print(diff) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package ssmParameterStore | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
func TestRun(t *testing.T) { | ||
CMDPull([]string{"/dev", "/ops"}, "yaml", "/home/justmiles/go/src/github.com/justmiles/ssm-parameter-store/scratch") | ||
// CMDPush([]string{"/dev", "/ops"}, "yaml", "/home/justmiles/go/src/github.com/justmiles/ssm-parameter-store/scratch") | ||
// CMDDiff([]string{"/dev", "/ops"}, "yaml", "/home/justmiles/go/src/github.com/justmiles/ssm-parameter-store/scratch") | ||
} |
Oops, something went wrong.