Skip to content

Commit

Permalink
Update active folder to support both LIST and SEARCH methods (GoogleC…
Browse files Browse the repository at this point in the history
  • Loading branch information
ScottSuarez authored and BBBmau committed May 8, 2024
1 parent 6c9f18a commit 1d1e61b
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-google/google/tpgresource"
transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport"
"github.com/hashicorp/terraform-provider-google/google/verify"
resourceManagerV3 "google.golang.org/api/cloudresourcemanager/v3"
)

Expand All @@ -26,6 +27,13 @@ func DataSourceGoogleActiveFolder() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"api_method": {
Type: schema.TypeString,
Optional: true,
Description: "Provides the REST method through which to find the folder. LIST is recommended as it is strongly consistent.",
Default: "LIST",
ValidateFunc: verify.ValidateEnum([]string{"LIST", "SEARCH"}),
},
},
}
}
Expand All @@ -40,24 +48,43 @@ func dataSourceGoogleActiveFolderRead(d *schema.ResourceData, meta interface{})
var folderMatch *resourceManagerV3.Folder
parent := d.Get("parent").(string)
displayName := d.Get("display_name").(string)
token := ""
apiMethod := d.Get("api_method").(string)

if apiMethod == "LIST" {
token := ""

for paginate := true; paginate; {
resp, err := config.NewResourceManagerV3Client(userAgent).Folders.List().Parent(parent).PageSize(300).PageToken(token).Do()
if err != nil {
return fmt.Errorf("error reading folder list: %s", err)
}

for paginate := true; paginate; {
resp, err := config.NewResourceManagerV3Client(userAgent).Folders.List().Parent(parent).PageSize(300).PageToken(token).Do()
for _, folder := range resp.Folders {
if folder.DisplayName == displayName && folder.State == "ACTIVE" {
if folderMatch != nil {
return fmt.Errorf("more than one matching folder found")
}
folderMatch = folder
}
}
token = resp.NextPageToken
paginate = token != ""
}
} else {
queryString := fmt.Sprintf("lifecycleState=ACTIVE AND parent=%s AND displayName=\"%s\"", parent, displayName)
searchRequest := config.NewResourceManagerV3Client(userAgent).Folders.Search()
searchRequest.Query(queryString)
searchResponse, err := searchRequest.Do()
if err != nil {
return fmt.Errorf("error reading folder list: %s", err)
return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("Folder Not Found : %s", displayName))
}

for _, folder := range resp.Folders {
if folder.DisplayName == displayName && folder.State == "ACTIVE" {
if folderMatch != nil {
return fmt.Errorf("more than one matching folder found")
}
for _, folder := range searchResponse.Folders {
if folder.DisplayName == displayName {
folderMatch = folder
break
}
}
token = resp.NextPageToken
paginate = token != ""
}

if folderMatch == nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,29 @@ func TestAccDataSourceGoogleActiveFolder_default(t *testing.T) {
})
}

func TestAccDataSourceGoogleActiveFolder_Search(t *testing.T) {
org := envvar.GetTestOrgFromEnv(t)

parent := fmt.Sprintf("organizations/%s", org)
displayName := "tf-test-" + acctest.RandString(t, 10)

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
ExternalProviders: map[string]resource.ExternalProvider{
"time": {},
},
Steps: []resource.TestStep{
{
Config: testAccDataSourceGoogleActiveFolderConfig_Search(parent, displayName),
Check: resource.ComposeTestCheckFunc(
testAccDataSourceGoogleActiveFolderCheck("data.google_active_folder.my_folder", "google_folder.foobar"),
),
},
},
})
}

func TestAccDataSourceGoogleActiveFolder_space(t *testing.T) {
org := envvar.GetTestOrgFromEnv(t)

Expand Down Expand Up @@ -113,3 +136,26 @@ data "google_active_folder" "my_folder" {
}
`, parent, displayName)
}

func testAccDataSourceGoogleActiveFolderConfig_Search(parent string, displayName string) string {
return fmt.Sprintf(`
resource "google_folder" "foobar" {
parent = "%s"
display_name = "%s"
}
# Wait after folder creation to limit eventual consistency errors.
resource "time_sleep" "wait_120_seconds" {
depends_on = [google_folder.foobar]
create_duration = "120s"
}
data "google_active_folder" "my_folder" {
depends_on = [time_sleep.wait_120_seconds]
parent = google_folder.foobar.parent
display_name = google_folder.foobar.display_name
api_method = "SEARCH"
}
`, parent, displayName)
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ The following arguments are supported:

* `parent` - (Required) The resource name of the parent Folder or Organization.

* `api_method` - (Optional) The API method to use to search for the folder. Valid values are `LIST` and `SEARCH`. Default Value is `LIST`. `LIST` is [strongly consistent](https://cloud.google.com/resource-manager/reference/rest/v3/folders/list#:~:text=list()%20provides%20a-,strongly%20consistent,-view%20of%20the) and requires `resourcemanager.folders.list` on the parent folder, while `SEARCH` is [eventually consistent](https://cloud.google.com/resource-manager/reference/rest/v3/folders/search#:~:text=eventually%20consistent) and only returns folders that the user has `resourcemanager.folders.get` permission on.

## Attributes Reference

In addition to the arguments listed above, the following attributes are exported:
Expand Down

0 comments on commit 1d1e61b

Please sign in to comment.