diff --git a/digitalocean/config.go b/digitalocean/config.go index 0e8da7cd..80ae8ed1 100644 --- a/digitalocean/config.go +++ b/digitalocean/config.go @@ -34,6 +34,11 @@ type CombinedConfig struct { func (c *CombinedConfig) godoClient() *godo.Client { return c.client } func (c *CombinedConfig) spacesClient(region string) (*session.Session, error) { + if c.accessID == "" || c.secretKey == "" { + err := fmt.Errorf("Spaces credentials not configured") + return &session.Session{}, err + } + endpoint := fmt.Sprintf("https://%s.digitaloceanspaces.com", region) client, err := session.NewSession(&aws.Config{ Region: aws.String("us-east-1"), diff --git a/digitalocean/import_digitalocean_bucket_test.go b/digitalocean/import_digitalocean_bucket_test.go index ea66b75b..555e02d5 100644 --- a/digitalocean/import_digitalocean_bucket_test.go +++ b/digitalocean/import_digitalocean_bucket_test.go @@ -1,13 +1,16 @@ package digitalocean import ( + "fmt" "testing" + "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" ) func TestAccDigitalOceanBucket_importBasic(t *testing.T) { - resourceName := "digitalocean_bucket.foobar" + resourceName := "digitalocean_bucket.bucket" + rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -15,13 +18,15 @@ func TestAccDigitalOceanBucket_importBasic(t *testing.T) { CheckDestroy: testAccCheckDigitalOceanBucketDestroy, Steps: []resource.TestStep{ { - Config: testAccDigitalOceanBucketConfigWithAcl, + Config: testAccDigitalOceanBucketConfigImport(rInt), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdPrefix: fmt.Sprintf("%s,", "nyc3"), + ImportStateVerifyIgnore: []string{"acl"}, // ACLs are not saved to tf state }, }, }) diff --git a/digitalocean/provider.go b/digitalocean/provider.go index 85af23be..c0732cbf 100644 --- a/digitalocean/provider.go +++ b/digitalocean/provider.go @@ -17,13 +17,13 @@ func Provider() terraform.ResourceProvider { }, "access_id": { Type: schema.TypeString, - Required: true, + Optional: true, DefaultFunc: schema.EnvDefaultFunc("DO_ACCESS_KEY_ID", nil), Description: "The access key ID for Spaces API operations.", }, "secret_key": { Type: schema.TypeString, - Required: true, + Optional: true, DefaultFunc: schema.EnvDefaultFunc("DO_SECRET_ACCESS_KEY", nil), Description: "The secret access key for Spaces API operations.", }, diff --git a/digitalocean/resource_digitalocean_bucket.go b/digitalocean/resource_digitalocean_bucket.go index 22529f32..3ea56d39 100644 --- a/digitalocean/resource_digitalocean_bucket.go +++ b/digitalocean/resource_digitalocean_bucket.go @@ -3,6 +3,7 @@ package digitalocean import ( "fmt" "log" + "strings" "time" "github.com/aws/aws-sdk-go/aws" @@ -19,7 +20,7 @@ func resourceDigitalOceanBucket() *schema.Resource { Update: resourceDigitalOceanBucketUpdate, Delete: resourceDigitalOceanBucketDelete, Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + State: resourceDigitalOceanBucketImport, }, Schema: map[string]*schema.Schema{ @@ -47,12 +48,13 @@ func resourceDigitalOceanBucket() *schema.Resource { func resourceDigitalOceanBucketCreate(d *schema.ResourceData, meta interface{}) error { region := d.Get("region").(string) client, err := meta.(*CombinedConfig).spacesClient(region) - svc := s3.New(client) if err != nil { return fmt.Errorf("Error creating bucket: %s", err) } + svc := s3.New(client) + input := &s3.CreateBucketInput{ Bucket: aws.String(d.Get("name").(string)), ACL: aws.String(d.Get("acl").(string)), @@ -88,14 +90,15 @@ func resourceDigitalOceanBucketCreate(d *schema.ResourceData, meta interface{}) func resourceDigitalOceanBucketUpdate(d *schema.ResourceData, meta interface{}) error { region := d.Get("region").(string) client, err := meta.(*CombinedConfig).spacesClient(region) - svc := s3.New(client) if err != nil { return fmt.Errorf("Error updating bucket: %s", err) } + svc := s3.New(client) + if d.HasChange("acl") { - if err := resourceDigitalOceanBucketAclUpdate(svc, d); err != nil { + if err := resourceDigitalOceanBucketACLUpdate(svc, d); err != nil { return err } } @@ -106,12 +109,13 @@ func resourceDigitalOceanBucketUpdate(d *schema.ResourceData, meta interface{}) func resourceDigitalOceanBucketRead(d *schema.ResourceData, meta interface{}) error { region := d.Get("region").(string) client, err := meta.(*CombinedConfig).spacesClient(region) - svc := s3.New(client) if err != nil { return fmt.Errorf("Error reading bucket: %s", err) } + svc := s3.New(client) + _, err = retryOnAwsCode("NoSuchBucket", func() (interface{}, error) { return svc.HeadBucket(&s3.HeadBucketInput{ Bucket: aws.String(d.Id()), @@ -165,12 +169,13 @@ func resourceDigitalOceanBucketRead(d *schema.ResourceData, meta interface{}) er func resourceDigitalOceanBucketDelete(d *schema.ResourceData, meta interface{}) error { region := d.Get("region").(string) client, err := meta.(*CombinedConfig).spacesClient(region) - svc := s3.New(client) if err != nil { return fmt.Errorf("Error deleting bucket: %s", err) } + svc := s3.New(client) + log.Printf("[DEBUG] Spaces Delete Bucket: %s", d.Id()) _, err = svc.DeleteBucket(&s3.DeleteBucketInput{ Bucket: aws.String(d.Id()), @@ -239,7 +244,7 @@ func resourceDigitalOceanBucketDelete(d *schema.ResourceData, meta interface{}) return nil } -func resourceDigitalOceanBucketAclUpdate(svc *s3.S3, d *schema.ResourceData) error { +func resourceDigitalOceanBucketACLUpdate(svc *s3.S3, d *schema.ResourceData) error { acl := d.Get("acl").(string) bucket := d.Get("name").(string) @@ -259,6 +264,25 @@ func resourceDigitalOceanBucketAclUpdate(svc *s3.S3, d *schema.ResourceData) err return nil } +func resourceDigitalOceanBucketImport(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]) + } + + err := resourceDigitalOceanBucketRead(d, meta) + if err != nil { + return nil, fmt.Errorf("unable to import bucket: %v", err) + } + + results := make([]*schema.ResourceData, 0) + results = append(results, d) + + return results, nil +} + func bucketDomainName(bucket string, region string) string { return fmt.Sprintf("%q.%q.digitaloceanspaces.com", bucket, region) } diff --git a/digitalocean/resource_digitalocean_bucket_test.go b/digitalocean/resource_digitalocean_bucket_test.go index dad813ec..2cdec94f 100644 --- a/digitalocean/resource_digitalocean_bucket_test.go +++ b/digitalocean/resource_digitalocean_bucket_test.go @@ -66,8 +66,8 @@ func TestAccDigitalOceanBucket_region(t *testing.T) { func TestAccDigitalOceanBucket_UpdateAcl(t *testing.T) { ri := acctest.RandInt() - preConfig := fmt.Sprintf(testAccDigitalOceanBucketConfigWithAcl, ri) - postConfig := fmt.Sprintf(testAccDigitalOceanBucketConfigWithAclUpdate, ri) + preConfig := fmt.Sprintf(testAccDigitalOceanBucketConfigWithACL, ri) + postConfig := fmt.Sprintf(testAccDigitalOceanBucketConfigWithACLUpdate, ri) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -121,23 +121,25 @@ func testAccCheckDigitalOceanBucketDestroy(s *terraform.State) error { } func testAccCheckDigitalOceanBucketDestroyWithProvider(s *terraform.State, provider *schema.Provider) error { - sesh, err := session.NewSession(&aws.Config{ - Region: aws.String("nyc3"), - Credentials: credentials.NewStaticCredentials(os.Getenv("DO_ACCESS_KEY_ID"), os.Getenv("DO_SECRET_ACCESS_KEY"), "")}, - ) - svc := s3.New(sesh, &aws.Config{ - Endpoint: aws.String("https://nyc3.digitaloceanspaces.com")}, - ) - - if err != nil { - log.Fatal(err) - } for _, rs := range s.RootModule().Resources { + sesh, err := session.NewSession(&aws.Config{ + Region: aws.String(rs.Primary.Attributes["region"]), + Credentials: credentials.NewStaticCredentials(os.Getenv("DO_ACCESS_KEY_ID"), os.Getenv("DO_SECRET_ACCESS_KEY"), "")}, + ) + + svc := s3.New(sesh, &aws.Config{ + Endpoint: aws.String(fmt.Sprintf("https://%s.digitaloceanspaces.com", rs.Primary.Attributes["region"]))}, + ) + + if err != nil { + log.Fatal(err) + } + if rs.Type != "digitalocean_bucket" { continue } - _, err := svc.DeleteBucket(&s3.DeleteBucketInput{ + _, err = svc.DeleteBucket(&s3.DeleteBucketInput{ Bucket: aws.String(rs.Primary.ID), }) if err != nil { @@ -166,11 +168,11 @@ func testAccCheckDigitalOceanBucketExistsWithProvider(n string, providerF func() } sesh, err := session.NewSession(&aws.Config{ - Region: aws.String("nyc3"), + Region: aws.String(rs.Primary.Attributes["region"]), Credentials: credentials.NewStaticCredentials(os.Getenv("DO_ACCESS_KEY_ID"), os.Getenv("DO_SECRET_ACCESS_KEY"), "")}, ) svc := s3.New(sesh, &aws.Config{ - Endpoint: aws.String("https://nyc3.digitaloceanspaces.com")}, + Endpoint: aws.String(fmt.Sprintf("https://%s.digitaloceanspaces.com", rs.Primary.Attributes["region"]))}, ) if err != nil { @@ -204,11 +206,11 @@ func testAccCheckDigitalOceanDestroyBucket(n string) resource.TestCheckFunc { } sesh, err := session.NewSession(&aws.Config{ - Region: aws.String("nyc3"), + Region: aws.String(rs.Primary.Attributes["region"]), Credentials: credentials.NewStaticCredentials(os.Getenv("DO_ACCESS_KEY_ID"), os.Getenv("DO_SECRET_ACCESS_KEY"), "")}, ) svc := s3.New(sesh, &aws.Config{ - Endpoint: aws.String("https://nyc3.digitaloceanspaces.com")}, + Endpoint: aws.String(fmt.Sprintf("https://%s.digitaloceanspaces.com", rs.Primary.Attributes["region"]))}, ) if err != nil { @@ -266,14 +268,22 @@ resource "digitalocean_bucket" "bucket" { `, randInt) } -var testAccDigitalOceanBucketConfigWithAcl = ` +func testAccDigitalOceanBucketConfigImport(randInt int) string { + return fmt.Sprintf(` +resource "digitalocean_bucket" "bucket" { + name = "tf-test-bucket-%d" +} +`, randInt) +} + +var testAccDigitalOceanBucketConfigWithACL = ` resource "digitalocean_bucket" "bucket" { name = "tf-test-bucket-%d" acl = "public-read" } ` -var testAccDigitalOceanBucketConfigWithAclUpdate = ` +var testAccDigitalOceanBucketConfigWithACLUpdate = ` resource "digitalocean_bucket" "bucket" { name = "tf-test-bucket-%d" acl = "private" diff --git a/website/docs/r/bucket.html.markdown b/website/docs/r/bucket.html.markdown index 6a28e80a..59fb415c 100644 --- a/website/docs/r/bucket.html.markdown +++ b/website/docs/r/bucket.html.markdown @@ -49,8 +49,8 @@ The following attributes are exported: ## Import -Buckets can be imported using the `name`, e.g. +Buckets can be imported using the `region` and `name` attributes (delimited by a comma): ``` -terraform import digitalocean_bucket.foobar `name` +terraform import digitalocean_bucket.foobar `region`,`name` ```