Skip to content

Commit 6f74ccd

Browse files
Create a terraform resource for the Fleet RBACRolebindingActuation Feature for supporting custom roles in scope RBACRoleBindings (#14046) (#23102)
[upstream:62b710d65caa332fcf3c61d33c46508d20c4e662] Signed-off-by: Modular Magician <[email protected]>
1 parent 4f84377 commit 6f74ccd

File tree

5 files changed

+246
-0
lines changed

5 files changed

+246
-0
lines changed

.changelog/14046.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
gkehub2: added field `spec.rbacrolebindingactuation` to resource `google_gke_hub_feature`
3+
```

google/services/gkehub2/resource_gke_hub_feature.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,24 @@ Please refer to the field 'effective_labels' for all of the labels present on th
640640
},
641641
},
642642
},
643+
"rbacrolebindingactuation": {
644+
Type: schema.TypeList,
645+
Optional: true,
646+
Description: `RBACRolebinding Actuation feature spec.`,
647+
MaxItems: 1,
648+
Elem: &schema.Resource{
649+
Schema: map[string]*schema.Schema{
650+
"allowed_custom_roles": {
651+
Type: schema.TypeList,
652+
Optional: true,
653+
Description: `The list of allowed custom roles (ClusterRoles). If a custom role is not part of this list, it cannot be used in a fleet scope RBACRoleBinding. If a custom role in this list is in use, it cannot be removed from the list until the scope RBACRolebindings using it are deleted.`,
654+
Elem: &schema.Schema{
655+
Type: schema.TypeString,
656+
},
657+
},
658+
},
659+
},
660+
},
643661
},
644662
},
645663
},
@@ -781,6 +799,31 @@ func resourceGKEHub2FeatureCreate(d *schema.ResourceData, meta interface{}) erro
781799
}
782800

783801
headers := make(http.Header)
802+
// Check if the fleet feature already exists. Do an update if so.
803+
804+
getUrl, err := tpgresource.ReplaceVars(d, config, "{{GKEHub2BasePath}}projects/{{project}}/locations/{{location}}/features/{{name}}")
805+
if err != nil {
806+
return err
807+
}
808+
_, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
809+
Config: config,
810+
Method: "GET",
811+
Project: billingProject,
812+
RawURL: getUrl,
813+
UserAgent: userAgent,
814+
Headers: headers,
815+
})
816+
817+
if err == nil {
818+
// Fleet feature already exists
819+
log.Printf("[DEBUG] Fleet feature already exists %s", d.Get("name"))
820+
id, err := tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{location}}/features/{{name}}")
821+
if err != nil {
822+
return fmt.Errorf("Error constructing id: %s", err)
823+
}
824+
d.SetId(id)
825+
return resourceGKEHub2FeatureUpdate(d, meta)
826+
}
784827
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
785828
Config: config,
786829
Method: "POST",
@@ -1019,6 +1062,61 @@ func resourceGKEHub2FeatureDelete(d *schema.ResourceData, meta interface{}) erro
10191062
}
10201063

10211064
headers := make(http.Header)
1065+
// Special handling for the mandatory 'rbacrolebindingactuation' feature.
1066+
// Instead of deleting it, we reset it to a default state by sending a PATCH request.
1067+
if d.Get("name").(string) == "rbacrolebindingactuation" {
1068+
log.Printf("[DEBUG] Mandatory feature 'rbacrolebindingactuation' detected. Resetting instead of deleting.")
1069+
1070+
patchUrl, err := tpgresource.ReplaceVarsForId(d, config, "{{GKEHub2BasePath}}projects/{{project}}/locations/{{location}}/features/{{name}}")
1071+
if err != nil {
1072+
return err
1073+
}
1074+
1075+
// Construct the request body to clear the desired field.
1076+
obj := map[string]interface{}{
1077+
"spec": map[string]interface{}{
1078+
"rbacrolebindingactuation": map[string]interface{}{
1079+
"allowedCustomRoles": []string{},
1080+
},
1081+
},
1082+
}
1083+
1084+
// A specific updateMask is required for a PATCH request.
1085+
updateMask := "spec.rbacrolebindingactuation.allowedCustomRoles"
1086+
url, err := transport_tpg.AddQueryParams(patchUrl, map[string]string{"updateMask": updateMask})
1087+
if err != nil {
1088+
return err
1089+
}
1090+
1091+
log.Printf("[DEBUG] Sending PATCH to reset Feature %q: %#v", d.Id(), obj)
1092+
1093+
// Send the raw PATCH request.
1094+
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
1095+
Config: config,
1096+
Method: "PATCH",
1097+
Project: billingProject,
1098+
RawURL: url,
1099+
UserAgent: userAgent,
1100+
Body: obj,
1101+
Timeout: d.Timeout(schema.TimeoutDelete), // Use the delete timeout for this reset operation.
1102+
Headers: headers,
1103+
})
1104+
if err != nil {
1105+
return fmt.Errorf("error resetting Feature %q: %s", d.Id(), err)
1106+
}
1107+
1108+
// Wait for the long-running operation to complete.
1109+
err = GKEHub2OperationWaitTime(
1110+
config, res, tpgresource.GetResourceNameFromSelfLink(project), "Resetting Feature", userAgent,
1111+
d.Timeout(schema.TimeoutDelete))
1112+
1113+
if err != nil {
1114+
return fmt.Errorf("error waiting to reset Feature %q: %s", d.Id(), err)
1115+
}
1116+
1117+
log.Printf("[DEBUG] Finished resetting Feature %q", d.Id())
1118+
return nil
1119+
}
10221120

10231121
log.Printf("[DEBUG] Deleting Feature %q", d.Id())
10241122
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
@@ -1120,6 +1218,8 @@ func flattenGKEHub2FeatureSpec(v interface{}, d *schema.ResourceData, config *tr
11201218
flattenGKEHub2FeatureSpecFleetobservability(original["fleetobservability"], d, config)
11211219
transformed["clusterupgrade"] =
11221220
flattenGKEHub2FeatureSpecClusterupgrade(original["clusterupgrade"], d, config)
1221+
transformed["rbacrolebindingactuation"] =
1222+
flattenGKEHub2FeatureSpecRbacrolebindingactuation(original["rbacrolebindingactuation"], d, config)
11231223
return []interface{}{transformed}
11241224
}
11251225
func flattenGKEHub2FeatureSpecMulticlusteringress(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
@@ -1298,6 +1398,23 @@ func flattenGKEHub2FeatureSpecClusterupgradeGkeUpgradeOverridesPostConditionsSoa
12981398
return v
12991399
}
13001400

1401+
func flattenGKEHub2FeatureSpecRbacrolebindingactuation(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
1402+
if v == nil {
1403+
return nil
1404+
}
1405+
original := v.(map[string]interface{})
1406+
if len(original) == 0 {
1407+
return nil
1408+
}
1409+
transformed := make(map[string]interface{})
1410+
transformed["allowed_custom_roles"] =
1411+
flattenGKEHub2FeatureSpecRbacrolebindingactuationAllowedCustomRoles(original["allowedCustomRoles"], d, config)
1412+
return []interface{}{transformed}
1413+
}
1414+
func flattenGKEHub2FeatureSpecRbacrolebindingactuationAllowedCustomRoles(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
1415+
return v
1416+
}
1417+
13011418
func flattenGKEHub2FeatureFleetDefaultMemberConfig(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
13021419
if v == nil {
13031420
return nil
@@ -1914,6 +2031,13 @@ func expandGKEHub2FeatureSpec(v interface{}, d tpgresource.TerraformResourceData
19142031
transformed["clusterupgrade"] = transformedClusterupgrade
19152032
}
19162033

2034+
transformedRbacrolebindingactuation, err := expandGKEHub2FeatureSpecRbacrolebindingactuation(original["rbacrolebindingactuation"], d, config)
2035+
if err != nil {
2036+
return nil, err
2037+
} else if val := reflect.ValueOf(transformedRbacrolebindingactuation); val.IsValid() && !tpgresource.IsEmptyValue(val) {
2038+
transformed["rbacrolebindingactuation"] = transformedRbacrolebindingactuation
2039+
}
2040+
19172041
return transformed, nil
19182042
}
19192043

@@ -2177,6 +2301,29 @@ func expandGKEHub2FeatureSpecClusterupgradeGkeUpgradeOverridesPostConditionsSoak
21772301
return v, nil
21782302
}
21792303

2304+
func expandGKEHub2FeatureSpecRbacrolebindingactuation(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
2305+
l := v.([]interface{})
2306+
if len(l) == 0 || l[0] == nil {
2307+
return nil, nil
2308+
}
2309+
raw := l[0]
2310+
original := raw.(map[string]interface{})
2311+
transformed := make(map[string]interface{})
2312+
2313+
transformedAllowedCustomRoles, err := expandGKEHub2FeatureSpecRbacrolebindingactuationAllowedCustomRoles(original["allowed_custom_roles"], d, config)
2314+
if err != nil {
2315+
return nil, err
2316+
} else if val := reflect.ValueOf(transformedAllowedCustomRoles); val.IsValid() && !tpgresource.IsEmptyValue(val) {
2317+
transformed["allowedCustomRoles"] = transformedAllowedCustomRoles
2318+
}
2319+
2320+
return transformed, nil
2321+
}
2322+
2323+
func expandGKEHub2FeatureSpecRbacrolebindingactuationAllowedCustomRoles(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
2324+
return v, nil
2325+
}
2326+
21802327
func expandGKEHub2FeatureFleetDefaultMemberConfig(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
21812328
l := v.([]interface{})
21822329
if len(l) == 0 || l[0] == nil {

google/services/gkehub2/resource_gke_hub_feature_generated_meta.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ fields:
7070
- field: 'spec.fleetobservability.logging_config.default_config.mode'
7171
- field: 'spec.fleetobservability.logging_config.fleet_scope_logs_config.mode'
7272
- field: 'spec.multiclusteringress.config_membership'
73+
- field: 'spec.rbacrolebindingactuation.allowed_custom_roles'
7374
- field: 'state.state.code'
7475
- field: 'state.state.description'
7576
- field: 'state.state.update_time'

google/services/gkehub2/resource_gke_hub_feature_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,6 +934,76 @@ resource "google_gke_hub_feature" "feature" {
934934
`, context)
935935
}
936936

