Skip to content

Commit 0fa5b72

Browse files
Add a custom role field for the Scope RBACRolebindings (#14168) (#23183)
[upstream:2ad216bee6112a0486dd78173451f99120e3c2d6] Signed-off-by: Modular Magician <[email protected]>
1 parent 86b2616 commit 0fa5b72

6 files changed

+197
-9
lines changed

.changelog/14168.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
gkehub: added `custom_role` field to `google_gke_hub_scope_rbac_role_binding` resource
3+
```

google/services/gkehub2/resource_gke_hub_scope_rbac_role_binding.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,18 @@ func ResourceGKEHub2ScopeRBACRoleBinding() *schema.Resource {
6565
MaxItems: 1,
6666
Elem: &schema.Resource{
6767
Schema: map[string]*schema.Schema{
68+
"custom_role": {
69+
Type: schema.TypeString,
70+
Optional: true,
71+
Description: `CustomRole is the custom Kubernetes ClusterRole to be used. The custom role format must be allowlisted in the rbacrolebindingactuation feature and RFC 1123 compliant.`,
72+
ExactlyOneOf: []string{"role.0.predefined_role", "role.0.custom_role"},
73+
},
6874
"predefined_role": {
6975
Type: schema.TypeString,
7076
Optional: true,
7177
ValidateFunc: verify.ValidateEnum([]string{"UNKNOWN", "ADMIN", "EDIT", "VIEW", ""}),
7278
Description: `PredefinedRole is an ENUM representation of the default Kubernetes Roles Possible values: ["UNKNOWN", "ADMIN", "EDIT", "VIEW"]`,
79+
ExactlyOneOf: []string{"role.0.predefined_role", "role.0.custom_role"},
7380
},
7481
},
7582
},
@@ -587,12 +594,18 @@ func flattenGKEHub2ScopeRBACRoleBindingRole(v interface{}, d *schema.ResourceDat
587594
transformed := make(map[string]interface{})
588595
transformed["predefined_role"] =
589596
flattenGKEHub2ScopeRBACRoleBindingRolePredefinedRole(original["predefinedRole"], d, config)
597+
transformed["custom_role"] =
598+
flattenGKEHub2ScopeRBACRoleBindingRoleCustomRole(original["customRole"], d, config)
590599
return []interface{}{transformed}
591600
}
592601
func flattenGKEHub2ScopeRBACRoleBindingRolePredefinedRole(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
593602
return v
594603
}
595604

605+
func flattenGKEHub2ScopeRBACRoleBindingRoleCustomRole(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
606+
return v
607+
}
608+
596609
func flattenGKEHub2ScopeRBACRoleBindingLabels(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
597610
if v == nil {
598611
return v
@@ -651,13 +664,24 @@ func expandGKEHub2ScopeRBACRoleBindingRole(v interface{}, d tpgresource.Terrafor
651664
transformed["predefinedRole"] = transformedPredefinedRole
652665
}
653666

667+
transformedCustomRole, err := expandGKEHub2ScopeRBACRoleBindingRoleCustomRole(original["custom_role"], d, config)
668+
if err != nil {
669+
return nil, err
670+
} else if val := reflect.ValueOf(transformedCustomRole); val.IsValid() && !tpgresource.IsEmptyValue(val) {
671+
transformed["customRole"] = transformedCustomRole
672+
}
673+
654674
return transformed, nil
655675
}
656676

657677
func expandGKEHub2ScopeRBACRoleBindingRolePredefinedRole(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
658678
return v, nil
659679
}
660680

681+
func expandGKEHub2ScopeRBACRoleBindingRoleCustomRole(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
682+
return v, nil
683+
}
684+
661685
func expandGKEHub2ScopeRBACRoleBindingEffectiveLabels(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (map[string]string, error) {
662686
if v == nil {
663687
return map[string]string{}, nil

google/services/gkehub2/resource_gke_hub_scope_rbac_role_binding_generated_meta.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ fields:
1212
- field: 'group'
1313
- field: 'labels'
1414
- field: 'name'
15+
- field: 'role.custom_role'
1516
- field: 'role.predefined_role'
1617
- field: 'scope_id'
1718
provider_only: true

google/services/gkehub2/resource_gke_hub_scope_rbac_role_binding_generated_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ resource "google_gke_hub_scope_rbac_role_binding" "scope_rbac_role_binding" {
7373
labels = {
7474
key = "value"
7575
}
76-
depends_on = [google_gke_hub_scope.scope]
7776
}
7877
`, context)
7978
}

google/services/gkehub2/resource_gke_hub_scope_rbac_role_binding_test.go

Lines changed: 134 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"testing"
2121

2222
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
23-
2423
"github.com/hashicorp/terraform-provider-google/google/acctest"
2524
"github.com/hashicorp/terraform-provider-google/google/envvar"
2625
)
@@ -29,13 +28,16 @@ func TestAccGKEHub2ScopeRBACRoleBinding_gkehubScopeRbacRoleBindingBasicExample_u
2928
t.Parallel()
3029

