Skip to content

Commit

Permalink
Merge pull request #32371 from hashicorp/b-kms-key-remove-tags
Browse files Browse the repository at this point in the history
KMS Keys: Fixes timeout when removing all tags
  • Loading branch information
gdavison authored Jul 5, 2023
2 parents 6f44d71 + 378a9bc commit e3a40d0
Show file tree
Hide file tree
Showing 21 changed files with 396 additions and 110 deletions.
15 changes: 15 additions & 0 deletions .changelog/32371.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
```release-note:bug
resource/aws_kms_external_key: Correctly remove all tags
```

```release-note:bug
resource/aws_kms_key: Correctly remove all tags
```

```release-note:bug
resource/aws_kms_replica_external_key: Correctly remove all tags
```

```release-note:bug
resource/aws_kms_replica_key: Correctly remove all tags
```
143 changes: 102 additions & 41 deletions internal/generate/tags/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"os"
"regexp"
"strings"
"time"

"github.com/hashicorp/terraform-provider-aws/internal/generate/common"
v1 "github.com/hashicorp/terraform-provider-aws/internal/generate/tags/templates/v1"
Expand All @@ -25,8 +26,9 @@ const (
)

const (
defaultListTagsFunc = "listTags"
defaultUpdateTagsFunc = "updateTags"
defaultListTagsFunc = "listTags"
defaultUpdateTagsFunc = "updateTags"
defaultWaitTagsPropagatedFunc = "waitTagsPropagated"
)