937+
func TestAccGKEHubFeature_Rbacrolebindingactuation(t *testing.T) {
938+
// VCR fails to handle batched project services
939+
acctest.SkipIfVcr(t)
940+
t.Parallel()
941+
942+
context := map[string]interface{}{
943+
"random_suffix": acctest.RandString(t, 10),
944+
"org_id": envvar.GetTestOrgFromEnv(t),
945+
"billing_account": envvar.GetTestBillingAccountFromEnv(t),
946+
}
947+
948+
acctest.VcrTest(t, resource.TestCase{
949+
PreCheck: func() { acctest.AccTestPreCheck(t) },
950+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
951+
CheckDestroy: testAccCheckGKEHubFeatureDestroyProducer(t),
952+
Steps: []resource.TestStep{
953+
{
954+
Config: testAccGKEHubFeature_Rbacrolebindingactuation(context),
955+
},
956+
{
957+
ResourceName: "google_gke_hub_feature.feature",
958+
ImportState: true,
959+
ImportStateVerify: true,
960+
ImportStateVerifyIgnore: []string{"project", "labels", "terraform_labels"},
961+
},
962+
{
963+
Config: testAccGKEHubFeature_RbacrolebindingactuationUpdate(context),
964+
},
965+
{
966+
ResourceName: "google_gke_hub_feature.feature",
967+
ImportState: true,
968+
ImportStateVerify: true,
969+
ImportStateVerifyIgnore: []string{"labels", "terraform_labels"},
970+
},
971+
},
972+
})
973+
}
974+
975+
func testAccGKEHubFeature_Rbacrolebindingactuation(context map[string]interface{}) string {
976+
return gkeHubFeatureProjectSetupForGA(context) + acctest.Nprintf(`
977+
resource "google_gke_hub_feature" "feature" {
978+
name = "rbacrolebindingactuation"
979+
location = "global"
980+
spec {
981+
rbacrolebindingactuation {
982+
allowed_custom_roles = ["custom-role1","custom-role2","custom-role3"]
983+
}
984+
}
985+
depends_on = [google_project_service.anthos, google_project_service.gkehub]
986+
project = google_project.project.project_id
987+
}
988+
`, context)
989+
}
990+
991+
func testAccGKEHubFeature_RbacrolebindingactuationUpdate(context map[string]interface{}) string {
992+
return gkeHubFeatureProjectSetupForGA(context) + acctest.Nprintf(`
993+
resource "google_gke_hub_feature" "feature" {
994+
name = "rbacrolebindingactuation"
995+
location = "global"
996+
spec {
997+
rbacrolebindingactuation {
998+
allowed_custom_roles = ["custom-role1","custom-role2","custom-role3","custom-role4"]
999+
}
1000+
}
1001+
depends_on = [google_project_service.anthos, google_project_service.gkehub]
1002+
project = google_project.project.project_id
1003+
}
1004+
`, context)
1005+
}
1006+
9371007
func gkeHubFeatureProjectSetupForGA(context map[string]interface{}) string {
9381008
return acctest.Nprintf(`
9391009
resource "google_project" "project" {

website/docs/r/gke_hub_feature.html.markdown

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,20 @@ resource "google_gke_hub_feature" "feature" {
301301
}
302302
}
303303
```
304+
## Example Usage - Gkehub Feature Rbacrolebinding Actuation
305+
306+
307+
```hcl
308+
resource "google_gke_hub_feature" "feature" {
309+
name = "rbacrolebindingactuation"
310+
location = "global"
311+
spec {
312+
rbacrolebindingactuation {
313+
allowed_custom_roles = ["custom-role1","custom-role2","custom-role3"]
314+
}
315+
}
316+
}
317+
```
304318

305319
## Argument Reference
306320

@@ -356,6 +370,11 @@ The following arguments are supported:
356370
Clusterupgrade feature spec.
357371
Structure is [documented below](#nested_spec_clusterupgrade).
358372

373+
* `rbacrolebindingactuation` -
374+
(Optional)
375+
RBACRolebinding Actuation feature spec.
376+
Structure is [documented below](#nested_spec_rbacrolebindingactuation).
377+
359378

360379
<a name="nested_spec_multiclusteringress"></a>The `multiclusteringress` block supports:
361380

@@ -450,6 +469,12 @@ The following arguments are supported:
450469
(Required)
451470
Amount of time to "soak" after a rollout has been finished before marking it COMPLETE. Cannot exceed 30 days.
452471

472+
<a name="nested_spec_rbacrolebindingactuation"></a>The `rbacrolebindingactuation` block supports:
473+
474+
* `allowed_custom_roles` -
475+
(Optional)
476+
The list of allowed custom roles (ClusterRoles). If a custom role is not part of this list, it cannot be used in a fleet scope RBACRoleBinding. If a custom role in this list is in use, it cannot be removed from the list until the scope RBACRolebindings using it are deleted.
477+
453478
<a name="nested_fleet_default_member_config"></a>The `fleet_default_member_config` block supports:
454479

455480
* `mesh` -

0 commit comments

Comments
 (0)