-
Notifications
You must be signed in to change notification settings - Fork 282
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
spaces: Implement support for bucket policy (#800)
Co-authored-by: Andrew Starr-Bochicchio <andrewsomething@users.noreply.github.com>
- Loading branch information
1 parent
9b08119
commit 438f41a
Showing
178 changed files
with
54,308 additions
and
6,995 deletions.
There are no files selected for viewing
57 changes: 57 additions & 0 deletions
57
digitalocean/import_digitalocean_spaces_bucket_policy_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package digitalocean | ||
|
||
import ( | ||
"fmt" | ||
"regexp" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
) | ||
|
||
func TestAccDigitalOceanBucketPolicy_importBasic(t *testing.T) { | ||
resourceName := "digitalocean_spaces_bucket_policy.policy" | ||
rInt := acctest.RandInt() | ||
|
||
bucketPolicy := `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":"*","Action":"s3:*","Resource":"*"}]}` | ||
|
||
resource.ParallelTest(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
ProviderFactories: testAccProviderFactories, | ||
CheckDestroy: testAccCheckDigitalOceanSpacesBucketPolicyDestroy, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccDigitalOceanSpacesBucketPolicy(rInt, bucketPolicy), | ||
}, | ||
|
||
{ | ||
ResourceName: resourceName, | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
ImportStateIdPrefix: fmt.Sprintf("%s,", testAccDigitalOceanSpacesBucketPolicy_TestRegion), | ||
}, | ||
// Test importing non-existent resource provides expected error. | ||
{ | ||
ResourceName: resourceName, | ||
ImportState: true, | ||
ImportStateVerify: false, | ||
ImportStateId: "policy", | ||
ExpectError: regexp.MustCompile(`importing a Spaces bucket policy requires the format: <region>,<bucket>`), | ||
}, | ||
{ | ||
ResourceName: resourceName, | ||
ImportState: true, | ||
ImportStateVerify: false, | ||
ImportStateId: "nyc2,", | ||
ExpectError: regexp.MustCompile(`importing a Spaces bucket policy requires the format: <region>,<bucket>`), | ||
}, | ||
{ | ||
ResourceName: resourceName, | ||
ImportState: true, | ||
ImportStateVerify: false, | ||
ImportStateId: ",policy", | ||
ExpectError: regexp.MustCompile(`importing a Spaces bucket policy requires the format: <region>,<bucket>`), | ||
}, | ||
}, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
159 changes: 159 additions & 0 deletions
159
digitalocean/resource_digitalocean_spaces_bucket_policy.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
package digitalocean | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"strings" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/aws/awserr" | ||
"github.com/aws/aws-sdk-go/service/s3" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" | ||
) | ||
|
||
func resourceDigitalOceanSpacesBucketPolicy() *schema.Resource { | ||
return &schema.Resource{ | ||
CreateContext: resourceDigitalOceanBucketPolicyCreate, | ||
ReadContext: resourceDigitalOceanBucketPolicyRead, | ||
UpdateContext: resourceDigitalOceanBucketPolicyUpdate, | ||
DeleteContext: resourceDigitalOceanBucketPolicyDelete, | ||
Importer: &schema.ResourceImporter{ | ||
StateContext: resourceDigitalOceanBucketPolicyImport, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"bucket": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"region": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validation.StringInSlice(SpacesRegions, true), | ||
}, | ||
"policy": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ValidateFunc: validation.StringIsJSON, | ||
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { | ||
return compareSpacesBucketPolicy(old, new) | ||
}, | ||
StateFunc: func(v interface{}) string { | ||
json, _ := structure.NormalizeJsonString(v) | ||
return json | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func s3connFromSpacesBucketPolicyResourceData(d *schema.ResourceData, meta interface{}) (*s3.S3, error) { | ||
region := d.Get("region").(string) | ||
|
||
client, err := meta.(*CombinedConfig).spacesClient(region) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
svc := s3.New(client) | ||
return svc, nil | ||
} | ||
|
||
func resourceDigitalOceanBucketPolicyImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { | ||
if strings.Contains(d.Id(), ",") { | ||
s := strings.Split(d.Id(), ",") | ||
|
||
d.SetId(s[1]) | ||
d.Set("region", s[0]) | ||
} | ||
|
||
if d.Id() == "" || d.Get("region") == "" { | ||
return nil, fmt.Errorf("importing a Spaces bucket policy requires the format: <region>,<bucket>") | ||
} | ||
|
||
return []*schema.ResourceData{d}, nil | ||
} | ||
|
||
func resourceDigitalOceanBucketPolicyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
conn, err := s3connFromSpacesBucketPolicyResourceData(d, meta) | ||
if err != nil { | ||
return diag.Errorf("Error occurred while creating new Spaces bucket policy: %s", err) | ||
} | ||
|
||
bucket := d.Get("bucket").(string) | ||
policy := d.Get("policy").(string) | ||
|
||
if policy == "" { | ||
return diag.Errorf("Spaces bucket policy must not be empty") | ||
} | ||
|
||
log.Printf("[DEBUG] Trying to create new Spaces bucket policy for bucket: %s, policy: %s", bucket, policy) | ||
_, err = conn.PutBucketPolicy(&s3.PutBucketPolicyInput{ | ||
Bucket: aws.String(bucket), | ||
Policy: aws.String(policy), | ||
}) | ||
if err != nil { | ||
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoSuchKey" { | ||
return diag.Errorf("Unable to create new Spaces bucket policy because bucket '%s' does not exist", bucket) | ||
} | ||
return diag.Errorf("Error occurred while creating new Spaces bucket policy: %s", err) | ||
} | ||
|
||
d.SetId(bucket) | ||
return resourceDigitalOceanBucketPolicyRead(ctx, d, meta) | ||
} | ||
|
||
func resourceDigitalOceanBucketPolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
conn, err := s3connFromSpacesBucketPolicyResourceData(d, meta) | ||
if err != nil { | ||
return diag.Errorf("Error occurred while fetching Spaces bucket policy: %s", err) | ||
} | ||
|
||
log.Printf("[DEBUG] Trying to fetch Spaces bucket policy for bucket: %s", d.Id()) | ||
response, err := conn.GetBucketPolicy(&s3.GetBucketPolicyInput{ | ||
Bucket: aws.String(d.Id()), | ||
}) | ||
|
||
policy := "" | ||
if err == nil && response.Policy != nil { | ||
policy = aws.StringValue(response.Policy) | ||
} | ||
|
||
d.Set("bucket", d.Id()) | ||
d.Set("policy", policy) | ||
|
||
return nil | ||
} | ||
|
||
func resourceDigitalOceanBucketPolicyUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
return resourceDigitalOceanBucketPolicyCreate(ctx, d, meta) | ||
} | ||
|
||
func resourceDigitalOceanBucketPolicyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
conn, err := s3connFromSpacesBucketPolicyResourceData(d, meta) | ||
if err != nil { | ||
return diag.Errorf("Error occurred while deleting Spaces bucket policy: %s", err) | ||
} | ||
|
||
bucket := d.Id() | ||
|
||
log.Printf("[DEBUG] Trying to delete Spaces bucket policy for bucket: %s", d.Id()) | ||
_, err = conn.DeleteBucketPolicy(&s3.DeleteBucketPolicyInput{ | ||
Bucket: aws.String(bucket), | ||
}) | ||
|
||
if err != nil { | ||
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "BucketDeleted" { | ||
return diag.Errorf("Unable to remove Spaces bucket policy because bucket '%s' is already deleted", bucket) | ||
} | ||
return diag.Errorf("Error occurred while deleting Spaces Bucket policy: %s", err) | ||
} | ||
return nil | ||
} |
Oops, something went wrong.