Skip to content

Commit 53df635

Browse files
authored
Fix aws.s3.Bucket tag drift detection (#3910)
Make sure that `pulumi refresh` detects changes to bucket tagging. The implementation restores aws_s3_legacy_bucket resource participation in the generic tags interceptor. Fixes #3674
1 parent 84c9dd9 commit 53df635

File tree

5 files changed

+195
-5
lines changed

5 files changed

+195
-5
lines changed

examples/examples_go_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ func TestTagsCombinationsGo(t *testing.T) {
9494
tagsState{DefaultTags: map[string]string{}, ResourceTags: map[string]string{"x": "s", "y": ""}},
9595
tagsState{DefaultTags: map[string]string{"x": ""}, ResourceTags: map[string]string{}},
9696
},
97+
{
98+
"regress 3",
99+
tagsState{DefaultTags: map[string]string{"x": "", "y": "s"}, ResourceTags: map[string]string{"x": "s", "y": "s"}},
100+
tagsState{DefaultTags: map[string]string{}, ResourceTags: map[string]string{"x": "", "y": "s"}},
101+
},
97102
}
98103

99104
for i, tc := range testCases {

patches/0038-Restore-legacy-bucket.patch

+132
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,54 @@ index deadff7071..6310023478 100644
7676
func (client *AWSClient) S3ConnURICleaningDisabled(ctx context.Context) *s3_sdkv1.S3 {
7777
config := client.S3Conn(ctx).Config
7878
config.DisableRestProtocolURICleaning = aws_sdkv2.Bool(true)
79+
diff --git a/internal/provider/provider.go b/internal/provider/provider.go
80+
index 9b4ebd5a46..0e7a94e470 100644
81+
--- a/internal/provider/provider.go
82+
+++ b/internal/provider/provider.go
83+
@@ -16,7 +16,6 @@ import (
84+
85+
"github.com/hashicorp/terraform-provider-aws/internal/service/ecr"
86+
"github.com/hashicorp/terraform-provider-aws/internal/service/gamelift"
87+
- "github.com/hashicorp/terraform-provider-aws/internal/service/s3legacy"
88+
89+
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
90+
awsbase "github.com/hashicorp/aws-sdk-go-base/v2"
91+
@@ -264,8 +263,6 @@ func New(ctx context.Context) (*schema.Provider, error) {
92+
},
93+
94+
ResourcesMap: map[string]*schema.Resource{
95+
- "aws_s3_bucket_legacy": s3legacy.ResourceBucketLegacy(),
96+
-
97+
"aws_gamelift_matchmaking_configuration": gamelift.ResourceMatchMakingConfiguration(),
98+
"aws_gamelift_matchmaking_rule_set": gamelift.ResourceMatchmakingRuleSet(),
99+
},
100+
@@ -278,7 +275,7 @@ func New(ctx context.Context) (*schema.Provider, error) {
101+
var errs []error
102+
servicePackageMap := make(map[string]conns.ServicePackage)
103+
104+
- for _, sp := range servicePackages(ctx) {
105+
+ for _, sp := range servicePackagesAll(ctx) {
106+
servicePackageName := sp.ServicePackageName()
107+
servicePackageMap[servicePackageName] = sp
108+
109+
diff --git a/internal/provider/service_packages_all.go b/internal/provider/service_packages_all.go
110+
new file mode 100644
111+
index 0000000000..51ca53f883
112+
--- /dev/null
113+
+++ b/internal/provider/service_packages_all.go
114+
@@ -0,0 +1,12 @@
115+
+package provider
116+
+
117+
+import (
118+
+ "context"
119+
+
120+
+ "github.com/hashicorp/terraform-provider-aws/internal/conns"
121+
+ "github.com/hashicorp/terraform-provider-aws/internal/service/s3legacy"
122+
+)
123+
+
124+
+func servicePackagesAll(ctx context.Context) []conns.ServicePackage {
125+
+ return append(servicePackages(ctx), s3legacy.ServicePackage(ctx))
126+
+}
79127
diff --git a/internal/service/s3/service_package_bwcompat.go b/internal/service/s3/service_package_bwcompat.go
80128
new file mode 100644
81129
index 0000000000..4278d1e70a
@@ -161,3 +209,87 @@ index 007c2f2dc1..5f64a814be 100644
161209
}
162210

163211
arn := arn.ARN{
212+
diff --git a/internal/service/s3legacy/service_package.go b/internal/service/s3legacy/service_package.go
213+
new file mode 100644
214+
index 0000000000..5d2ea27364
215+
--- /dev/null
216+
+++ b/internal/service/s3legacy/service_package.go
217+
@@ -0,0 +1,78 @@
218+
+package s3legacy
219+
+
220+
+import (
221+
+ "context"
222+
+
223+
+ "github.com/hashicorp/terraform-provider-aws/internal/conns"
224+
+ "github.com/hashicorp/terraform-provider-aws/internal/types"
225+
+)
226+
+
227+
+type servicePackage struct{}
228+
+
229+
+func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.ServicePackageFrameworkDataSource {
230+
+ return []*types.ServicePackageFrameworkDataSource{}
231+
+}
232+
+
233+
+func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource {
234+
+ return []*types.ServicePackageFrameworkResource{}
235+
+}
236+
+
237+
+func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePackageSDKDataSource {
238+
+ return []*types.ServicePackageSDKDataSource{}
239+
+}
240+
+
241+
+func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePackageSDKResource {
242+
+ return []*types.ServicePackageSDKResource{
243+
+ {
244+
+ Factory: ResourceBucketLegacy,
245+
+ TypeName: "aws_s3_bucket_legacy",
246+
+ Name: "BucketLegacy",
247+
+ Tags: &types.ServicePackageResourceTags{
248+
+ IdentifierAttribute: "bucket",
249+
+ ResourceType: "Bucket",
250+
+ },
251+
+ },
252+
+ }
253+
+}
254+
+
255+
+func (p *servicePackage) ServicePackageName() string {
256+
+ return "s3legacy"
257+
+}
258+
+
259+
+func ServicePackage(ctx context.Context) conns.ServicePackage {
260+
+ return &servicePackage{}
261+
+}
262+
+
263+
+// import (
264+
+// "context"
265+
+
266+
+// "github.com/aws/aws-sdk-go-v2/aws"
267+
+// "github.com/aws/aws-sdk-go-v2/aws/retry"
268+
+// "github.com/aws/aws-sdk-go-v2/service/s3"
269+
+// "github.com/hashicorp/aws-sdk-go-base/v2/tfawserr"
270+
+// "github.com/hashicorp/terraform-provider-aws/internal/conns"
271+
+// "github.com/hashicorp/terraform-provider-aws/names"
272+
+// )
273+
+
274+
+// NewClient returns a new AWS SDK for Go v2 client for this service package's AWS API.
275+
+// func (p *servicePackage) NewClient(ctx context.Context, config map[string]any) (*s3.Client, error) {
276+
+// cfg := *(config["aws_sdkv2_config"].(*aws.Config))
277+
+
278+
+// return s3.NewFromConfig(cfg, func(o *s3.Options) {
279+
+// if endpoint := config["endpoint"].(string); endpoint != "" {
280+
+// o.BaseEndpoint = aws.String(endpoint)
281+
+// } else if o.Region == names.USEast1RegionID && config["s3_us_east_1_regional_endpoint"].(string) != "regional" {
282+
+// // Maintain the AWS SDK for Go v1 default of using the global endpoint in us-east-1.
283+
+// // See https://github.com/hashicorp/terraform-provider-aws/issues/33028.
284+
+// o.Region = names.GlobalRegionID
285+
+// }
286+
+// o.UsePathStyle = config["s3_use_path_style"].(bool)
287+
+
288+
+// o.Retryer = conns.AddIsErrorRetryables(cfg.Retryer().(aws.RetryerV2), retry.IsErrorRetryableFunc(func(err error) aws.Ternary {
289+
+// if tfawserr.ErrMessageContains(err, errCodeOperationAborted, "A conflicting conditional operation is currently in progress against this resource. Please try again.") {
290+
+// return aws.TrueTernary
291+
+// }
292+
+// return aws.UnknownTernary // Delegate to configured Retryer.
293+
+// }))
294+
+// }), nil
295+
+// }

provider/provider_test.go

+3-5
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func testProviderUpgrade(t *testing.T, dir string, opts *testProviderUpgradeOpti
7373
assertpreview.HasNoReplacements(t, result)
7474
}
7575

76-
func pulumiTest(t *testing.T, dir string) *pulumitest.PulumiTest {
76+
func pulumiTest(t *testing.T, dir string, opts ...opttest.Option) *pulumitest.PulumiTest {
7777
if testing.Short() {
7878
t.Skipf("Skipping in testing.Short() mode, assuming this is a CI run without AWS creds")
7979
return nil
@@ -82,9 +82,7 @@ func pulumiTest(t *testing.T, dir string) *pulumitest.PulumiTest {
8282
if err != nil {
8383
t.Error(err)
8484
}
85-
ptest := pulumitest.NewPulumiTest(t, dir,
86-
opttest.LocalProviderPath("aws", filepath.Join(cwd, "..", "bin")),
87-
)
88-
85+
opts = append(opts, opttest.LocalProviderPath("aws", filepath.Join(cwd, "..", "bin")))
86+
ptest := pulumitest.NewPulumiTest(t, dir, opts...)
8987
return ptest
9088
}

provider/provider_yaml_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@
66
package provider
77

88
import (
9+
"context"
910
"fmt"
1011
"math/rand"
1112
"os"
1213
"path/filepath"
1314
"testing"
1415

16+
"github.com/aws/aws-sdk-go-v2/config"
17+
s3sdk "github.com/aws/aws-sdk-go-v2/service/s3"
1518
"github.com/pulumi/providertest/pulumitest"
1619
"github.com/pulumi/providertest/pulumitest/assertpreview"
1720
"github.com/pulumi/providertest/pulumitest/opttest"
1821
"github.com/pulumi/pulumi/sdk/v3/go/common/apitype"
22+
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
1923
"github.com/stretchr/testify/assert"
2024
"github.com/stretchr/testify/require"
2125
)
@@ -296,3 +300,38 @@ func TestNonIdempotentSnsTopic(t *testing.T) {
296300
_, err := ptest.CurrentStack().Up(ptest.Context())
297301
require.ErrorContains(t, err, "already exists")
298302
}
303+
304+
// Make sure that legacy Bucket supports deleting tags out of band and detecting drift.
305+
func TestRegress3674(t *testing.T) {
306+
ptest := pulumiTest(t, filepath.Join("test-programs", "regress-3674"), opttest.SkipInstall())
307+
upResult := ptest.Up()
308+
bucketName := upResult.Outputs["bucketName"].Value.(string)
309+
deleteBucketTagging(ptest.Context(), bucketName)
310+
result := ptest.Refresh()
311+
t.Logf("%s", result.StdOut)
312+
require.Equal(t, 1, (*result.Summary.ResourceChanges)["update"])
313+
state, err := ptest.ExportStack().Deployment.MarshalJSON()
314+
require.NoError(t, err)
315+
require.NotContainsf(t, string(state), "MyTestTag", "Expected MyTestTag to be removed")
316+
}
317+
318+
func configureS3() *s3sdk.Client {
319+
loadOpts := []func(*config.LoadOptions) error{}
320+
if p, ok := os.LookupEnv("AWS_PROFILE"); ok {
321+
loadOpts = append(loadOpts, config.WithSharedConfigProfile(p))
322+
}
323+
if r, ok := os.LookupEnv("AWS_REGION"); ok {
324+
loadOpts = append(loadOpts, config.WithRegion(r))
325+
}
326+
cfg, err := config.LoadDefaultConfig(context.TODO(), loadOpts...)
327+
contract.AssertNoErrorf(err, "failed to load AWS config")
328+
return s3sdk.NewFromConfig(cfg)
329+
}
330+
331+
func deleteBucketTagging(ctx context.Context, awsBucket string) {
332+
s3 := configureS3()
333+
_, err := s3.DeleteBucketTagging(ctx, &s3sdk.DeleteBucketTaggingInput{
334+
Bucket: &awsBucket,
335+
})
336+
contract.AssertNoErrorf(err, "failed to delete bucket tagging")
337+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: regress-3674
2+
runtime: yaml
3+
config:
4+
pulumi:tags:
5+
value:
6+
pulumi:template: aws-yaml
7+
outputs:
8+
# Export the name of the bucket
9+
bucketName: ${my-bucket.id}
10+
resources:
11+
# Create an AWS resource (S3 Bucket)
12+
my-bucket:
13+
type: aws:s3:BucketV2
14+
properties:
15+
tags:
16+
MyTestTag: MyTestTag

0 commit comments

Comments
 (0)