Skip to content

Commit 5c19c50

Browse files
author
Martin Linkhorst
committed
ref: change tags parameter from json parsing to string map
1 parent 7246dfa commit 5c19c50

3 files changed

Lines changed: 57 additions & 68 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ Wait until the operator discovered and executed the change, then look at your AW
142142
## Tags
143143

144144
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:
145-
- `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"}'
145+
- `--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"`)
146146
- `tags` parameter at kubernetes resource spec:
147147
```yaml
148148
apiVersion: cloudformation.linki.space/v1alpha1
@@ -164,7 +164,7 @@ spec:
164164
Status: Enabled
165165
```
166166

167-
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.
167+
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.
168168

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

@@ -291,7 +291,7 @@ Argument | Environment variable | Default value | Description
291291
---------|----------------------|---------------|------------
292292
debug | DEBUG | | Enable debug logging.
293293
dry-run | DRY_RUN | | If true, don't actually do anything.
294-
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.
294+
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"`)
295295
namespace | WATCH_NAMESPACE | default | The Kubernetes namespace to watch
296296
region | AWS_REGION | | The AWS region to use
297297

cmd/cloudformation-operator/main.go

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package main
22

33
import (
44
"context"
5-
"encoding/json"
65
"runtime"
76

87
"github.com/alecthomas/kingpin"
@@ -18,25 +17,18 @@ import (
1817
)
1918

2019
var (
21-
namespace string
22-
region string
23-
globalTags string
24-
dryRun bool
25-
debug bool
26-
version = "0.2.0+git"
20+
namespace string
21+
region string
22+
tags = map[string]string{}
23+
dryRun bool
24+
debug bool
25+
version = "0.3.0+git"
2726
)
2827

29-
type Tags map[string]string
30-
3128
func init() {
3229
kingpin.Flag("namespace", "The Kubernetes namespace to watch").Default("default").Envar("WATCH_NAMESPACE").StringVar(&namespace)
3330
kingpin.Flag("region", "The AWS region to use").Envar("AWS_REGION").StringVar(&region)
34-
kingpin.Flag(
35-
"global-tags",
36-
"Global tags which should be applied for all stacks." +
37-
" Current parameter accepts JSON format where every key-value pair defines a tag." +
38-
" Key is a tag name and value is a tag value.",
39-
).Default("{}").Envar("GLOBAL_TAGS").StringVar(&globalTags)
31+
kingpin.Flag("tag", "Tags to apply to all Stacks by default. Specify multiple times for multiple tags.").Envar("AWS_TAGS").StringMapVar(&tags)
4032
kingpin.Flag("dry-run", "If true, don't actually do anything.").Envar("DRY_RUN").BoolVar(&dryRun)
4133
kingpin.Flag("debug", "Enable debug logging.").Envar("DEBUG").BoolVar(&debug)
4234
}
@@ -48,15 +40,6 @@ func printVersion() {
4840
logrus.Infof("cloudformation-operator Version: %v", version)
4941
}
5042

51-
func parseTags() map[string]string {
52-
var globalTagsParsed map[string]string
53-
err := json.Unmarshal([]byte(globalTags), &globalTagsParsed)
54-
if err != nil {
55-
logrus.Error("Failed to parse global tags: ", err)
56-
}
57-
return globalTagsParsed
58-
}
59-
6043
func main() {
6144
kingpin.Version(version)
6245
kingpin.Parse()
@@ -76,6 +59,6 @@ func main() {
7659
})
7760

7861
sdk.Watch("cloudformation.linki.space/v1alpha1", "Stack", namespace, 0)
79-
sdk.Handle(stub.NewHandler(client, parseTags(), dryRun))
62+
sdk.Handle(stub.NewHandler(client, tags, dryRun))
8063
sdk.Run(context.TODO())
8164
}

pkg/stub/handler.go

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ var (
3030

3131
type Handler struct {
3232
client cloudformationiface.CloudFormationAPI
33-
globalTags map[string]string
33+
defautTags map[string]string
3434
dryRun bool
3535
}
3636

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

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

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

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

169-
func (h *Handler) processStackParams(stack *v1alpha1.Stack) ([]*cloudformation.Parameter) {
170-
params := []*cloudformation.Parameter{}
171-
for k, v := range stack.Spec.Parameters {
172-
params = append(params, &cloudformation.Parameter{
173-
ParameterKey: aws.String(k),
174-
ParameterValue: aws.String(v),
175-
})
176-
}
177-
return params
178-
}
179-
180-
func (h *Handler) processStackTags(stack *v1alpha1.Stack) ([]*cloudformation.Tag) {
181-
tags := []*cloudformation.Tag{
182-
{
183-
Key: aws.String(ownerTagKey),
184-
Value: aws.String(ownerTagValue),
185-
},
186-
}
187-
for k, v := range h.globalTags {
188-
tags = append(tags, &cloudformation.Tag{
189-
Key: aws.String(k),
190-
Value: aws.String(v),
191-
})
192-
}
193-
for k, v := range stack.Spec.Tags {
194-
tags = append(tags, &cloudformation.Tag{
195-
Key: aws.String(k),
196-
Value: aws.String(v),
197-
})
198-
}
199-
return tags
200-
}
201-
202169
func (h *Handler) stackExists(stack *v1alpha1.Stack) (bool, error) {
203170
_, err := h.getStack(stack)
204171
if err != nil {
@@ -282,3 +249,42 @@ func (h *Handler) waitWhile(stack *v1alpha1.Stack, status string) error {
282249
return nil
283250
}
284251
}
252+
253+
// stackParameters converts the parameters field on a Stack resource to CloudFormation Parameters.
254+
func stackParameters(stack *v1alpha1.Stack) []*cloudformation.Parameter {
255+
params := []*cloudformation.Parameter{}
256+
for k, v := range stack.Spec.Parameters {
257+
params = append(params, &cloudformation.Parameter{
258+
ParameterKey: aws.String(k),
259+
ParameterValue: aws.String(v),
260+
})
261+
}
262+
return params
263+
}
264+
265+
// stackTags converts the tags field on a Stack resource to CloudFormation Tags.
266+
// Furthermore, it adds a tag for marking ownership as well as any tags given by defaultTags.
267+
func stackTags(stack *v1alpha1.Stack, defaultTags map[string]string) []*cloudformation.Tag {
268+
// ownership tag
269+
tags := []*cloudformation.Tag{
270+
{
271+
Key: aws.String(ownerTagKey),
272+
Value: aws.String(ownerTagValue),
273+
},
274+
}
275+
// default tags
276+
for k, v := range defaultTags {
277+
tags = append(tags, &cloudformation.Tag{
278+
Key: aws.String(k),
279+
Value: aws.String(v),
280+
})
281+
}
282+
// tags specified on the Stack resource
283+
for k, v := range stack.Spec.Tags {
284+
tags = append(tags, &cloudformation.Tag{
285+
Key: aws.String(k),
286+
Value: aws.String(v),
287+
})
288+
}
289+
return tags
290+
}

0 commit comments

Comments
 (0)