-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Deal with undeleatable bucket ACLs in storage.
When GCS buckets are created, they're created with a set of default ACLs: * `OWNER:project-owners-{project_number}` * `OWNER:project-editors-{project_number}` * `READER:project-viewers-{project_number}` Normally, this would be fine, or a minor inconvenience. Terraform could either delete them itself, or the first apply of a user would overwrite them. However, trying to remove the `OWNER:project-owners-{project_number}` ACL yields an API error that the bucket owner must maintain OWNER access to the bucket. This breaks things like `terraform destroy`, but also means any config without that line in it will fail to apply, not just overwrite the value. To make matters worse, trying to *add* the `OWNER:project-owners-{project_number}` ACL to any bucket that already has it _also_ yields the same error about not being able to remove it. To get around this, the storage_bucket_acl resource has been updated to largely ignore _just this_ ACL. It will not try to add it if it already exists, will not try to remove it at all. This does mean that Terraform is incapable of removing this ACL from a bucket, but I'm not sure it's possible to do that with the API, anyways. Tests were also updated to keep the default ACLs as part of the config, and to change the email addresses to addresses we actually own. I tried changing to non-existant hashicorp.com email addresses, but was rejected; only email addresses that are backed by actual Google accounts can be used, sadly.
- Loading branch information
1 parent
b694d5a
commit 7211463
Showing
2 changed files
with
105 additions
and
29 deletions.
There are no files selected for viewing
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
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 |
---|---|---|
|
@@ -2,35 +2,62 @@ package google | |
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform/helper/acctest" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
//"google.golang.org/api/storage/v1" | ||
) | ||
|
||
var roleEntityBasic1 = "OWNER:[email protected]" | ||
|
||
var roleEntityBasic2 = "READER:[email protected]" | ||
|
||
var roleEntityBasic3_owner = "OWNER:[email protected]" | ||
var ( | ||
roleEntityBasic1 = "OWNER:[email protected]" | ||
roleEntityBasic2 = "READER:[email protected]" | ||
roleEntityBasic3_owner = "OWNER:[email protected]" | ||
roleEntityBasic3_reader = "READER:[email protected]" | ||
) | ||
|
||
var roleEntityBasic3_reader = "READER:[email protected]" | ||
func defaultRoleEntities() ([]string, error) { | ||
creds := multiEnvSearch(credsEnvVars) | ||
project := multiEnvSearch(projectEnvVars) | ||
region := multiEnvSearch(regionEnvVars) | ||
config := Config{ | ||
Credentials: creds, | ||
Project: project, | ||
Region: region, | ||
} | ||
if err := config.loadAndValidate(); err != nil { | ||
return nil, fmt.Errorf("Error setting up client: %s", err) | ||
} | ||
p, err := config.clientResourceManager.Projects.Get(project).Do() | ||
if err != nil { | ||
return nil, fmt.Errorf("Error retrieving test project %q: %s", project, err) | ||
} | ||
projectNumber := p.ProjectNumber | ||
return []string{ | ||
"OWNER:project-owners-" + strconv.FormatInt(projectNumber, 10), | ||
"OWNER:project-editors-" + strconv.FormatInt(projectNumber, 10), | ||
"READER:project-viewers-" + strconv.FormatInt(projectNumber, 10), | ||
}, nil | ||
} | ||
|
||
func testBucketName() string { | ||
return fmt.Sprintf("%s-%d", "tf-test-acl-bucket", acctest.RandInt()) | ||
} | ||
|
||
func TestAccGoogleStorageBucketAcl_basic(t *testing.T) { | ||
bucketName := testBucketName() | ||
entities, err := defaultRoleEntities() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccGoogleStorageBucketAclDestroy, | ||
Steps: []resource.TestStep{ | ||
resource.TestStep{ | ||
Config: testGoogleStorageBucketsAclBasic1(bucketName), | ||
Config: testGoogleStorageBucketsAclBasic1(bucketName, entities), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic1), | ||
testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic2), | ||
|
@@ -42,21 +69,25 @@ func TestAccGoogleStorageBucketAcl_basic(t *testing.T) { | |
|
||
func TestAccGoogleStorageBucketAcl_upgrade(t *testing.T) { | ||
bucketName := testBucketName() | ||
entities, err := defaultRoleEntities() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccGoogleStorageBucketAclDestroy, | ||
Steps: []resource.TestStep{ | ||
resource.TestStep{ | ||
Config: testGoogleStorageBucketsAclBasic1(bucketName), | ||
Config: testGoogleStorageBucketsAclBasic1(bucketName, entities), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic1), | ||
testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic2), | ||
), | ||
}, | ||
|
||
resource.TestStep{ | ||
Config: testGoogleStorageBucketsAclBasic2(bucketName), | ||
Config: testGoogleStorageBucketsAclBasic2(bucketName, entities), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic2), | ||
testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic3_owner), | ||
|
@@ -77,21 +108,25 @@ func TestAccGoogleStorageBucketAcl_upgrade(t *testing.T) { | |
|
||
func TestAccGoogleStorageBucketAcl_downgrade(t *testing.T) { | ||
bucketName := testBucketName() | ||
entities, err := defaultRoleEntities() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccGoogleStorageBucketAclDestroy, | ||
Steps: []resource.TestStep{ | ||
resource.TestStep{ | ||
Config: testGoogleStorageBucketsAclBasic2(bucketName), | ||
Config: testGoogleStorageBucketsAclBasic2(bucketName, entities), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic2), | ||
testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic3_owner), | ||
), | ||
}, | ||
|
||
resource.TestStep{ | ||
Config: testGoogleStorageBucketsAclBasic3(bucketName), | ||
Config: testGoogleStorageBucketsAclBasic3(bucketName, entities), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic2), | ||
testAccCheckGoogleStorageBucketAcl(bucketName, roleEntityBasic3_reader), | ||
|
@@ -178,30 +213,30 @@ func testAccGoogleStorageBucketAclDestroy(s *terraform.State) error { | |
return nil | ||
} | ||
|
||
func testGoogleStorageBucketsAclBasic1(bucketName string) string { | ||
func testGoogleStorageBucketsAclBasic1(bucketName string, entities []string) string { | ||
return fmt.Sprintf(` | ||
resource "google_storage_bucket" "bucket" { | ||
name = "%s" | ||
} | ||
resource "google_storage_bucket_acl" "acl" { | ||
bucket = "${google_storage_bucket.bucket.name}" | ||
role_entity = ["%s", "%s"] | ||
role_entity = ["%s", "%s", "%s", "%s", "%s"] | ||
} | ||
`, bucketName, roleEntityBasic1, roleEntityBasic2) | ||
`, bucketName, entities[0], entities[1], entities[2], roleEntityBasic1, roleEntityBasic2) | ||
} | ||
|
||
func testGoogleStorageBucketsAclBasic2(bucketName string) string { | ||
func testGoogleStorageBucketsAclBasic2(bucketName string, entities []string) string { | ||
return fmt.Sprintf(` | ||
resource "google_storage_bucket" "bucket" { | ||
name = "%s" | ||
} | ||
resource "google_storage_bucket_acl" "acl" { | ||
bucket = "${google_storage_bucket.bucket.name}" | ||
role_entity = ["%s", "%s"] | ||
role_entity = ["%s", "%s", "%s", "%s", "%s"] | ||
} | ||
`, bucketName, roleEntityBasic2, roleEntityBasic3_owner) | ||
`, bucketName, entities[0], entities[1], entities[2], roleEntityBasic2, roleEntityBasic3_owner) | ||
} | ||
|
||
func testGoogleStorageBucketsAclBasicDelete(bucketName string) string { | ||
|
@@ -217,17 +252,17 @@ resource "google_storage_bucket_acl" "acl" { | |
`, bucketName) | ||
} | ||
|
||
func testGoogleStorageBucketsAclBasic3(bucketName string) string { | ||
func testGoogleStorageBucketsAclBasic3(bucketName string, entities []string) string { | ||
return fmt.Sprintf(` | ||
resource "google_storage_bucket" "bucket" { | ||
name = "%s" | ||
} | ||
resource "google_storage_bucket_acl" "acl" { | ||
bucket = "${google_storage_bucket.bucket.name}" | ||
role_entity = ["%s", "%s"] | ||
role_entity = ["%s", "%s", "%s", "%s", "%s"] | ||
} | ||
`, bucketName, roleEntityBasic2, roleEntityBasic3_reader) | ||
`, bucketName, entities[0], entities[1], entities[2], roleEntityBasic2, roleEntityBasic3_reader) | ||
} | ||
|
||
func testGoogleStorageBucketsAclPredefined(bucketName string) string { | ||
|