From 9cfcb40887702cbef78afa7be3d59e98b0fe52d6 Mon Sep 17 00:00:00 2001 From: Scott Suarez Date: Tue, 16 Apr 2024 15:41:10 -0700 Subject: [PATCH] Update active folder to support both LIST and SEARCH methods (#10439) --- .../data_source_google_active_folder.go | 49 ++++++++++++++----- .../data_source_google_active_folder_test.go | 46 +++++++++++++++++ .../docs/d/active_folder.html.markdown | 2 + 3 files changed, 86 insertions(+), 11 deletions(-) diff --git a/mmv1/third_party/terraform/services/resourcemanager/data_source_google_active_folder.go b/mmv1/third_party/terraform/services/resourcemanager/data_source_google_active_folder.go index d1d600992d55..c3580e4e2682 100644 --- a/mmv1/third_party/terraform/services/resourcemanager/data_source_google_active_folder.go +++ b/mmv1/third_party/terraform/services/resourcemanager/data_source_google_active_folder.go @@ -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" ) @@ -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"}), + }, }, } } @@ -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 { diff --git a/mmv1/third_party/terraform/services/resourcemanager/data_source_google_active_folder_test.go b/mmv1/third_party/terraform/services/resourcemanager/data_source_google_active_folder_test.go index 592febaee63b..2370504e28e1 100644 --- a/mmv1/third_party/terraform/services/resourcemanager/data_source_google_active_folder_test.go +++ b/mmv1/third_party/terraform/services/resourcemanager/data_source_google_active_folder_test.go @@ -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) @@ -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) +} diff --git a/mmv1/third_party/terraform/website/docs/d/active_folder.html.markdown b/mmv1/third_party/terraform/website/docs/d/active_folder.html.markdown index 24b65818f083..a258e46f5b83 100644 --- a/mmv1/third_party/terraform/website/docs/d/active_folder.html.markdown +++ b/mmv1/third_party/terraform/website/docs/d/active_folder.html.markdown @@ -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: