Skip to content

Commit

Permalink
internal/keyvaluetags: Create {SERVICE}GetTag generator, support EC2 …
Browse files Browse the repository at this point in the history
…list/get, use in aws_ec2_tag implementation

Reference: #9061

The GetTag generator simplifies the creation of the new individual service tag resources into a consistent implementation. This consistent implementation can be used to automatically generate the service tag resources themselves in the future.

Output from acceptance testing:

```
--- PASS: TestAccAWSEc2Tag_basic (485.52s)
--- PASS: TestAccAWSEc2Tag_disappears (529.67s)
--- PASS: TestAccAWSEc2Tag_Value (588.49s)
```
  • Loading branch information
bflad committed Jun 13, 2020
1 parent 990fefe commit 50f0f6e
Show file tree
Hide file tree
Showing 12 changed files with 458 additions and 97 deletions.
2 changes: 2 additions & 0 deletions aws/internal/keyvaluetags/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Any tagging functions that cannot be generated should be hand implemented in a s
```text
aws/internal/keyvaluetags
├── generators
│ ├── createtags (generates create_tags_gen.go)
│ ├── gettag (generates get_tag_gen.go)
│ ├── listtags (generates list_tags_gen.go)
│ ├── servicetags (generates service_tags_gen.go)
│ └── updatetags (generates update_tags_gen.go)
Expand Down
133 changes: 133 additions & 0 deletions aws/internal/keyvaluetags/generators/gettag/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// +build ignore

package main

import (
"bytes"
"go/format"
"log"
"os"
"sort"
"strings"
"text/template"

"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
)

const filename = `get_tag_gen.go`

var serviceNames = []string{
"dynamodb",
"ec2",
"ecs",
"route53resolver",
}

type TemplateData struct {
ServiceNames []string
}

func main() {
// Always sort to reduce any potential generation churn
sort.Strings(serviceNames)

templateData := TemplateData{
ServiceNames: serviceNames,
}
templateFuncMap := template.FuncMap{
"ClientType": keyvaluetags.ServiceClientType,
"ListTagsFunction": keyvaluetags.ServiceListTagsFunction,
"ListTagsInputFilterIdentifierName": keyvaluetags.ServiceListTagsInputFilterIdentifierName,
"ListTagsInputResourceTypeField": keyvaluetags.ServiceListTagsInputResourceTypeField,
"ListTagsOutputTagsField": keyvaluetags.ServiceListTagsOutputTagsField,
"TagPackage": keyvaluetags.ServiceTagPackage,
"Title": strings.Title,
}

tmpl, err := template.New("gettag").Funcs(templateFuncMap).Parse(templateBody)

if err != nil {
log.Fatalf("error parsing template: %s", err)
}

var buffer bytes.Buffer
err = tmpl.Execute(&buffer, templateData)

if err != nil {
log.Fatalf("error executing template: %s", err)
}

generatedFileContents, err := format.Source(buffer.Bytes())

if err != nil {
log.Fatalf("error formatting generated file: %s", err)
}

f, err := os.Create(filename)

if err != nil {
log.Fatalf("error creating file (%s): %s", filename, err)
}

defer f.Close()

_, err = f.Write(generatedFileContents)

if err != nil {
log.Fatalf("error writing to file (%s): %s", filename, err)
}
}

var templateBody = `
// Code generated by generators/gettag/main.go; DO NOT EDIT.
package keyvaluetags
import (
"github.com/aws/aws-sdk-go/aws"
{{- range .ServiceNames }}
"github.com/aws/aws-sdk-go/service/{{ . }}"
{{- end }}
)
{{- range .ServiceNames }}
// {{ . | Title }}GetTag fetches an individual {{ . }} service tag for a resource.
// Returns whether the key exists, the key value, and any errors.
// This function will optimise the handling over {{ . | Title }}ListTags, if possible.
// The identifier is typically the Amazon Resource Name (ARN), although
// it may also be a different identifier depending on the service.
func {{ . | Title }}GetTag(conn {{ . | ClientType }}, identifier string{{ if . | ListTagsInputResourceTypeField }}, resourceType string{{ end }}, key string) (bool, *string, error) {
{{- if . | ListTagsInputFilterIdentifierName }}
input := &{{ . | TagPackage }}.{{ . | ListTagsFunction }}Input{
Filters: []*{{ . | TagPackage }}.Filter{
{
Name: aws.String("{{ . | ListTagsInputFilterIdentifierName }}"),
Values: []*string{aws.String(identifier)},
},
{
Name: aws.String("key"),
Values: []*string{aws.String(key)},
},
},
}
output, err := conn.{{ . | ListTagsFunction }}(input)
if err != nil {
return false, nil, err
}
listTags := {{ . | Title }}KeyValueTags(output.{{ . | ListTagsOutputTagsField }})
{{- else }}
listTags, err := {{ . | Title }}ListTags(conn, identifier{{ if . | ListTagsInputResourceTypeField }}, resourceType{{ end }})
if err != nil {
return false, nil, err
}
{{- end }}
return listTags.KeyExists(key), listTags.KeyValue(key), nil
}
{{- end }}
`
11 changes: 11 additions & 0 deletions aws/internal/keyvaluetags/generators/listtags/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ var serviceNames = []string{
"dlm",
"docdb",
"dynamodb",
"ec2",
"ecr",
"ecs",
"efs",
Expand Down Expand Up @@ -129,6 +130,7 @@ func main() {
templateFuncMap := template.FuncMap{
"ClientType": keyvaluetags.ServiceClientType,
"ListTagsFunction": keyvaluetags.ServiceListTagsFunction,
"ListTagsInputFilterIdentifierName": keyvaluetags.ServiceListTagsInputFilterIdentifierName,
"ListTagsInputIdentifierField": keyvaluetags.ServiceListTagsInputIdentifierField,
"ListTagsInputIdentifierRequiresSlice": keyvaluetags.ServiceListTagsInputIdentifierRequiresSlice,
"ListTagsInputResourceTypeField": keyvaluetags.ServiceListTagsInputResourceTypeField,
Expand Down Expand Up @@ -189,6 +191,14 @@ import (
// it may also be a different identifier depending on the service.
func {{ . | Title }}ListTags(conn {{ . | ClientType }}, identifier string{{ if . | ListTagsInputResourceTypeField }}, resourceType string{{ end }}) (KeyValueTags, error) {
input := &{{ . | TagPackage }}.{{ . | ListTagsFunction }}Input{
{{- if . | ListTagsInputFilterIdentifierName }}
Filters: []*{{ . | TagPackage }}.Filter{
{
Name: aws.String("{{ . | ListTagsInputFilterIdentifierName }}"),
Values: []*string{aws.String(identifier)},
},
},
{{- else }}
{{- if . | ListTagsInputIdentifierRequiresSlice }}
{{ . | ListTagsInputIdentifierField }}: aws.StringSlice([]string{identifier}),
{{- else }}
Expand All @@ -197,6 +207,7 @@ func {{ . | Title }}ListTags(conn {{ . | ClientType }}, identifier string{{ if .
{{- if . | ListTagsInputResourceTypeField }}
{{ . | ListTagsInputResourceTypeField }}: aws.String(resourceType),
{{- end }}
{{- end }}
}
output, err := conn.{{ . | ListTagsFunction }}(input)
Expand Down
27 changes: 27 additions & 0 deletions aws/internal/keyvaluetags/generators/servicetags/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ func main() {
"TagKeyType": keyvaluetags.ServiceTagKeyType,
"TagPackage": keyvaluetags.ServiceTagPackage,
"TagType": keyvaluetags.ServiceTagType,
"TagType2": keyvaluetags.ServiceTagType2,
"TagTypeKeyField": keyvaluetags.ServiceTagTypeKeyField,
"TagTypeValueField": keyvaluetags.ServiceTagTypeValueField,
"Title": strings.Title,
Expand Down Expand Up @@ -259,6 +260,31 @@ func (tags KeyValueTags) {{ . | Title }}Tags() []*{{ . | TagPackage }}.{{ . | Ta
}
// {{ . | Title }}KeyValueTags creates KeyValueTags from {{ . }} service tags.
{{- if . | TagType2 }}
// Accepts []*{{ . | TagPackage }}.{{ . | TagType }} and []*{{ . | TagPackage }}.{{ . | TagType2 }}.
func {{ . | Title }}KeyValueTags(tags interface{}) KeyValueTags {
switch tags := tags.(type) {
case []*{{ . | TagPackage }}.{{ . | TagType }}:
m := make(map[string]*string, len(tags))
for _, tag := range tags {
m[aws.StringValue(tag.{{ . | TagTypeKeyField }})] = tag.{{ . | TagTypeValueField }}
}
return New(m)
case []*{{ . | TagPackage }}.{{ . | TagType2 }}:
m := make(map[string]*string, len(tags))
for _, tag := range tags {
m[aws.StringValue(tag.{{ . | TagTypeKeyField }})] = tag.{{ . | TagTypeValueField }}
}
return New(m)
default:
return New(nil)
}
}
{{- else }}
func {{ . | Title }}KeyValueTags(tags []*{{ . | TagPackage }}.{{ . | TagType }}) KeyValueTags {
m := make(map[string]*string, len(tags))
Expand All @@ -269,4 +295,5 @@ func {{ . | Title }}KeyValueTags(tags []*{{ . | TagPackage }}.{{ . | TagType }})
return New(m)
}
{{- end }}
{{- end }}
`
86 changes: 86 additions & 0 deletions aws/internal/keyvaluetags/get_tag_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions aws/internal/keyvaluetags/key_value_tags.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//go:generate go run -tags generate generators/servicetags/main.go
//go:generate go run -tags generate generators/listtags/main.go
//go:generate go run -tags generate generators/gettag/main.go
//go:generate go run -tags generate generators/createtags/main.go
//go:generate go run -tags generate generators/updatetags/main.go

Expand Down Expand Up @@ -138,6 +139,28 @@ func (tags KeyValueTags) Ignore(ignoreTags KeyValueTags) KeyValueTags {
return result
}

// KeyExists returns true if a tag key exists.
// If the key is not found, returns nil.
// Use KeyExists to determine if key is present.
func (tags KeyValueTags) KeyExists(key string) bool {
if _, ok := tags[key]; ok {
return true
}

return false
}

// KeyValue returns a tag key value.
// If the key is not found, returns nil.
// Use KeyExists to determine if key is present.
func (tags KeyValueTags) KeyValue(key string) *string {
if v, ok := tags[key]; ok {
return v
}

return nil
}

// Keys returns tag keys.
func (tags KeyValueTags) Keys() []string {
result := make([]string, 0, len(tags))
Expand Down
Loading

0 comments on commit 50f0f6e

Please sign in to comment.