3130
context := map[string]interface{}{
32-
"project": envvar.GetTestProjectFromEnv(),
33-
"random_suffix": acctest.RandString(t, 10),
31+
"project": envvar.GetTestProjectFromEnv(),
32+
"random_suffix": acctest.RandString(t, 10),
33+
"org_id": envvar.GetTestOrgFromEnv(t),
34+
"billing_account": envvar.GetTestBillingAccountFromEnv(t),
3435
}
3536

3637
acctest.VcrTest(t, resource.TestCase{
3738
PreCheck: func() { acctest.AccTestPreCheck(t) },
3839
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
40+
CheckDestroy: testAccCheckGKEHub2ScopeRBACRoleBindingDestroyProducer(t),
3941
Steps: []resource.TestStep{
4042
{
4143
Config: testAccGKEHub2ScopeRBACRoleBinding_gkehubScopeRbacRoleBindingBasicExample_basic(context),
@@ -67,15 +69,14 @@ resource "google_gke_hub_scope" "scoperbacrolebinding" {
6769
6870
resource "google_gke_hub_scope_rbac_role_binding" "scoperbacrolebinding" {
6971
scope_rbac_role_binding_id = "tf-test-scope-rbac-role-binding%{random_suffix}"
70-
scope_id = "tf-test-scope%{random_suffix}"
72+
scope_id = google_gke_hub_scope.scoperbacrolebinding.scope_id
7173
7274
role {
7375
predefined_role = "ADMIN"
7476
}
7577
labels = {
7678
key = "value"
7779
}
78-
depends_on = [google_gke_hub_scope.scoperbacrolebinding]
7980
}
8081
`, context)
8182
}
@@ -88,15 +89,141 @@ resource "google_gke_hub_scope" "scoperbacrolebinding" {
8889
8990
resource "google_gke_hub_scope_rbac_role_binding" "scoperbacrolebinding" {
9091
scope_rbac_role_binding_id = "tf-test-scope-rbac-role-binding%{random_suffix}"
91-
scope_id = "tf-test-scope%{random_suffix}"
92+
scope_id = google_gke_hub_scope.scoperbacrolebinding.scope_id
9293
9394
role {
9495
predefined_role = "VIEW"
9596
}
9697
labels = {
9798
key = "updated_value"
9899
}
99-
depends_on = [google_gke_hub_scope.scoperbacrolebinding]
100+
}
101+
`, context)
102+
}
103+
104+
func TestAccGKEHub2ScopeRBACRoleBinding_gkehubScopeRbacCustomRoleBindingBasicExample_update(t *testing.T) {
105+
// VCR fails to handle batched project services
106+
acctest.SkipIfVcr(t)
107+
t.Parallel()
108+
109+
context := map[string]interface{}{
110+
"project": envvar.GetTestProjectFromEnv(),
111+
"random_suffix": acctest.RandString(t, 10),
112+
"org_id": envvar.GetTestOrgFromEnv(t),
113+
"billing_account": envvar.GetTestBillingAccountFromEnv(t),
114+
}
115+
116+
acctest.VcrTest(t, resource.TestCase{
117+
PreCheck: func() { acctest.AccTestPreCheck(t) },
118+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
119+
CheckDestroy: testAccCheckGKEHub2ScopeRBACRoleBindingDestroyProducer(t),
120+
Steps: []resource.TestStep{
121+
{
122+
Config: testAccGKEHub2ScopeRBACRoleBinding_gkehubScopeRbacCustomRoleBindingBasicExample_basic(context),
123+
},
124+
{
125+
ResourceName: "google_gke_hub_scope_rbac_role_binding.scope_rbac_custom_role_binding",
126+
ImportState: true,
127+
ImportStateVerify: true,
128+
ImportStateVerifyIgnore: []string{"labels", "scope_id", "scope_rbac_role_binding_id", "terraform_labels"},
129+
},
130+
{
131+
Config: testAccGKEHub2ScopeRBACRoleBinding_gkehubScopeRbacCustomRoleBindingBasicExample_update(context),
132+
},
133+
{
134+
ResourceName: "google_gke_hub_scope_rbac_role_binding.scope_rbac_custom_role_binding",
135+
ImportState: true,
136+
ImportStateVerify: true,
137+
ImportStateVerifyIgnore: []string{"scope_rbac_role_binding_id", "scope_id", "labels", "terraform_labels"},
138+
},
139+
},
140+
})
141+
}
142+
143+
func testAccGKEHub2ScopeRBACRoleBinding_gkehubScopeRbacCustomRoleBindingBasicExample_basic(context map[string]interface{}) string {
144+
return gkeHubRRBActuationProjectSetupForGA(context) + acctest.Nprintf(`
145+
resource "google_gke_hub_scope" "scope" {
146+
scope_id = "tf-test-scope%{random_suffix}"
147+
depends_on = [google_project_service.anthos, google_project_service.gkehub]
148+
}
149+
150+
resource "google_gke_hub_feature" "rbacrolebindingactuation" {
151+
name = "rbacrolebindingactuation"
152+
location = "global"
153+
spec {
154+
rbacrolebindingactuation {
155+
allowed_custom_roles = ["my-custom-role", "my-custom-role-2"]
156+
}
157+
}
158+
depends_on = [google_project_service.anthos, google_project_service.gkehub]
159+
}
160+
161+
resource "google_gke_hub_scope_rbac_role_binding" "scope_rbac_custom_role_binding" {
162+
scope_rbac_role_binding_id = "tf-test-scope-rbac-role-binding%{random_suffix}"
163+
scope_id = google_gke_hub_scope.scope.scope_id
164+
165+
role {
166+
custom_role = "my-custom-role"
167+
}
168+
labels = {
169+
key = "value"
170+
}
171+
depends_on = [google_gke_hub_feature.rbacrolebindingactuation]
172+
}
173+
`, context)
174+
}
175+
176+
func testAccGKEHub2ScopeRBACRoleBinding_gkehubScopeRbacCustomRoleBindingBasicExample_update(context map[string]interface{}) string {
177+
return gkeHubRRBActuationProjectSetupForGA(context) + acctest.Nprintf(`
178+
resource "google_gke_hub_scope" "scope" {
179+
scope_id = "tf-test-scope%{random_suffix}"
180+
}
181+
182+
resource "google_gke_hub_feature" "rbacrolebindingactuation" {
183+
name = "rbacrolebindingactuation"
184+
location = "global"
185+
spec {
186+
rbacrolebindingactuation {
187+
allowed_custom_roles = ["my-custom-role", "my-custom-role-2"]
188+
}
189+
}
190+
depends_on = [google_project_service.anthos, google_project_service.gkehub]
191+
}
192+
193+
resource "google_gke_hub_scope_rbac_role_binding" "scope_rbac_custom_role_binding" {
194+
scope_rbac_role_binding_id = "tf-test-scope-rbac-role-binding%{random_suffix}"
195+
scope_id = google_gke_hub_scope.scope.scope_id
196+
197+
role {
198+
custom_role = "my-custom-role-2"
199+
}
200+
labels = {
201+
key = "value"
202+
}
203+
depends_on = [google_gke_hub_feature.rbacrolebindingactuation]
204+
}
205+
`, context)
206+
}
207+
208+
func gkeHubRRBActuationProjectSetupForGA(context map[string]interface{}) string {
209+
return acctest.Nprintf(`
210+
resource "google_project" "project" {
211+
name = "tf-test-gkehub%{random_suffix}"
212+
project_id = "tf-test-gkehub%{random_suffix}"
213+
org_id = "%{org_id}"
214+
billing_account = "%{billing_account}"
215+
deletion_policy = "DELETE"
216+
}
217+
218+
resource "google_project_service" "anthos" {
219+
project = google_project.project.project_id
220+
service = "anthos.googleapis.com"
221+
}
222+
223+
resource "google_project_service" "gkehub" {
224+
project = google_project.project.project_id
225+
service = "gkehub.googleapis.com"
226+
disable_on_destroy = false
100227
}
101228
`, context)
102229
}

website/docs/r/gke_hub_scope_rbac_role_binding.html.markdown

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,37 @@ resource "google_gke_hub_scope_rbac_role_binding" "scope_rbac_role_binding" {
4848
labels = {
4949
key = "value"
5050
}
51-
depends_on = [google_gke_hub_scope.scope]
51+
}
52+
```
53+
## Example Usage - Gkehub Scope Rbac Custom Role Binding Basic
54+
55+
56+
```hcl
57+
resource "google_gke_hub_scope" "scope" {
58+
scope_id = "tf-test-scope%{random_suffix}"
59+
}
60+
61+
resource "google_gke_hub_feature" "rbacrolebindingactuation" {
62+
name = "rbacrolebindingactuation"
63+
location = "global"
64+
spec {
65+
rbacrolebindingactuation {
66+
allowed_custom_roles = ["my-custom-role"]
67+
}
68+
}
69+
}
70+
71+
resource "google_gke_hub_scope_rbac_role_binding" "scope_rbac_role_binding" {
72+
scope_rbac_role_binding_id = "tf-test-scope-rbac-role-binding%{random_suffix}"
73+
scope_id = google_gke_hub_scope.scope.scope_id
74+
75+
role {
76+
custom_role = "my-custom-role"
77+
}
78+
labels = {
79+
key = "value"
80+
}
81+
depends_on = [google_gke_hub_feature.rbacrolebindingactuation]
5282
}
5383
```
5484

@@ -78,6 +108,10 @@ The following arguments are supported:
78108
PredefinedRole is an ENUM representation of the default Kubernetes Roles
79109
Possible values are: `UNKNOWN`, `ADMIN`, `EDIT`, `VIEW`.
80110

111+
* `custom_role` -
112+
(Optional)
113+
CustomRole is the custom Kubernetes ClusterRole to be used. The custom role format must be allowlisted in the rbacrolebindingactuation feature and RFC 1123 compliant.
114+
81115
- - -
82116

83117

0 commit comments

Comments
 (0)