-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Data source for IAM Testable Permissions (#3460)
* Added new data source for iam_testable_permissions * Added tests and docs * Fixed linter errors * Use sdk validation package and allow case-insensitive * Changed stage to stages list and concat results
- Loading branch information
1 parent
351f787
commit 453c9a9
Showing
4 changed files
with
335 additions
and
0 deletions.
There are no files selected for viewing
136 changes: 136 additions & 0 deletions
136
third_party/terraform/data_sources/data_source_google_iam_testable_permissions.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,136 @@ | ||
package google | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema" | ||
"github.com/hashicorp/terraform-plugin-sdk/helper/validation" | ||
) | ||
|
||
func dataSourceGoogleIamTestablePermissions() *schema.Resource { | ||
return &schema.Resource{ | ||
Read: dataSourceGoogleIamTestablePermissionsRead, | ||
Schema: map[string]*schema.Schema{ | ||
"full_resource_name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"stages": { | ||
Type: schema.TypeList, | ||
Optional: true, | ||
Elem: &schema.Schema{ | ||
Type: schema.TypeString, | ||
ValidateFunc: validation.StringInSlice([]string{"ALPHA", "BETA", "GA", "DEPRECATED"}, true), | ||
}, | ||
}, | ||
"custom_support_level": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Default: "SUPPORTED", | ||
ValidateFunc: validation.StringInSlice([]string{"NOT_SUPPORTED", "SUPPORTED", "TESTING"}, true), | ||
}, | ||
"permissions": { | ||
Type: schema.TypeList, | ||
Computed: true, | ||
Elem: &schema.Resource{ | ||
Schema: map[string]*schema.Schema{ | ||
"name": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"title": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"custom_support_level": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"stage": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"api_disabled": { | ||
Type: schema.TypeBool, | ||
Computed: true, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func dataSourceGoogleIamTestablePermissionsRead(d *schema.ResourceData, meta interface{}) (err error) { | ||
config := meta.(*Config) | ||
body := make(map[string]interface{}) | ||
body["pageSize"] = 500 | ||
permissions := make([]map[string]interface{}, 0) | ||
|
||
custom_support_level := strings.ToUpper(d.Get("custom_support_level").(string)) | ||
stages := []string{} | ||
for _, e := range d.Get("stages").([]interface{}) { | ||
stages = append(stages, strings.ToUpper(e.(string))) | ||
} | ||
if len(stages) == 0 { | ||
// Since schema.TypeLists cannot specify defaults, we'll specify it here | ||
stages = append(stages, "GA") | ||
} | ||
for { | ||
url := "https://iam.googleapis.com/v1/permissions:queryTestablePermissions" | ||
body["fullResourceName"] = d.Get("full_resource_name").(string) | ||
res, err := sendRequest(config, "POST", "", url, body) | ||
if err != nil { | ||
return fmt.Errorf("Error retrieving permissions: %s", err) | ||
} | ||
|
||
pagePermissions := flattenTestablePermissionsList(res["permissions"], custom_support_level, stages) | ||
permissions = append(permissions, pagePermissions...) | ||
pToken, ok := res["nextPageToken"] | ||
if ok && pToken != nil && pToken.(string) != "" { | ||
body["pageToken"] = pToken.(string) | ||
} else { | ||
break | ||
} | ||
} | ||
|
||
if err := d.Set("permissions", permissions); err != nil { | ||
return fmt.Errorf("Error retrieving permissions: %s", err) | ||
} | ||
|
||
d.SetId(d.Get("full_resource_name").(string)) | ||
return nil | ||
} | ||
|
||
func flattenTestablePermissionsList(v interface{}, custom_support_level string, stages []string) []map[string]interface{} { | ||
if v == nil { | ||
return make([]map[string]interface{}, 0) | ||
} | ||
|
||
ls := v.([]interface{}) | ||
permissions := make([]map[string]interface{}, 0, len(ls)) | ||
for _, raw := range ls { | ||
p := raw.(map[string]interface{}) | ||
|
||
if _, ok := p["name"]; ok { | ||
var csl bool | ||
if custom_support_level == "SUPPORTED" { | ||
csl = p["customRolesSupportLevel"] == nil || p["customRolesSupportLevel"] == "SUPPORTED" | ||
} else { | ||
csl = p["customRolesSupportLevel"] == custom_support_level | ||
} | ||
if csl && p["stage"] != nil && stringInSlice(stages, p["stage"].(string)) { | ||
permissions = append(permissions, map[string]interface{}{ | ||
"name": p["name"], | ||
"title": p["title"], | ||
"stage": p["stage"], | ||
"api_disabled": p["apiDisabled"], | ||
"custom_support_level": p["customRolesSupportLevel"], | ||
}) | ||
} | ||
} | ||
} | ||
|
||
return permissions | ||
} |
154 changes: 154 additions & 0 deletions
154
third_party/terraform/tests/data_source_google_iam_testable_permissions_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,154 @@ | ||
package google | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-sdk/terraform" | ||
) | ||
|
||
func TestAccDataSourceGoogleIamTestablePermissions_basic(t *testing.T) { | ||
t.Parallel() | ||
|
||
project := getTestProjectFromEnv() | ||
vcrTest(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: fmt.Sprintf(` | ||
data "google_iam_testable_permissions" "perms" { | ||
full_resource_name = "//cloudresourcemanager.googleapis.com/projects/%s" | ||
} | ||
`, project), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckGoogleIamTestablePermissionsMeta( | ||
project, | ||
"data.google_iam_testable_permissions.perms", | ||
[]string{"GA"}, | ||
"", | ||
), | ||
), | ||
}, | ||
{ | ||
Config: fmt.Sprintf(` | ||
data "google_iam_testable_permissions" "perms" { | ||
full_resource_name = "//cloudresourcemanager.googleapis.com/projects/%s" | ||
stages = ["GA"] | ||
} | ||
`, project), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckGoogleIamTestablePermissionsMeta( | ||
project, | ||
"data.google_iam_testable_permissions.perms", | ||
[]string{"GA"}, | ||
"", | ||
), | ||
), | ||
}, | ||
{ | ||
Config: fmt.Sprintf(` | ||
data "google_iam_testable_permissions" "perms" { | ||
full_resource_name = "//cloudresourcemanager.googleapis.com/projects/%s" | ||
custom_support_level = "NOT_SUPPORTED" | ||
stages = ["BETA"] | ||
} | ||
`, project), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckGoogleIamTestablePermissionsMeta( | ||
project, | ||
"data.google_iam_testable_permissions.perms", | ||
[]string{"BETA"}, | ||
"NOT_SUPPORTED", | ||
), | ||
), | ||
}, | ||
{ | ||
Config: fmt.Sprintf(` | ||
data "google_iam_testable_permissions" "perms" { | ||
full_resource_name = "//cloudresourcemanager.googleapis.com/projects/%s" | ||
custom_support_level = "not_supported" | ||
stages = ["beta"] | ||
} | ||
`, project), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckGoogleIamTestablePermissionsMeta( | ||
project, | ||
"data.google_iam_testable_permissions.perms", | ||
[]string{"BETA"}, | ||
"NOT_SUPPORTED", | ||
), | ||
), | ||
}, | ||
{ | ||
Config: fmt.Sprintf(` | ||
data "google_iam_testable_permissions" "perms" { | ||
full_resource_name = "//cloudresourcemanager.googleapis.com/projects/%s" | ||
stages = ["ga", "beta"] | ||
} | ||
`, project), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckGoogleIamTestablePermissionsMeta( | ||
project, | ||
"data.google_iam_testable_permissions.perms", | ||
[]string{"GA", "BETA"}, | ||
"", | ||
), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckGoogleIamTestablePermissionsMeta(project string, n string, expectedStages []string, expectedSupportLevel string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
rs, ok := s.RootModule().Resources[n] | ||
if !ok { | ||
return fmt.Errorf("Can't find perms data source: %s", n) | ||
} | ||
expectedId := fmt.Sprintf("//cloudresourcemanager.googleapis.com/projects/%s", project) | ||
if rs.Primary.ID != expectedId { | ||
return fmt.Errorf("perms data source ID not set.") | ||
} | ||
attrs := rs.Primary.Attributes | ||
count, ok := attrs["permissions.#"] | ||
if !ok { | ||
return fmt.Errorf("can't find 'permsissions' attribute") | ||
} | ||
permCount, err := strconv.Atoi(count) | ||
if err != nil { | ||
return err | ||
} | ||
if permCount < 2 { | ||
return fmt.Errorf("count should be greater than 2") | ||
} | ||
foundStageCounter := len(expectedStages) | ||
foundSupport := false | ||
|
||
for i := 0; i < permCount; i++ { | ||
for s := 0; s < len(expectedStages); s++ { | ||
stageKey := "permissions." + strconv.Itoa(i) + ".stage" | ||
supportKey := "permissions." + strconv.Itoa(i) + ".custom_support_level" | ||
if stringInSlice(expectedStages, attrs[stageKey]) { | ||
foundStageCounter -= 1 | ||
} | ||
if attrs[supportKey] == expectedSupportLevel { | ||
foundSupport = true | ||
} | ||
if foundSupport && foundStageCounter == 0 { | ||
return nil | ||
} | ||
} | ||
} | ||
|
||
if foundSupport { // This means we didn't find a stage | ||
return fmt.Errorf("Could not find stages %v in output", expectedStages) | ||
} | ||
if foundStageCounter == 0 { // This meads we didn't fins a custom_support_level | ||
return fmt.Errorf("Could not find custom_support_level %s in output", expectedSupportLevel) | ||
} | ||
return fmt.Errorf("Unable to find customSupportLevel or stages in output") | ||
} | ||
} |
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
44 changes: 44 additions & 0 deletions
44
...arty/terraform/website/docs/d/datasource_iam_testable_permissions.html.markdown
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,44 @@ | ||
--- | ||
subcategory: "Cloud Platform" | ||
layout: "google" | ||
page_title: "Google: google_projects" | ||
sidebar_current: "docs-google-datasource-iam-testable-permissions" | ||
description: |- | ||
Retrieve a list of testable permissions for a resource. Testable permissions mean the permissions that user can add or remove in a role at a given resource. The resource can be referenced either via the full resource name or via a URI. | ||
--- | ||
|
||
# google\_iam\_testable\_permissions | ||
|
||
Retrieve a list of testable permissions for a resource. Testable permissions mean the permissions that user can add or remove in a role at a given resource. The resource can be referenced either via the full resource name or via a URI. | ||
|
||
## Example Usage - searching for projects about to be deleted in an org | ||
|
||
```hcl | ||
data "google_iam_testable_permissions" "perms" { | ||
full_resource_name = "//cloudresourcemanager.googleapis.com/projects/my-project" | ||
stages = ["GA", "BETA"] | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
* `full_resource_name` - (Required) See [full resource name documentation](https://cloud.google.com/apis/design/resource_names#full_resource_name) for more detail. | ||
* `stages` - (Optional) The acceptable release stages of the permission in the output. Note that `BETA` does not include permissions in `GA`, but you can specify both with `["GA", "BETA"]` for example. Can be a list of `"ALPHA"`, `"BETA"`, `"GA"`, `"DEPRECATED"`. Default is `["GA"]`. | ||
* `custom_support_level` - (Optional) The level of support for custom roles. Can be one of `"NOT_SUPPORTED"`, `"SUPPORTED"`, `"TESTING"`. Default is `"SUPPORTED"` | ||
|
||
## Attributes Reference | ||
|
||
The following attributes are exported: | ||
|
||
* `permissions` - A list of permissions matching the provided input. Structure is defined below. | ||
|
||
The `permissions` block supports: | ||
|
||
* `name` - Name of the permission. | ||
* `title` - Human readable title of the permission. | ||
* `stage` - Release stage of the permission. | ||
* `custom_support_level` - The the support level of this permission for custom roles. | ||
* `api_disabled` - Whether the corresponding API has been enabled for the resource. | ||
|