Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add --json flag to
config list
command
A big refactor and tests
- Loading branch information
Showing
7 changed files
with
366 additions
and
90 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
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,119 @@ | ||
package app | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"sort" | ||
"strings" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/ssm" | ||
"github.com/juju/ansiterm" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
type ConfigVariable struct { | ||
Name string | ||
Value string | ||
Managed bool | ||
parameterName string | ||
} | ||
|
||
// LoadManaged loads the Managed value for the ConfigVariable from SSM tags | ||
func (v *ConfigVariable) LoadManaged(ssmListTagsForResource func(*ssm.ListTagsForResourceInput) (*ssm.ListTagsForResourceOutput, error)) error { | ||
logrus.WithFields(logrus.Fields{"parameter": v.parameterName}).Debug("loading parameter tag") | ||
resp, err := ssmListTagsForResource(&ssm.ListTagsForResourceInput{ | ||
ResourceId: &v.parameterName, | ||
ResourceType: aws.String(ssm.ResourceTypeForTaggingParameter), | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, tag := range resp.TagList { | ||
if *tag.Key == "aws:cloudformation:stack-id" || *tag.Key == "apppack:cloudformation:stack-id" { | ||
v.Managed = true | ||
|
||
return nil | ||
} | ||
} | ||
|
||
v.Managed = false | ||
|
||
return nil | ||
} | ||
|
||
type ConfigVariables []*ConfigVariable | ||
|
||
// NewConfigVariables creates a new AppConfigVariables from the provided SSM parameters | ||
func NewConfigVariables(parameters []*ssm.Parameter) ConfigVariables { | ||
var configVars ConfigVariables | ||
|
||
for _, parameter := range parameters { | ||
parts := strings.Split(*parameter.Name, "/") | ||
name := parts[len(parts)-1] | ||
configVars = append(configVars, &ConfigVariable{ | ||
Name: name, | ||
Value: *parameter.Value, | ||
parameterName: *parameter.Name, | ||
}) | ||
} | ||
|
||
sort.Slice(configVars, func(i, j int) bool { | ||
return configVars[i].Name < configVars[j].Name | ||
}) | ||
|
||
return configVars | ||
} | ||
|
||
// Transform runs the provided function on each config variable | ||
func (a *ConfigVariables) Transform(transformer func(*ConfigVariable) error) error { | ||
for _, configVar := range *a { | ||
if err := transformer(configVar); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// ToJSON returns a JSON representation of the config variables | ||
func (a *ConfigVariables) ToJSON() (*bytes.Buffer, error) { | ||
results := map[string]string{} | ||
|
||
for _, configVar := range *a { | ||
results[configVar.Name] = configVar.Value | ||
} | ||
|
||
return toJSON(results) | ||
} | ||
|
||
// ToJSONUnmanaged returns a JSON representation of the unmanaged config variables | ||
func (a *ConfigVariables) ToJSONUnmanaged() (*bytes.Buffer, error) { | ||
results := map[string]string{} | ||
|
||
for _, configVar := range *a { | ||
if configVar.Managed { | ||
continue | ||
} | ||
|
||
results[configVar.Name] = configVar.Value | ||
} | ||
|
||
return toJSON(results) | ||
} | ||
|
||
// printRow prints a single row of the table to the TabWriter | ||
func printRow(w *ansiterm.TabWriter, name, value string) { | ||
w.SetForeground(ansiterm.Green) | ||
fmt.Fprintf(w, "%s:", name) | ||
w.SetForeground(ansiterm.Default) | ||
fmt.Fprintf(w, "\t%s\n", value) | ||
} | ||
|
||
// ToConsole prints the config vars to the console via the TabWriter | ||
func (a *ConfigVariables) ToConsole(w *ansiterm.TabWriter) { | ||
for _, configVar := range *a { | ||
printRow(w, configVar.Name, configVar.Value) | ||
} | ||
} |
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,162 @@ | ||
package app_test | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"testing" | ||
|
||
"github.com/apppackio/apppack/app" | ||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/ssm" | ||
"github.com/juju/ansiterm" | ||
) | ||
|
||
var errMock = errors.New("mock error") | ||
|
||
func TestConfigVariablesToJSON(t *testing.T) { | ||
t.Parallel() | ||
|
||
c := app.NewConfigVariables([]*ssm.Parameter{ | ||
{Name: aws.String("/apppack/apps/myapp/FOO"), Value: aws.String("bar")}, | ||
}) | ||
|
||
js, err := c.ToJSON() | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
|
||
expected := `{ | ||
"FOO": "bar" | ||
}` | ||
actual := js.String() | ||
|
||
if actual != expected { | ||
t.Errorf("expected %s, got %s", expected, actual) | ||
} | ||
} | ||
|
||
func TestConfigVariablesToJSONUnmanaged(t *testing.T) { | ||
t.Parallel() | ||
|
||
c := app.ConfigVariables([]*app.ConfigVariable{ | ||
{Name: "FOO", Value: "bar", Managed: false}, | ||
{Name: "BAZ", Value: "qux", Managed: true}, | ||
}) | ||
js, err := c.ToJSONUnmanaged() | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
|
||
expected := `{ | ||
"FOO": "bar" | ||
}` | ||
actual := js.String() | ||
|
||
if actual != expected { | ||
t.Errorf("expected %s, got %s", expected, actual) | ||
} | ||
} | ||
|
||
func TestConfigVariablesToConsole(t *testing.T) { | ||
t.Parallel() | ||
|
||
c := app.NewConfigVariables([]*ssm.Parameter{ | ||
{Name: aws.String("/apppack/apps/myapp/config/FOO"), Value: aws.String("bar")}, | ||
{Name: aws.String("/apppack/apps/myapp/LONGERVARIABLEFOO"), Value: aws.String("baz")}, | ||
}) | ||
out := &bytes.Buffer{} | ||
w := ansiterm.NewTabWriter(out, 8, 8, 0, '\t', 0) | ||
c.ToConsole(w) | ||
|
||
expected := []byte("FOO:\t\t\tbar\nLONGERVARIABLEFOO:\tbaz\n") | ||
|
||
w.Flush() | ||
|
||
actual := out.Bytes() | ||
if !bytes.Equal(actual, expected) { | ||
t.Errorf("expected %b, got %b", expected, actual) | ||
} | ||
} | ||
|
||
func TestConfigVariablesTransform(t *testing.T) { | ||
t.Parallel() | ||
|
||
c := app.NewConfigVariables([]*ssm.Parameter{ | ||
{Name: aws.String("/apppack/apps/myapp/config/FOO"), Value: aws.String("bar")}, | ||
{Name: aws.String("/apppack/apps/myapp/config/BAZ"), Value: aws.String("qux")}, | ||
}) | ||
transformedVals := map[string]string{ | ||
"FOO": "newvalue_foo", | ||
"BAZ": "newvalue_qux", | ||
} | ||
|
||
err := c.Transform(func(v *app.ConfigVariable) error { | ||
v.Value = transformedVals[v.Name] | ||
|
||
return nil | ||
}) | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
|
||
for _, v := range c { | ||
if v.Value != transformedVals[v.Name] { | ||
t.Errorf("expected %s, got %s", transformedVals[v.Name], v.Value) | ||
} | ||
} | ||
|
||
err = c.Transform(func(*app.ConfigVariable) error { return errMock }) | ||
|
||
if !errors.Is(err, errMock) { | ||
t.Errorf("expected %s, got %s", errMock, err) | ||
} | ||
} | ||
|
||
func TestConfigVariableLoadManaged(t *testing.T) { | ||
t.Parallel() | ||
|
||
managedVar := app.NewConfigVariables([]*ssm.Parameter{ | ||
{Name: aws.String("/apppack/apps/myapp/config/FOO"), Value: aws.String("bar")}, | ||
})[0] | ||
|
||
unmanagedVar := managedVar | ||
|
||
managedFunc := func(*ssm.ListTagsForResourceInput) (*ssm.ListTagsForResourceOutput, error) { | ||
return &ssm.ListTagsForResourceOutput{ | ||
TagList: []*ssm.Tag{{Key: aws.String("aws:cloudformation:stack-id"), Value: aws.String("stackid")}}, | ||
}, nil | ||
} | ||
|
||
unmanagedFunc := func(*ssm.ListTagsForResourceInput) (*ssm.ListTagsForResourceOutput, error) { | ||
return &ssm.ListTagsForResourceOutput{TagList: []*ssm.Tag{}}, nil | ||
} | ||
|
||
errorFunc := func(*ssm.ListTagsForResourceInput) (*ssm.ListTagsForResourceOutput, error) { | ||
return nil, errMock | ||
} | ||
|
||
scenarios := []struct { | ||
cVar *app.ConfigVariable | ||
f func(*ssm.ListTagsForResourceInput) (*ssm.ListTagsForResourceOutput, error) | ||
expected bool | ||
}{ | ||
{cVar: managedVar, f: managedFunc, expected: true}, | ||
{cVar: unmanagedVar, f: unmanagedFunc, expected: false}, | ||
} | ||
|
||
for _, s := range scenarios { | ||
err := s.cVar.LoadManaged(s.f) | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
|
||
if s.cVar.Managed != s.expected { | ||
t.Errorf("expected %t, got %t", s.expected, s.cVar.Managed) | ||
} | ||
} | ||
|
||
err := managedVar.LoadManaged(errorFunc) | ||
if !errors.Is(err, errMock) { | ||
t.Errorf("expected %s, got %s", errMock, err) | ||
} | ||
} |
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
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.