Skip to content

Commit

Permalink
ref: change tags parameter from json parsing to string map
Browse files Browse the repository at this point in the history
  • Loading branch information
linki committed Nov 2, 2018
1 parent 7246dfa commit 5c19c50
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 68 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ Wait until the operator discovered and executed the change, then look at your AW
## Tags

You may want to assign tags to your CloudFormation stacks. The tags added to a CloudFormation stack will be propagated to the managed resources. This feature may be useful in multiple cases, for example, to distinguish resources at billing report. Current operator provides two ways to assign tags:
- `global-tags` command line argument or `GLOBAL_TAGS` environment variable which allows setting global tags for all resources managed by the operator. This option accepts JSON format where every key is a tag name and value is a tag value. For example '{"foo": "fooValue", "bar": "barValue"}'
- `--tag` command line argument or `AWS_TAGS` environment variable which allows setting default tags for all resources managed by the operator. The format is `--tag=foo=bar --tag=wambo=baz` on the command line or with a line break when specifying as an env var. (e.g. in zsh: `AWS_TAGS="foo=bar"$'\n'"wambo=baz"`)
- `tags` parameter at kubernetes resource spec:
```yaml
apiVersion: cloudformation.linki.space/v1alpha1
Expand All @@ -164,7 +164,7 @@ spec:
Status: Enabled
```

Resource-specific tags have precedence over the global tags. Thus if a tag is defined at command-line arguments and for a `Stack` resource, the value from the `Stack` resource will be used.
Resource-specific tags have precedence over the default tags. Thus if a tag is defined at command-line arguments and for a `Stack` resource, the value from the `Stack` resource will be used.

If we run the operation and a `Stack` resource with the described above examples, we'll see such picture:

Expand Down Expand Up @@ -291,7 +291,7 @@ Argument | Environment variable | Default value | Description
---------|----------------------|---------------|------------
debug | DEBUG | | Enable debug logging.
dry-run | DRY_RUN | | If true, don't actually do anything.
global-tags | GLOBAL_TAGS | {} | Global tags which should be applied for all stacks. Current parameter accepts JSON format where every key-value pair defines a tag. Key is a tag name and value is a tag value.
tag ... | AWS_TAGS | | Default tags which should be applied for all stacks. The format is `--tag=foo=bar --tag=wambo=baz` on the command line or with a line break when specifying as an env var. (e.g. in zsh: `AWS_TAGS="foo=bar"$'\n'"wambo=baz"`)
namespace | WATCH_NAMESPACE | default | The Kubernetes namespace to watch
region | AWS_REGION | | The AWS region to use

Expand Down
33 changes: 8 additions & 25 deletions cmd/cloudformation-operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package main

