Skip to content

Commit

Permalink
Support --tags for YAML tag files.
Browse files Browse the repository at this point in the history
  • Loading branch information
pda committed Aug 28, 2018
1 parent a22b976 commit f1a4ab2
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ PACKAGE = github.com/cultureamp/cfparams
VERSION = $(shell git describe --tags --candidates=1 --dirty)
FLAGS=-X main.Version=$(VERSION) -s -w

cfparams: main.go parameters.go template.go parameterstore/store.go
cfparams: main.go parameters.go tags.go template.go parameterstore/store.go
go build -ldflags="$(FLAGS)"

.PHONY: install
Expand Down
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,44 @@ Resulting JSON:
{"ParameterKey": "Cluster", "ParameterValue": "staging"}
]
```

### Bonus feature: Stack Tags

CloudFormation stacks can be tagged, and those tags flow into all taggable
resources the stack creates. As with `--parameters`, the `aws cloudformation`
commands expect these in an awkward format. `cfparams --tags file.yaml` helps.

```sh
aws cloudformation create-stack \
... \
--tags "$(cfparams --tags=tags-production.yaml)" \
...
```

```yaml
# tags-production.yaml
Name: Widgets as a Service
asset: widget-api
workload: production
```
```sh
cfparams --tags=tags-production.yaml
```

```json
[
{
"Key": "Name",
"Value": "Widgets as a Service"
},
{
"Key": "asset",
"Value": "widget-api"
},
{
"Key": "workload",
"Value": "production"
}
]
```
3 changes: 3 additions & 0 deletions example/tags-production.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Name: Widgets as a Service
asset: widget-api
workload: production
23 changes: 19 additions & 4 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var Version = "dev"

type Input struct {
TemplateBody []byte
TagsBody []byte
AcceptDefaults bool
NoPrevious bool
ParametersCLI []string
Expand All @@ -21,7 +22,7 @@ type Input struct {

func main() {
input := &Input{}
var tplFile, paramFile string
var tplFile, paramFile, tagFile string

flag.Usage = func() {
fmt.Fprintf(os.Stderr, "cfparams %s\n\n", Version)
Expand All @@ -31,19 +32,29 @@ func main() {
}
flag.StringVar(&tplFile, "template", "", "CloudFormation YAML template path")
flag.StringVar(&paramFile, "parameters", "", "Parameters YAML file")
flag.StringVar(&tagFile, "tags", "", "Tags YAML file")
flag.BoolVar(&input.AcceptDefaults, "accept-defaults", false, "Accept defaults from CloudFormation template, omit from JSON")
flag.BoolVar(&input.NoPrevious, "no-previous", false, "Disable UsePreviousValue, fail if a parameter has no default and is not specified")
flag.Parse()

if tplFile != "" {
if tagFile != "" {
data, err := ioutil.ReadFile(tagFile)
if err != nil {
fmt.Fprintf(os.Stderr, "cannot read tags file: %s\n", tplFile)
os.Exit(1)
}
input.TagsBody = data
} else if tplFile != "" {
data, err := ioutil.ReadFile(tplFile)
if err != nil {
fmt.Fprintf(os.Stderr, "cannot read CloudFormation template: %s\n", tplFile)
os.Exit(1)
}
input.TemplateBody = data
} else {
fmt.Fprintf(os.Stderr, "CloudFormation template required, e.g: --template=cfn.yaml\n")
fmt.Fprintf(os.Stderr, "CloudFormation template or tags file required\n")
fmt.Fprintf(os.Stderr, " e.g: --template=cloudformation.yaml\n")
fmt.Fprintf(os.Stderr, " e.g: --tags=tags-foo.yaml\n")
os.Exit(1)
}

Expand All @@ -70,5 +81,9 @@ func main() {
}

func getJsonForInput(input *Input) ([]byte, error) {
return getJsonForInputParams(input) // parameters.go
if len(input.TagsBody) > 0 {
return getJsonForInputTags(input) // tags.go
} else {
return getJsonForInputParams(input) // parameters.go
}
}
18 changes: 18 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,24 @@ func TestCustomYamlTags(t *testing.T) {
require.Equal(t, false, item.UsePreviousValue)
}

func TestTags(t *testing.T) {
input := &Input{
TagsBody: []byte("Foo: 1\nBar: two\n"),
}
j, err := getJsonForInput(input)
require.NoError(t, err)
type tagItem struct {
Key string
Value string
}
var items []tagItem
err = json.Unmarshal(j, &items)
require.ElementsMatch(t, []tagItem{
{Key: "Foo", Value: "1"},
{Key: "Bar", Value: "two"},
}, items)
}

func mustGetJson(t *testing.T, input *Input) string {
j, err := getJsonForInput(input)
require.NoError(t, err)
Expand Down
25 changes: 25 additions & 0 deletions tags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package main

import (
"encoding/json"

yaml "github.com/sanathkr/go-yaml"
)

type jsonItem struct {
Key string
Value string
}

func getJsonForInputTags(input *Input) ([]byte, error) {
data := map[string]string{}
yaml.Unmarshal(input.TagsBody, &data)
items := []jsonItem{}
for key, value := range data {
items = append(items, jsonItem{Key: key, Value: value})
}
for key, value := range input.Parameters {
items = append(items, jsonItem{Key: key, Value: value})
}
return json.MarshalIndent(items, "", " ")
}

0 comments on commit f1a4ab2

Please sign in to comment.