Skip to content

Commit 6a255b0

Browse files
authored
feat(object): bucket policy takes on bucket's region rather than default region if not explicit (#2229)
1 parent b2a4834 commit 6a255b0

13 files changed

+3792
-1179
lines changed

docs/resources/object_bucket_policy.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ resource "scaleway_iam_application" "main" {
2121
}
2222
2323
resource "scaleway_object_bucket_policy" "policy" {
24-
bucket = scaleway_object_bucket.bucket.name
24+
bucket = scaleway_object_bucket.bucket.id
2525
policy = jsonencode(
2626
{
2727
Version = "2023-04-17",
@@ -53,7 +53,7 @@ resource "scaleway_object_bucket" "bucket" {
5353
}
5454
5555
resource "scaleway_object_bucket_policy" "main" {
56-
bucket = scaleway_object_bucket.bucket.name
56+
bucket = scaleway_object_bucket.bucket.id
5757
policy = data.aws_iam_policy_document.policy.json
5858
}
5959

docs/resources/object_bucket_website_configuration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ resource "scaleway_object_bucket" "main" {
3333
}
3434
3535
resource "scaleway_object_bucket_policy" "main" {
36-
bucket = scaleway_object_bucket.main.name
36+
bucket = scaleway_object_bucket.main.id
3737
policy = jsonencode(
3838
{
3939
"Version" = "2012-10-17",

scaleway/data_source_object_bucket_policy.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,18 @@ func dataSourceScalewayObjectBucketPolicyRead(ctx context.Context, d *schema.Res
3232
return diag.FromErr(err)
3333
}
3434

35-
bucket := expandID(d.Get("bucket"))
35+
regionalID := expandRegionalID(d.Get("bucket"))
36+
bucket := regionalID.ID
37+
bucketRegion := regionalID.Region
3638
tflog.Debug(ctx, fmt.Sprintf("bucket name: %s", bucket))
3739

40+
if bucketRegion != "" && bucketRegion != region {
41+
s3Client, err = s3ClientForceRegion(d, meta, bucketRegion.String())
42+
if err != nil {
43+
return diag.FromErr(err)
44+
}
45+
region = bucketRegion
46+
}
3847
_ = d.Set("region", region)
3948

4049
tflog.Debug(ctx, fmt.Sprintf("[DEBUG] SCW bucket policy, read for bucket: %s", d.Id()))

scaleway/data_source_object_bucket_policy_test.go

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import (
44
"fmt"
55
"testing"
66

7+
awspolicy "github.com/hashicorp/awspolicyequivalence"
78
sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
89
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
911
)
1012

1113
func TestAccScalewayDataSourceObjectBucketPolicy_Basic(t *testing.T) {
@@ -14,6 +16,24 @@ func TestAccScalewayDataSourceObjectBucketPolicy_Basic(t *testing.T) {
1416

1517
bucketName := sdkacctest.RandomWithPrefix("test-acc-scw-obp-data-basic")
1618

19+
expectedPolicyText := `{
20+
"Version":"2012-10-17",
21+
"Id":"MyPolicy",
22+
"Statement": [
23+
{
24+
"Sid":"GrantToEveryone",
25+
"Effect":"Allow",
26+
"Principal":{
27+
"SCW":"*"
28+
},
29+
"Action":[
30+
"s3:ListBucket",
31+
"s3:GetObject"
32+
]
33+
}
34+
]
35+
}`
36+
1737
resource.ParallelTest(t, resource.TestCase{
1838
PreCheck: func() { testAccPreCheck(t) },
1939
ProviderFactories: tt.ProviderFactories,
@@ -23,10 +43,12 @@ func TestAccScalewayDataSourceObjectBucketPolicy_Basic(t *testing.T) {
2343
Config: fmt.Sprintf(`
2444
resource "scaleway_object_bucket" "main" {
2545
name = "%[1]s"
46+
region = "%[2]s"
2647
}
2748
2849
resource "scaleway_object_bucket_policy" "main" {
29-
bucket = scaleway_object_bucket.main.name
50+
bucket = scaleway_object_bucket.main.id
51+
region = "%[2]s"
3052
policy = jsonencode(
3153
{
3254
Id = "MyPolicy"
@@ -55,14 +77,41 @@ func TestAccScalewayDataSourceObjectBucketPolicy_Basic(t *testing.T) {
5577
data "scaleway_object_bucket_policy" "selected" {
5678
bucket = scaleway_object_bucket_policy.main.bucket
5779
}
58-
`, bucketName),
80+
`, bucketName, objectTestsMainRegion),
5981
Check: resource.ComposeTestCheckFunc(
60-
resource.TestCheckResourceAttr("data.scaleway_object_bucket_policy.selected", "bucket", bucketName),
82+
testAccCheckScalewayObjectBucketExistsForceRegion(tt, "scaleway_object_bucket.main"),
83+
resource.TestCheckResourceAttr("data.scaleway_object_bucket_policy.selected", "bucket", objectTestsMainRegion+"/"+bucketName),
6184
resource.TestCheckResourceAttrSet("data.scaleway_object_bucket_policy.selected", "policy"),
62-
resource.TestCheckResourceAttrPair("data.scaleway_object_bucket_policy.selected", "policy", "scaleway_object_bucket_policy.main", "policy"),
85+
testAccCheckDataSourcePolicyIsEquivalent("data.scaleway_object_bucket_policy.selected", expectedPolicyText),
6386
),
6487
ExpectNonEmptyPlan: !*UpdateCassettes,
6588
},
6689
},
6790
})
6891
}
92+
93+
func testAccCheckDataSourcePolicyIsEquivalent(n, expectedPolicyText string) resource.TestCheckFunc {
94+
return func(s *terraform.State) error {
95+
ds, ok := s.RootModule().Resources[n]
96+
if !ok {
97+
return fmt.Errorf("not found: %s", n)
98+
}
99+
dataSourcePolicy := ds.Primary.Attributes["policy"]
100+
101+
dataSourcePolicyToCompare, err := removePolicyStatementResources(dataSourcePolicy)
102+
if err != nil {
103+
return err
104+
}
105+
106+
equivalent, err := awspolicy.PoliciesAreEquivalent(expectedPolicyText, dataSourcePolicyToCompare)
107+
if err != nil {
108+
return fmt.Errorf("error testing policy equivalence: %s", err)
109+
}
110+
if !equivalent {
111+
return fmt.Errorf("non equivalent policy error:\n\nexpected: %s\n\n got: %s",
112+
expectedPolicyText, dataSourcePolicyToCompare)
113+
}
114+
115+
return nil
116+
}
117+
}

scaleway/helpers_object.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ const (
3030
defaultObjectBucketTimeout = 10 * time.Minute
3131

3232
maxObjectVersionDeletionWorkers = 8
33+
34+
objectTestsMainRegion = "nl-ams"
35+
objectTestsSecondaryRegion = "pl-waw"
3336
)
3437

3538
func newS3Client(httpClient *http.Client, region, accessKey, secretKey string) (*s3.S3, error) {
@@ -66,6 +69,18 @@ func newS3ClientFromMeta(meta *Meta) (*s3.S3, error) {
6669
return newS3Client(meta.httpClient, region.String(), accessKey, secretKey)
6770
}
6871

72+
func newS3ClientFromMetaForceRegion(meta *Meta, region string) (*s3.S3, error) {
73+
accessKey, _ := meta.scwClient.GetAccessKey()
74+
secretKey, _ := meta.scwClient.GetSecretKey()
75+
76+
projectID, _ := meta.scwClient.GetDefaultProjectID()
77+
if projectID != "" {
78+
accessKey = accessKeyWithProjectID(accessKey, projectID)
79+
}
80+
81+
return newS3Client(meta.httpClient, region, accessKey, secretKey)
82+
}
83+
6984
func s3ClientWithRegion(d *schema.ResourceData, m interface{}) (*s3.S3, scw.Region, error) {
7085
meta := m.(*Meta)
7186
region, err := extractRegion(d, meta)
@@ -163,6 +178,23 @@ func s3ClientWithRegionWithNameACL(d *schema.ResourceData, m interface{}, name s
163178
return s3Client, scw.Region(region), name, outerID, err
164179
}
165180

181+
func s3ClientForceRegion(d *schema.ResourceData, m interface{}, region string) (*s3.S3, error) {
182+
meta := m.(*Meta)
183+
184+
accessKey, _ := meta.scwClient.GetAccessKey()
185+
if projectID, _, err := extractProjectID(d, meta); err == nil {
186+
accessKey = accessKeyWithProjectID(accessKey, projectID)
187+
}
188+
secretKey, _ := meta.scwClient.GetSecretKey()
189+
190+
s3Client, err := newS3Client(meta.httpClient, region, accessKey, secretKey)
191+
if err != nil {
192+
return nil, err
193+
}
194+
195+
return s3Client, err
196+
}
197+
166198
func accessKeyWithProjectID(accessKey string, projectID string) string {
167199
return accessKey + "@" + projectID
168200
}

scaleway/resource_object_bucket_policy.go

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
1111
"github.com/hashicorp/terraform-plugin-log/tflog"
1212
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
13-
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
13+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
1414
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1515
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure"
1616
"github.com/scaleway/scaleway-sdk-go/scw"
@@ -30,9 +30,10 @@ func resourceScalewayObjectBucketPolicy() *schema.Resource {
3030
},
3131
Schema: map[string]*schema.Schema{
3232
"bucket": {
33-
Type: schema.TypeString,
34-
Required: true,
35-
Description: "The bucket name.",
33+
Type: schema.TypeString,
34+
Required: true,
35+
Description: "The bucket's name or regional ID.",
36+
DiffSuppressFunc: diffSuppressFuncLocality,
3637
},
3738
"policy": {
3839
Type: schema.TypeString,
@@ -52,9 +53,19 @@ func resourceScalewayObjectBucketPolicyCreate(ctx context.Context, d *schema.Res
5253
return diag.FromErr(err)
5354
}
5455

55-
bucket := expandID(d.Get("bucket"))
56+
regionalID := expandRegionalID(d.Get("bucket"))
57+
bucket := regionalID.ID
58+
bucketRegion := regionalID.Region
5659
tflog.Debug(ctx, fmt.Sprintf("bucket name: %s", bucket))
5760

61+
if bucketRegion != "" && bucketRegion != region {
62+
s3Client, err = s3ClientForceRegion(d, meta, bucketRegion.String())
63+
if err != nil {
64+
return diag.FromErr(err)
65+
}
66+
region = bucketRegion
67+
}
68+
5869
policy, err := structure.NormalizeJsonString(d.Get("policy").(string))
5970
if err != nil {
6071
return diag.FromErr(fmt.Errorf("policy (%s) is an invalid JSON: %w", policy, err))
@@ -67,13 +78,13 @@ func resourceScalewayObjectBucketPolicyCreate(ctx context.Context, d *schema.Res
6778
Policy: scw.StringPtr(policy),
6879
}
6980

70-
err = resource.RetryContext(ctx, 1*time.Minute, func() *resource.RetryError {
81+
err = retry.RetryContext(ctx, 1*time.Minute, func() *retry.RetryError {
7182
_, err := s3Client.PutBucketPolicyWithContext(ctx, params)
7283
if tfawserr.ErrCodeEquals(err, "MalformedPolicy") {
73-
return resource.RetryableError(err)
84+
return retry.RetryableError(err)
7485
}
7586
if err != nil {
76-
return resource.NonRetryableError(err)
87+
return retry.NonRetryableError(err)
7788
}
7889
return nil
7990
})
@@ -97,7 +108,8 @@ func resourceScalewayObjectBucketPolicyRead(ctx context.Context, d *schema.Resou
97108
return diag.FromErr(err)
98109
}
99110

100-
bucket := expandID(d.Id())
111+
regionalID := expandRegionalID(d.Id())
112+
bucket := regionalID.ID
101113

102114
_ = d.Set("region", region)
103115

@@ -132,7 +144,7 @@ func resourceScalewayObjectBucketPolicyRead(ctx context.Context, d *schema.Resou
132144
return diag.FromErr(err)
133145
}
134146

135-
if err := d.Set("bucket", bucket); err != nil {
147+
if err := d.Set("bucket", regionalID.String()); err != nil {
136148
return diag.FromErr(err)
137149
}
138150

0 commit comments

Comments
 (0)