import (
"context"
"encoding/json"
"runtime"

"github.com/alecthomas/kingpin"
Expand All @@ -18,25 +17,18 @@ import (
)

var (
namespace string
region string
globalTags string
dryRun bool
debug bool
version = "0.2.0+git"
namespace string
region string
tags = map[string]string{}
dryRun bool
debug bool
version = "0.3.0+git"
)

type Tags map[string]string

func init() {
kingpin.Flag("namespace", "The Kubernetes namespace to watch").Default("default").Envar("WATCH_NAMESPACE").StringVar(&namespace)
kingpin.Flag("region", "The AWS region to use").Envar("AWS_REGION").StringVar(&region)
kingpin.Flag(
"global-tags",
"Global tags which should be applied for all stacks." +
" Current parameter accepts JSON format where every key-value pair defines a tag." +
" Key is a tag name and value is a tag value.",
).Default("{}").Envar("GLOBAL_TAGS").StringVar(&globalTags)
kingpin.Flag("tag", "Tags to apply to all Stacks by default. Specify multiple times for multiple tags.").Envar("AWS_TAGS").StringMapVar(&tags)
kingpin.Flag("dry-run", "If true, don't actually do anything.").Envar("DRY_RUN").BoolVar(&dryRun)
kingpin.Flag("debug", "Enable debug logging.").Envar("DEBUG").BoolVar(&debug)
}
Expand All @@ -48,15 +40,6 @@ func printVersion() {
logrus.Infof("cloudformation-operator Version: %v", version)
}

func parseTags() map[string]string {
var globalTagsParsed map[string]string
err := json.Unmarshal([]byte(globalTags), &globalTagsParsed)
if err != nil {
logrus.Error("Failed to parse global tags: ", err)
}
return globalTagsParsed
}

func main() {
kingpin.Version(version)
kingpin.Parse()
Expand All @@ -76,6 +59,6 @@ func main() {
})

sdk.Watch("cloudformation.linki.space/v1alpha1", "Stack", namespace, 0)
sdk.Handle(stub.NewHandler(client, parseTags(), dryRun))
sdk.Handle(stub.NewHandler(client, tags, dryRun))
sdk.Run(context.TODO())
}
86 changes: 46 additions & 40 deletions pkg/stub/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ var (

type Handler struct {
client cloudformationiface.CloudFormationAPI
globalTags map[string]string
defautTags map[string]string
dryRun bool
}

func NewHandler(client cloudformationiface.CloudFormationAPI, globalTags map[string]string, dryRun bool) handler.Handler {
return &Handler{client: client, globalTags: globalTags, dryRun: dryRun}
func NewHandler(client cloudformationiface.CloudFormationAPI, defautTags map[string]string, dryRun bool) handler.Handler {
return &Handler{client: client, defautTags: defautTags, dryRun: dryRun}
}

func (h *Handler) Handle(ctx types.Context, event types.Event) error {
Expand Down Expand Up @@ -85,8 +85,8 @@ func (h *Handler) createStack(stack *v1alpha1.Stack) error {
input := &cloudformation.CreateStackInput{
StackName: aws.String(stack.Name),
TemplateBody: aws.String(stack.Spec.Template),
Parameters: h.processStackParams(stack),
Tags: h.processStackTags(stack),
Parameters: stackParameters(stack),
Tags: stackTags(stack, h.defautTags),
}

if _, err := h.client.CreateStack(input); err != nil {
Expand All @@ -111,8 +111,8 @@ func (h *Handler) updateStack(stack *v1alpha1.Stack) error {
input := &cloudformation.UpdateStackInput{
StackName: aws.String(stack.Name),
TemplateBody: aws.String(stack.Spec.Template),
Parameters: h.processStackParams(stack),
Tags: h.processStackTags(stack),
Parameters: stackParameters(stack),
Tags: stackTags(stack, h.defautTags),
}

if _, err := h.client.UpdateStack(input); err != nil {
Expand Down Expand Up @@ -166,39 +166,6 @@ func (h *Handler) getStack(stack *v1alpha1.Stack) (*cloudformation.Stack, error)
return resp.Stacks[0], nil
}

func (h *Handler) processStackParams(stack *v1alpha1.Stack) ([]*cloudformation.Parameter) {
params := []*cloudformation.Parameter{}
for k, v := range stack.Spec.Parameters {
params = append(params, &cloudformation.Parameter{
ParameterKey: aws.String(k),
ParameterValue: aws.String(v),
})
}
return params
}

func (h *Handler) processStackTags(stack *v1alpha1.Stack) ([]*cloudformation.Tag) {
tags := []*cloudformation.Tag{
{
Key: aws.String(ownerTagKey),
Value: aws.String(ownerTagValue),
},
}
for k, v := range h.globalTags {
tags = append(tags, &cloudformation.Tag{
Key: aws.String(k),
Value: aws.String(v),
})
}
for k, v := range stack.Spec.Tags {
tags = append(tags, &cloudformation.Tag{
Key: aws.String(k),
Value: aws.String(v),
})
}
return tags
}

func (h *Handler) stackExists(stack *v1alpha1.Stack) (bool, error) {
_, err := h.getStack(stack)
if err != nil {
Expand Down Expand Up @@ -282,3 +249,42 @@ func (h *Handler) waitWhile(stack *v1alpha1.Stack, status string) error {
return nil
}
}

// stackParameters converts the parameters field on a Stack resource to CloudFormation Parameters.
func stackParameters(stack *v1alpha1.Stack) []*cloudformation.Parameter {
params := []*cloudformation.Parameter{}
for k, v := range stack.Spec.Parameters {
params = append(params, &cloudformation.Parameter{
ParameterKey: aws.String(k),
ParameterValue: aws.String(v),
})
}
return params
}

// stackTags converts the tags field on a Stack resource to CloudFormation Tags.
// Furthermore, it adds a tag for marking ownership as well as any tags given by defaultTags.
func stackTags(stack *v1alpha1.Stack, defaultTags map[string]string) []*cloudformation.Tag {
// ownership tag
tags := []*cloudformation.Tag{
{
Key: aws.String(ownerTagKey),
Value: aws.String(ownerTagValue),
},
}
// default tags
for k, v := range defaultTags {
tags = append(tags, &cloudformation.Tag{
Key: aws.String(k),
Value: aws.String(v),
})
}
// tags specified on the Stack resource
for k, v := range stack.Spec.Tags {
tags = append(tags, &cloudformation.Tag{
Key: aws.String(k),
Value: aws.String(v),
})
}
return tags
}

0 comments on commit 5c19c50

Please sign in to comment.