var (
Expand All @@ -38,38 +40,45 @@ var (
untagInNeedTagType = flag.Bool("UntagInNeedTagType", false, "whether Untag input needs tag type")
updateTags = flag.Bool("UpdateTags", false, "whether to generate UpdateTags")
updateTagsNoIgnoreSystem = flag.Bool("UpdateTagsNoIgnoreSystem", false, "whether to not ignore system tags in UpdateTags")

createTagsFunc = flag.String("CreateTagsFunc", "createTags", "createTagsFunc")
getTagFunc = flag.String("GetTagFunc", "GetTag", "getTagFunc")
getTagsInFunc = flag.String("GetTagsInFunc", "getTagsIn", "getTagsInFunc")
keyValueTagsFunc = flag.String("KeyValueTagsFunc", "KeyValueTags", "keyValueTagsFunc")
listTagsFunc = flag.String("ListTagsFunc", defaultListTagsFunc, "listTagsFunc")
listTagsInFiltIDName = flag.String("ListTagsInFiltIDName", "", "listTagsInFiltIDName")
listTagsInIDElem = flag.String("ListTagsInIDElem", "ResourceArn", "listTagsInIDElem")
listTagsInIDNeedSlice = flag.String("ListTagsInIDNeedSlice", "", "listTagsInIDNeedSlice")
listTagsOp = flag.String("ListTagsOp", "ListTagsForResource", "listTagsOp")
listTagsOutTagsElem = flag.String("ListTagsOutTagsElem", "Tags", "listTagsOutTagsElem")
setTagsOutFunc = flag.String("SetTagsOutFunc", "setTagsOut", "setTagsOutFunc")
tagInCustomVal = flag.String("TagInCustomVal", "", "tagInCustomVal")
tagInIDElem = flag.String("TagInIDElem", "ResourceArn", "tagInIDElem")
tagInIDNeedSlice = flag.String("TagInIDNeedSlice", "", "tagInIDNeedSlice")
tagInTagsElem = flag.String("TagInTagsElem", "Tags", "tagInTagsElem")
tagKeyType = flag.String("TagKeyType", "", "tagKeyType")
tagOp = flag.String("TagOp", "TagResource", "tagOp")
tagOpBatchSize = flag.String("TagOpBatchSize", "", "tagOpBatchSize")
tagResTypeElem = flag.String("TagResTypeElem", "", "tagResTypeElem")
tagType = flag.String("TagType", "Tag", "tagType")
tagType2 = flag.String("TagType2", "", "tagType")
tagTypeAddBoolElem = flag.String("TagTypeAddBoolElem", "", "TagTypeAddBoolElem")
tagTypeIDElem = flag.String("TagTypeIDElem", "", "tagTypeIDElem")
tagTypeKeyElem = flag.String("TagTypeKeyElem", "Key", "tagTypeKeyElem")
tagTypeValElem = flag.String("TagTypeValElem", "Value", "tagTypeValElem")
tagsFunc = flag.String("TagsFunc", "Tags", "tagsFunc")
untagInCustomVal = flag.String("UntagInCustomVal", "", "untagInCustomVal")
untagInNeedTagKeyType = flag.String("UntagInNeedTagKeyType", "", "untagInNeedTagKeyType")
untagInTagsElem = flag.String("UntagInTagsElem", "TagKeys", "untagInTagsElem")
untagOp = flag.String("UntagOp", "UntagResource", "untagOp")
updateTagsFunc = flag.String("UpdateTagsFunc", defaultUpdateTagsFunc, "updateTagsFunc")
waitForPropagation = flag.Bool("Wait", false, "whether to generate WaitTagsPropagated")

createTagsFunc = flag.String("CreateTagsFunc", "createTags", "createTagsFunc")
getTagFunc = flag.String("GetTagFunc", "GetTag", "getTagFunc")
getTagsInFunc = flag.String("GetTagsInFunc", "getTagsIn", "getTagsInFunc")
keyValueTagsFunc = flag.String("KeyValueTagsFunc", "KeyValueTags", "keyValueTagsFunc")
listTagsFunc = flag.String("ListTagsFunc", defaultListTagsFunc, "listTagsFunc")
listTagsInFiltIDName = flag.String("ListTagsInFiltIDName", "", "listTagsInFiltIDName")
listTagsInIDElem = flag.String("ListTagsInIDElem", "ResourceArn", "listTagsInIDElem")
listTagsInIDNeedSlice = flag.String("ListTagsInIDNeedSlice", "", "listTagsInIDNeedSlice")
listTagsOp = flag.String("ListTagsOp", "ListTagsForResource", "listTagsOp")
listTagsOutTagsElem = flag.String("ListTagsOutTagsElem", "Tags", "listTagsOutTagsElem")
setTagsOutFunc = flag.String("SetTagsOutFunc", "setTagsOut", "setTagsOutFunc")
tagInCustomVal = flag.String("TagInCustomVal", "", "tagInCustomVal")
tagInIDElem = flag.String("TagInIDElem", "ResourceArn", "tagInIDElem")
tagInIDNeedSlice = flag.String("TagInIDNeedSlice", "", "tagInIDNeedSlice")
tagInTagsElem = flag.String("TagInTagsElem", "Tags", "tagInTagsElem")
tagKeyType = flag.String("TagKeyType", "", "tagKeyType")
tagOp = flag.String("TagOp", "TagResource", "tagOp")
tagOpBatchSize = flag.String("TagOpBatchSize", "", "tagOpBatchSize")
tagResTypeElem = flag.String("TagResTypeElem", "", "tagResTypeElem")
tagType = flag.String("TagType", "Tag", "tagType")
tagType2 = flag.String("TagType2", "", "tagType")
tagTypeAddBoolElem = flag.String("TagTypeAddBoolElem", "", "TagTypeAddBoolElem")
tagTypeIDElem = flag.String("TagTypeIDElem", "", "tagTypeIDElem")
tagTypeKeyElem = flag.String("TagTypeKeyElem", "Key", "tagTypeKeyElem")
tagTypeValElem = flag.String("TagTypeValElem", "Value", "tagTypeValElem")
tagsFunc = flag.String("TagsFunc", "Tags", "tagsFunc")
untagInCustomVal = flag.String("UntagInCustomVal", "", "untagInCustomVal")
untagInNeedTagKeyType = flag.String("UntagInNeedTagKeyType", "", "untagInNeedTagKeyType")
untagInTagsElem = flag.String("UntagInTagsElem", "TagKeys", "untagInTagsElem")
untagOp = flag.String("UntagOp", "UntagResource", "untagOp")
updateTagsFunc = flag.String("UpdateTagsFunc", defaultUpdateTagsFunc, "updateTagsFunc")
waitTagsPropagatedFunc = flag.String("WaitFunc", defaultWaitTagsPropagatedFunc, "waitFunc")
waitContinuousOccurence = flag.Int("WaitContinuousOccurence", 0, "ContinuousTargetOccurence for Wait function")
waitDelay = flag.Duration("WaitDelay", 0, "Delay for Wait function")
waitMinTimeout = flag.Duration("WaitMinTimeout", 0, `"MinTimeout" (minimum poll interval) for Wait function`)
waitPollInterval = flag.Duration("WaitPollInterval", 0, "PollInterval for Wait function")
waitTimeout = flag.Duration("WaitTimeout", 0, "Timeout for Wait function")

parentNotFoundErrCode = flag.String("ParentNotFoundErrCode", "", "Parent 'NotFound' Error Code")
parentNotFoundErrMsg = flag.String("ParentNotFoundErrMsg", "", "Parent 'NotFound' Error Message")
Expand All @@ -90,12 +99,13 @@ func usage() {
}

type TemplateBody struct {
getTag string
header string
listTags string
serviceTagsMap string
serviceTagsSlice string
updateTags string
getTag string
header string
listTags string
serviceTagsMap string
serviceTagsSlice string
updateTags string
waitTagsPropagated string
}

func newTemplateBody(version int, kvtValues bool) *TemplateBody {
Expand All @@ -108,6 +118,7 @@ func newTemplateBody(version int, kvtValues bool) *TemplateBody {
"\n" + v1.ServiceTagsMapBody,
"\n" + v1.ServiceTagsSliceBody,
"\n" + v1.UpdateTagsBody,
"\n" + v1.WaitTagsPropagatedBody,
}
case sdkV2:
if kvtValues {
Expand All @@ -118,6 +129,7 @@ func newTemplateBody(version int, kvtValues bool) *TemplateBody {
"\n" + v2.ServiceTagsValueMapBody,
"\n" + v2.ServiceTagsSliceBody,
"\n" + v2.UpdateTagsBody,
"\n" + v2.WaitTagsPropagatedBody,
}
}
return &TemplateBody{
Expand All @@ -127,6 +139,7 @@ func newTemplateBody(version int, kvtValues bool) *TemplateBody {
"\n" + v2.ServiceTagsMapBody,
"\n" + v2.ServiceTagsSliceBody,
"\n" + v2.UpdateTagsBody,
"\n" + v2.WaitTagsPropagatedBody,
}
default:
return nil
Expand Down Expand Up @@ -178,6 +191,13 @@ type TemplateData struct {
UntagOp string
UpdateTagsFunc string
UpdateTagsIgnoreSystem bool
WaitForPropagation bool
WaitTagsPropagatedFunc string
WaitContinuousOccurence int
WaitDelay string
WaitMinTimeout string
WaitPollInterval string
WaitTimeout string

// The following are specific to writing import paths in the `headerBody`;
// to include the package, set the corresponding field's value to true
Expand All @@ -189,6 +209,7 @@ type TemplateData struct {
SkipServiceImp bool
SkipTypesImp bool
TfResourcePkg bool
TimePkg bool

IsDefaultListTags bool
IsDefaultUpdateTags bool
Expand Down Expand Up @@ -276,7 +297,8 @@ func main() {
NamesPkg: *updateTags && !*skipNamesImp,
SkipServiceImp: *skipServiceImp,
SkipTypesImp: *skipTypesImp,
TfResourcePkg: *getTag,
TfResourcePkg: (*getTag || *waitForPropagation),
TimePkg: *waitForPropagation,

CreateTagsFunc: createTagsFunc,
GetTagFunc: *getTagFunc,
Expand Down Expand Up @@ -315,6 +337,13 @@ func main() {
UntagOp: *untagOp,
UpdateTagsFunc: *updateTagsFunc,
UpdateTagsIgnoreSystem: !*updateTagsNoIgnoreSystem,
WaitForPropagation: *waitForPropagation,
WaitTagsPropagatedFunc: *waitTagsPropagatedFunc,
WaitContinuousOccurence: *waitContinuousOccurence,
WaitDelay: formatDuration(*waitDelay),
WaitMinTimeout: formatDuration(*waitMinTimeout),
WaitPollInterval: formatDuration(*waitPollInterval),
WaitTimeout: formatDuration(*waitTimeout),

IsDefaultListTags: *listTagsFunc == defaultListTagsFunc,
IsDefaultUpdateTags: *updateTagsFunc == defaultUpdateTagsFunc,
Expand Down Expand Up @@ -366,6 +395,12 @@ func main() {
}
}

if *waitForPropagation {
if err := d.WriteTemplate("waittagspropagated", templateBody.waitTagsPropagated, templateData); err != nil {
g.Fatalf("generating file (%s): %s", filename, err)
}
}

if err := d.Write(); err != nil {
g.Fatalf("generating file (%s): %s", filename, err)
}
Expand All @@ -376,3 +411,29 @@ func toSnakeCase(str string) string {
result = regexp.MustCompile("([a-z0-9])([A-Z])").ReplaceAllString(result, "${1}_${2}")
return strings.ToLower(result)
}

func formatDuration(d time.Duration) string {
if d == 0 {
return ""
}

var buf []string
if h := d.Hours(); h >= 1 {
buf = append(buf, fmt.Sprintf("%d * time.Hour", int64(h)))
d = d - time.Duration(int64(h)*int64(time.Hour))
}
if m := d.Minutes(); m >= 1 {
buf = append(buf, fmt.Sprintf("%d * time.Minute", int64(m)))
d = d - time.Duration(int64(m)*int64(time.Minute))
}
if s := d.Seconds(); s >= 1 {
buf = append(buf, fmt.Sprintf("%d * time.Second", int64(s)))
d = d - time.Duration(int64(s)*int64(time.Second))
}
if ms := d.Milliseconds(); ms >= 1 {
buf = append(buf, fmt.Sprintf("%d * time.Millisecond", int64(ms)))
}
// Ignoring anything below milliseconds

return strings.Join(buf, " + ")
}
3 changes: 3 additions & 0 deletions internal/generate/tags/templates/v1/header_body.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import (
{{- if .FmtPkg }}
"fmt"
{{- end }}
{{- if .TimePkg }}
"time"
{{- end }}

"github.com/aws/aws-sdk-go/aws"
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
Expand Down
8 changes: 8 additions & 0 deletions internal/generate/tags/templates/v1/update_tags_body.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,14 @@ func {{ .UpdateTagsFunc }}(ctx context.Context, conn {{ .ClientType }}, identifi

{{- end }}

{{ if .WaitForPropagation }}
if len(removedTags) > 0 || len(updatedTags) > 0 {
if err := {{ .WaitTagsPropagatedFunc }}(ctx, conn, identifier, newTags); err != nil {
return fmt.Errorf("waiting for resource (%s) tag propagation: %w", identifier, err)
}
}
{{- end }}

return nil
}

Expand Down
3 changes: 3 additions & 0 deletions internal/generate/tags/templates/v1/v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ var ServiceTagsSliceBody string

//go:embed update_tags_body.tmpl
var UpdateTagsBody string

//go:embed wait_tags_propagated_body.tmpl
var WaitTagsPropagatedBody string
34 changes: 34 additions & 0 deletions internal/generate/tags/templates/v1/wait_tags_propagated_body.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// {{ .WaitTagsPropagatedFunc }} waits for {{ .ServicePackage }} service tags to be propagated.
// The identifier is typically the Amazon Resource Name (ARN), although
// it may also be a different identifier depending on the service.
func {{ .WaitTagsPropagatedFunc }}(ctx context.Context, conn {{ .ClientType }}, id string, tags tftags.KeyValueTags) error {
checkFunc := func() (bool, error) {
output, err := listTags(ctx, conn, id)

if tfresource.NotFound(err) {
return false, nil
}

if err != nil {
return false, err
}

return output.Equal(tags), nil
}
opts := tfresource.WaitOpts{
{{- if ne .WaitContinuousOccurence 0 }}
ContinuousTargetOccurence: {{ .WaitContinuousOccurence }},
{{- end }}
{{- if ne .WaitDelay "" }}
Delay: {{ .WaitDelay }},
{{- end }}
{{- if ne .WaitMinTimeout "" }}
MinTimeout: {{ .WaitMinTimeout }},
{{- end }}
{{- if ne .WaitPollInterval "" }}
PollInterval: {{ .WaitPollInterval }},
{{- end }}
}

return tfresource.WaitUntil(ctx, {{ .WaitTimeout }}, checkFunc, opts)
}
3 changes: 3 additions & 0 deletions internal/generate/tags/templates/v2/header_body.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import (
{{- if .FmtPkg }}
"fmt"
{{- end }}
{{- if .TimePkg }}
"time"
{{- end }}

"github.com/aws/aws-sdk-go-v2/aws"
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
Expand Down
8 changes: 8 additions & 0 deletions internal/generate/tags/templates/v2/update_tags_body.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,14 @@ func {{ .UpdateTagsFunc }}(ctx context.Context, conn {{ .ClientType }}, identifi

{{- end }}

{{ if .WaitForPropagation }}
if len(removedTags) > 0 || len(updatedTags) > 0 {
if err := {{ .WaitTagsPropagatedFunc }}(ctx, conn, identifier, newTags); err != nil {
return fmt.Errorf("waiting for resource (%s) tag propagation: %w", identifier, err)
}
}
{{- end }}

return nil
}

Expand Down
3 changes: 3 additions & 0 deletions internal/generate/tags/templates/v2/v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ var ServiceTagsSliceBody string

//go:embed update_tags_body.tmpl
var UpdateTagsBody string

//go:embed wait_tags_propagated_body.tmpl
var WaitTagsPropagatedBody string
34 changes: 34 additions & 0 deletions internal/generate/tags/templates/v2/wait_tags_propagated_body.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// {{ .WaitTagsPropagatedFunc }} waits for {{ .ServicePackage }} service tags to be propagated.
// The identifier is typically the Amazon Resource Name (ARN), although
// it may also be a different identifier depending on the service.
func {{ .WaitTagsPropagatedFunc }}(ctx context.Context, conn {{ .ClientType }}, id string, tags tftags.KeyValueTags) error {
checkFunc := func() (bool, error) {
output, err := listTags(ctx, conn, id)

if tfresource.NotFound(err) {
return false, nil
}

if err != nil {
return false, err
}

return output.Equal(tags), nil
}
opts := tfresource.WaitOpts{
{{- if ne .WaitContinuousOccurence 0 }}
ContinuousTargetOccurence: {{ .WaitContinuousOccurence }},
{{- end }}
{{- if ne .WaitDelay "" }}
Delay: {{ .WaitDelay }},
{{- end }}
{{- if ne .WaitMinTimeout "" }}
MinTimeout: {{ .WaitMinTimeout }},
{{- end }}
{{- if ne .WaitPollInterval "" }}
PollInterval: {{ .WaitPollInterval }},
{{- end }}
}

return tfresource.WaitUntil(ctx, {{ .WaitTimeout }}, checkFunc, opts)
}
Loading

0 comments on commit e3a40d0

Please sign in to comment.