Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Service Catalog Provisioned Product tagging issues #37066

Merged
merged 12 commits into from
Apr 23, 2024
7 changes: 7 additions & 0 deletions .changelog/37066.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:bug
resource/aws_servicecatalog_provisioned_product: Fixes error where tag values are not applied to products when tag values don't change.
```

```release-note:bug
resource/aws_servicecatalog_portfolio: Fixes error where deletion fails if resource was deleted out of band.
```
37 changes: 37 additions & 0 deletions internal/acctest/s3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package acctest

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
tfs3 "github.com/hashicorp/terraform-provider-aws/internal/service/s3"
)

func S3BucketHasTag(ctx context.Context, bucketName, key, value string) resource.TestCheckFunc {
return func(s *terraform.State) error {
conn := Provider.Meta().(*conns.AWSClient).S3Client(ctx)

tags, err := tfs3.BucketListTags(ctx, conn, bucketName)
if err != nil {
return err
}

for k, v := range tags {
if k == key {
if v.ValueString() == value {
return nil
} else {
return fmt.Errorf("expected tag %q value to be %s, got %s", key, value, v.ValueString())
}
}
}

return fmt.Errorf("expected tag %q not found", key)
}
}
15 changes: 15 additions & 0 deletions internal/generate/tagstests/file.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ import (
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-provider-aws/internal/acctest"
"github.com/hashicorp/terraform-provider-aws/names"
{{ range .GoImports -}}
{{ if .Alias }}{{ .Alias }} {{ end }}"{{ .Path }}"
{{ end }}
)

{{ if .Serialize }}
Expand Down Expand Up @@ -173,6 +176,9 @@ func {{ template "testname" . }}_tags_AddOnUpdate(t *testing.T) {
}

func {{ template "testname" . }}_tags_EmptyTag_OnCreate(t *testing.T) {
{{- if .SkipEmptyTags }}
t.Skip("Resource {{ .Name }} does not support empty tags")
{{ end }}
{{- template "Init" . }}

resource.{{ if .Serialize }}Test{{ else }}ParallelTest{{ end }}(t, resource.TestCase{
Expand Down Expand Up @@ -200,6 +206,9 @@ func {{ template "testname" . }}_tags_EmptyTag_OnCreate(t *testing.T) {
}

func {{ template "testname" . }}_tags_EmptyTag_OnUpdate_Add(t *testing.T) {
{{- if .SkipEmptyTags }}
t.Skip("Resource {{ .Name }} does not support empty tags")
{{ end }}
{{- template "Init" . }}

resource.{{ if .Serialize }}Test{{ else }}ParallelTest{{ end }}(t, resource.TestCase{
Expand Down Expand Up @@ -237,6 +246,9 @@ func {{ template "testname" . }}_tags_EmptyTag_OnUpdate_Add(t *testing.T) {
}

func {{ template "testname" . }}_tags_EmptyTag_OnUpdate_Replace(t *testing.T) {
{{- if .SkipEmptyTags }}
t.Skip("Resource {{ .Name }} does not support empty tags")
{{ end }}
{{- template "Init" . }}

resource.{{ if .Serialize }}Test{{ else }}ParallelTest{{ end }}(t, resource.TestCase{
Expand Down Expand Up @@ -500,6 +512,9 @@ func {{ template "testname" . }}_tags_DefaultTags_updateToResourceOnly(t *testin
}

func {{ template "testname" . }}_tags_DefaultTags_emptyResourceTag(t *testing.T) {
{{- if .SkipEmptyTags }}
t.Skip("Resource {{ .Name }} does not support empty tags")
{{ end }}
{{- template "Init" . }}

resource.{{ if .Serialize }}Test{{ else }}ParallelTest{{ end }}(t, resource.TestCase{
Expand Down
38 changes: 37 additions & 1 deletion internal/generate/tagstests/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ type ResourceDatum struct {
Implementation implementation
Serialize bool
PreCheck bool
SkipEmptyTags bool // TODO: Remove when we have a strategy for resources that have a minimum tag value length of 1
GoImports []goImport
}

type goImport struct {
Path string
Alias string
}

//go:embed file.tmpl
Expand Down Expand Up @@ -221,7 +228,28 @@ func (v *visitor) processFuncDecl(funcDecl *ast.FuncDecl) {
d.ExistsTypeName = typeName
}
if attr, ok := args.Keyword["generator"]; ok {
d.Generator = attr
parts := strings.Split(attr, ";")
switch len(parts) {
case 1:
d.Generator = parts[0]

case 2:
d.Generator = parts[1]
d.GoImports = append(d.GoImports, goImport{
Path: parts[0],
})

case 3:
d.Generator = parts[2]
d.GoImports = append(d.GoImports, goImport{
Path: parts[0],
Alias: parts[1],
})

default:
v.errs = append(v.errs, fmt.Errorf("invalid generator value: %q at %s.", attr, fmt.Sprintf("%s.%s", v.packageName, v.functionName)))
continue
}
}
if attr, ok := args.Keyword["importIgnore"]; ok {
d.ImportIgnore = strings.Split(attr, ";")
Expand Down Expand Up @@ -254,6 +282,14 @@ func (v *visitor) processFuncDecl(funcDecl *ast.FuncDecl) {
skip = true
}
}
if attr, ok := args.Keyword["skipEmptyTags"]; ok {
if b, err := strconv.ParseBool(attr); err != nil {
v.errs = append(v.errs, fmt.Errorf("invalid skipEmptyTags value: %q at %s. Should be boolean value.", attr, fmt.Sprintf("%s.%s", v.packageName, v.functionName)))
continue
} else {
d.SkipEmptyTags = b
}
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions internal/service/s3/exports.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ package s3
var (
ResourceBucket = resourceBucket
ResourceObject = resourceObject

BucketListTags = bucketListTags
)
1 change: 0 additions & 1 deletion internal/service/s3/exports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ var (
ResourceDirectoryBucket = newDirectoryBucketResource
ResourceObjectCopy = resourceObjectCopy

BucketListTags = bucketListTags
BucketUpdateTags = bucketUpdateTags
BucketRegionalDomainName = bucketRegionalDomainName
BucketWebsiteEndpointAndDomain = bucketWebsiteEndpointAndDomain
Expand Down
1 change: 1 addition & 0 deletions internal/service/servicecatalog/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

//go:generate go run ../../generate/tags/main.go -ServiceTagsSlice
//go:generate go run ../../generate/servicepackage/main.go
//go:generate go run ../../generate/tagstests/main.go
// ONLY generate directives and package declaration! Do not add anything else to this file.

package servicecatalog
5 changes: 5 additions & 0 deletions internal/service/servicecatalog/portfolio.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (

// @SDKResource("aws_servicecatalog_portfolio", name="Portfolio")
// @Tags
// @Testing(existsType="github.com/aws/aws-sdk-go/service/servicecatalog.DescribePortfolioOutput", generator="github.com/hashicorp/terraform-plugin-testing/helper/acctest;sdkacctest;sdkacctest.RandString(5)", skipEmptyTags=true)
func ResourcePortfolio() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourcePortfolioCreate,
Expand Down Expand Up @@ -185,6 +186,10 @@ func resourcePortfolioDelete(ctx context.Context, d *schema.ResourceData, meta i
Id: aws.String(d.Id()),
})

if tfawserr.ErrCodeEquals(err, servicecatalog.ErrCodeResourceNotFoundException) {
return diags
}

if err != nil {
return sdkdiag.AppendErrorf(diags, "deleting Service Catalog Portfolio (%s): %s", d.Id(), err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestAccServiceCatalogPortfolioDataSource_basic(t *testing.T) {
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.ServiceCatalogServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckServiceCatlaogPortfolioDestroy(ctx),
CheckDestroy: testAccCheckPortfolioDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccPortfolioDataSourceConfig_basic(rName),
Expand Down
Loading
Loading