From e0fd03ac22212f2c1895f2651e263f36409937c4 Mon Sep 17 00:00:00 2001 From: Hossein Golestani Date: Fri, 19 Jul 2024 14:39:40 -0700 Subject: [PATCH] feat: Fleet app operator permissions (#1986) Co-authored-by: Andrew Peabody --- build/int.cloudbuild.yaml | 20 ++++ .../README.md | 26 +++++ .../main.tf | 50 ++++++++++ .../outputs.tf | 26 +++++ .../variables.tf | 21 ++++ .../versions.tf | 31 ++++++ .../fleet-app-operator-permissions/README.md | 44 +++++++++ .../fleet-app-operator-permissions/main.tf | 97 +++++++++++++++++++ .../fleet-app-operator-permissions/outputs.tf | 28 ++++++ .../variables.tf | 45 +++++++++ .../versions.tf | 39 ++++++++ .../main.tf | 26 +++++ .../outputs.tf | 20 ++++ .../variables.tf | 21 ++++ ...ple_fleet_app_operator_permissions_test.go | 61 ++++++++++++ test/setup/iam.tf | 8 ++ test/setup/main.tf | 16 +++ test/setup/outputs.tf | 2 +- 18 files changed, 580 insertions(+), 1 deletion(-) create mode 100644 examples/simple_fleet_app_operator_permissions/README.md create mode 100644 examples/simple_fleet_app_operator_permissions/main.tf create mode 100644 examples/simple_fleet_app_operator_permissions/outputs.tf create mode 100644 examples/simple_fleet_app_operator_permissions/variables.tf create mode 100644 examples/simple_fleet_app_operator_permissions/versions.tf create mode 100644 modules/fleet-app-operator-permissions/README.md create mode 100644 modules/fleet-app-operator-permissions/main.tf create mode 100644 modules/fleet-app-operator-permissions/outputs.tf create mode 100644 modules/fleet-app-operator-permissions/variables.tf create mode 100644 modules/fleet-app-operator-permissions/versions.tf create mode 100644 test/fixtures/simple_fleet_app_operator_permissions/main.tf create mode 100644 test/fixtures/simple_fleet_app_operator_permissions/outputs.tf create mode 100644 test/fixtures/simple_fleet_app_operator_permissions/variables.tf create mode 100644 test/integration/simple_fleet_app_operator_permissions/simple_fleet_app_operator_permissions_test.go diff --git a/build/int.cloudbuild.yaml b/build/int.cloudbuild.yaml index 13306c87b8..47ece2199e 100644 --- a/build/int.cloudbuild.yaml +++ b/build/int.cloudbuild.yaml @@ -535,6 +535,26 @@ steps: - verify simple-autopilot-private-non-default-sa name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' args: ['/bin/bash', '-c', 'cft test run TestSimpleAutopilotPrivateNonDefaultSA --stage teardown --verbose'] +- id: init simple-fleet-app-operator-permissions + waitFor: + - create-all + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestSimpleFleetAppOperatorPermissions --stage init --verbose'] +- id: apply simple-fleet-app-operator-permissions + waitFor: + - init simple-fleet-app-operator-permissions + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestSimpleFleetAppOperatorPermissions --stage apply --verbose'] +- id: verify simple-fleet-app-operator-permissions + waitFor: + - apply simple-fleet-app-operator-permissions + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestSimpleFleetAppOperatorPermissions --stage verify --verbose'] +- id: teardown simple-fleet-app-operator-permissions + waitFor: + - verify simple-fleet-app-operator-permissions + name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' + args: ['/bin/bash', '-c', 'cft test run TestSimpleFleetAppOperatorPermissions --stage teardown --verbose'] tags: - 'ci' - 'integration' diff --git a/examples/simple_fleet_app_operator_permissions/README.md b/examples/simple_fleet_app_operator_permissions/README.md new file mode 100644 index 0000000000..6de1c4e59d --- /dev/null +++ b/examples/simple_fleet_app_operator_permissions/README.md @@ -0,0 +1,26 @@ +# Simple App Operator Permissions Setup for a Fleet Scope + +This example illustrates how to create a Fleet Scope for a [team](https://cloud.google.com/kubernetes-engine/fleet-management/docs/team-management) and set up permissions for an app operator in the team. + + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| fleet\_project\_id | The project to which the Fleet belongs. | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| fleet\_project\_id | The project to which the Fleet belongs. | +| wait | An output (Fleet Scope RBAC Role Binding IDs) to use when you want to depend on granting permissions finishing. | + + + +To provision this example, run the following from within this directory: +- `terraform init` to get the plugins +- `terraform plan` to see the infrastructure plan +- `terraform apply` to apply the infrastructure build +- `terraform destroy` to destroy the built infrastructure + diff --git a/examples/simple_fleet_app_operator_permissions/main.tf b/examples/simple_fleet_app_operator_permissions/main.tf new file mode 100644 index 0000000000..e905f8bc2e --- /dev/null +++ b/examples/simple_fleet_app_operator_permissions/main.tf @@ -0,0 +1,50 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +locals { + app_operator_id = "app-operator-id" + app_operator_team = "app-operator-team" + app_operator_role = "VIEW" +} + +# Create a Service Account, which can be used as an app operator. +resource "google_service_account" "service_account" { + project = var.fleet_project_id + account_id = local.app_operator_id + display_name = "Test App Operator Service Account" +} + +# Create a Fleet Scope for the app operator's team. +resource "google_gke_hub_scope" "scope" { + project = var.fleet_project_id + scope_id = local.app_operator_team +} + +# Grant permissions to the app operator to work with the Fleet Scope. +module "permissions" { + source = "../../modules/fleet-app-operator-permissions" + + fleet_project_id = var.fleet_project_id + scope_id = google_gke_hub_scope.scope.scope_id + users = ["${local.app_operator_id}@${var.fleet_project_id}.iam.gserviceaccount.com"] + groups = [] + role = local.app_operator_role + + depends_on = [ + google_service_account.service_account + ] +} + diff --git a/examples/simple_fleet_app_operator_permissions/outputs.tf b/examples/simple_fleet_app_operator_permissions/outputs.tf new file mode 100644 index 0000000000..d0e55d96eb --- /dev/null +++ b/examples/simple_fleet_app_operator_permissions/outputs.tf @@ -0,0 +1,26 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "fleet_project_id" { + description = "The project to which the Fleet belongs." + value = var.fleet_project_id +} + +output "wait" { + description = "An output (Fleet Scope RBAC Role Binding IDs) to use when you want to depend on granting permissions finishing." + value = module.permissions.wait +} + diff --git a/examples/simple_fleet_app_operator_permissions/variables.tf b/examples/simple_fleet_app_operator_permissions/variables.tf new file mode 100644 index 0000000000..93185b8300 --- /dev/null +++ b/examples/simple_fleet_app_operator_permissions/variables.tf @@ -0,0 +1,21 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "fleet_project_id" { + description = "The project to which the Fleet belongs." + type = string +} + diff --git a/examples/simple_fleet_app_operator_permissions/versions.tf b/examples/simple_fleet_app_operator_permissions/versions.tf new file mode 100644 index 0000000000..fe82a4e387 --- /dev/null +++ b/examples/simple_fleet_app_operator_permissions/versions.tf @@ -0,0 +1,31 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 1.2.0" + + required_providers { + google = { + source = "hashicorp/google" + version = ">= 4.81.0" + } + google-beta = { + source = "hashicorp/google-beta" + version = ">= 4.81.0" + } + } +} + diff --git a/modules/fleet-app-operator-permissions/README.md b/modules/fleet-app-operator-permissions/README.md new file mode 100644 index 0000000000..085fcb4c9c --- /dev/null +++ b/modules/fleet-app-operator-permissions/README.md @@ -0,0 +1,44 @@ +# Terrafrom Module for Fleet App Operator Permissions + +This module bundles different permissions (IAM and RBAC Role Bindings) required for [Fleet team management](https://cloud.google.com/kubernetes-engine/fleet-management/docs/team-management). A platform admin can use this module to set up permissions for an app operator (user or group) in a team--including usage of Fleet Scopes, Connect Gateway, logging, and metrics--based on predefined roles (VIEW, EDIT, ADMIN). + +## Usage +```tf +Example: +module "fleet_app_operator_permissions" { + source = "terraform-google-modules/kubernetes-engine/google//modules/fleet-app-operator-permissions" + + fleet_project_id = "my-project-id" + scope_id = "frontend-team" + users = ["person1@company.com", "person2@company.com"] + groups = ["people@company.com"] + role = "EDIT" +} +``` + +To deploy this config, run: +- `terraform init` to get the plugins +- `terraform plan` to see the infrastructure plan +- `terraform apply` to apply the infrastructure build +- `terraform destroy` to destroy the built infrastructure + + + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| fleet\_project\_id | The project to which the Fleet belongs. | `string` | n/a | yes | +| groups | The list of app operator group principals, e.g., `people@google.com`, `principalSet://iam.googleapis.com/locations/global/workforcePools/my-pool/group/people`. | `list(string)` | n/a | yes | +| role | The principals role for the Fleet Scope (`VIEW`/`EDIT`/`ADMIN`). | `string` | n/a | yes | +| scope\_id | The scope for which IAM and RBAC role bindings are created. | `string` | n/a | yes | +| users | The list of app operator user principals, e.g., `person@google.com`, `principal://iam.googleapis.com/locations/global/workforcePools/my-pool/subject/person`, `serviceAccount:my-service-account@my-project.iam.gserviceaccount.com`. | `list(string)` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| fleet\_project\_id | The project to which the Fleet belongs. | +| wait | An output to use when you want to depend on Scope RBAC Role Binding creation finishing. | + + diff --git a/modules/fleet-app-operator-permissions/main.tf b/modules/fleet-app-operator-permissions/main.tf new file mode 100644 index 0000000000..81fc13a96c --- /dev/null +++ b/modules/fleet-app-operator-permissions/main.tf @@ -0,0 +1,97 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +locals { + user_principals = [for name in var.users : ( + startswith(name, "principal://") ? name : ( + endswith(name, "gserviceaccount.com") ? "serviceAccount:${name}" : ( + "user:${name}" + )))] + + group_principals = [for name in var.groups : ( + startswith(name, "principalSet://") ? name : ( + "group:${name}" + ))] + + project_level_scope_role = { + "VIEW" = "roles/gkehub.scopeViewerProjectLevel" + "EDIT" = "roles/gkehub.scopeEditorProjectLevel" + "ADMIN" = "roles/gkehub.scopeEditorProjectLevel" # Same as EDIT + } + + resource_level_scope_role = { + "VIEW" = "roles/gkehub.scopeViewer" + "EDIT" = "roles/gkehub.scopeEditor" + "ADMIN" = "roles/gkehub.scopeAdmin" + } +} + +resource "google_project_iam_binding" "log_view_permissions" { + project = var.fleet_project_id + role = "roles/logging.viewAccessor" + members = concat(local.user_principals, local.group_principals) + condition { + title = "conditional log view access" + description = "log view access for scope ${var.scope_id}" + expression = "resource.name == \"projects/${var.fleet_project_id}/locations/global/buckets/fleet-o11y-scope-${var.scope_id}/views/fleet-o11y-scope-${var.scope_id}-k8s_container\" || resource.name == \"projects/${var.fleet_project_id}/locations/global/buckets/fleet-o11y-scope-${var.scope_id}/views/fleet-o11y-scope-${var.scope_id}-k8s_pod\"" + } +} + +resource "google_project_iam_binding" "project_level_scope_permissions" { + project = var.fleet_project_id + role = local.project_level_scope_role[var.role] + members = concat(local.user_principals, local.group_principals) +} + +resource "google_gke_hub_scope_iam_binding" "resource_level_scope_permissions" { + project = var.fleet_project_id + scope_id = var.scope_id + role = local.resource_level_scope_role[var.role] + members = concat(local.user_principals, local.group_principals) +} + +resource "random_id" "user_rand_suffix" { + for_each = toset(var.users) + byte_length = 4 +} + +resource "google_gke_hub_scope_rbac_role_binding" "scope_rbac_user_role_bindings" { + for_each = toset(var.users) + project = var.fleet_project_id + scope_rbac_role_binding_id = "tf-${substr(join("", regexall("[a-z0-9]+", each.key)), 0, 16)}-${random_id.user_rand_suffix[each.key].hex}" + scope_id = var.scope_id + user = each.key + role { + predefined_role = var.role + } +} + +resource "random_id" "group_rand_suffix" { + for_each = toset(var.groups) + byte_length = 4 +} + +resource "google_gke_hub_scope_rbac_role_binding" "scope_rbac_group_role_bindings" { + for_each = toset(var.groups) + project = var.fleet_project_id + scope_rbac_role_binding_id = "tf-${substr(join("", regexall("[a-z0-9]+", each.key)), 0, 16)}-${random_id.group_rand_suffix[each.key].hex}" + scope_id = var.scope_id + group = each.key + role { + predefined_role = var.role + } +} + diff --git a/modules/fleet-app-operator-permissions/outputs.tf b/modules/fleet-app-operator-permissions/outputs.tf new file mode 100644 index 0000000000..8b4fbfa5fd --- /dev/null +++ b/modules/fleet-app-operator-permissions/outputs.tf @@ -0,0 +1,28 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "fleet_project_id" { + description = "The project to which the Fleet belongs." + value = var.fleet_project_id +} + +output "wait" { + description = "An output to use when you want to depend on Scope RBAC Role Binding creation finishing." + value = { + for k, v in merge(google_gke_hub_scope_rbac_role_binding.scope_rbac_user_role_bindings, google_gke_hub_scope_rbac_role_binding.scope_rbac_group_role_bindings) : k => v.scope_rbac_role_binding_id + } +} + diff --git a/modules/fleet-app-operator-permissions/variables.tf b/modules/fleet-app-operator-permissions/variables.tf new file mode 100644 index 0000000000..88f27d376f --- /dev/null +++ b/modules/fleet-app-operator-permissions/variables.tf @@ -0,0 +1,45 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "fleet_project_id" { + description = "The project to which the Fleet belongs." + type = string +} + +variable "scope_id" { + description = "The scope for which IAM and RBAC role bindings are created." + type = string +} + +variable "users" { + description = "The list of app operator user principals, e.g., `person@google.com`, `principal://iam.googleapis.com/locations/global/workforcePools/my-pool/subject/person`, `serviceAccount:my-service-account@my-project.iam.gserviceaccount.com`." + type = list(string) +} + +variable "groups" { + description = "The list of app operator group principals, e.g., `people@google.com`, `principalSet://iam.googleapis.com/locations/global/workforcePools/my-pool/group/people`." + type = list(string) +} + +variable "role" { + description = "The principals role for the Fleet Scope (`VIEW`/`EDIT`/`ADMIN`)." + type = string + validation { + condition = contains(["VIEW", "EDIT", "ADMIN"], var.role) + error_message = "Allowed values for role are VIEW, EDIT, or ADMIN." + } +} + diff --git a/modules/fleet-app-operator-permissions/versions.tf b/modules/fleet-app-operator-permissions/versions.tf new file mode 100644 index 0000000000..8903345713 --- /dev/null +++ b/modules/fleet-app-operator-permissions/versions.tf @@ -0,0 +1,39 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_version = ">= 1.2.0" + + required_providers { + google = { + source = "hashicorp/google" + version = ">= 4.81.0" + } + google-beta = { + source = "hashicorp/google-beta" + version = ">= 4.81.0" + } + random = { + source = "hashicorp/random" + version = ">= 2.0.0" + } + } + + provider_meta "google" { + module_name = "blueprints/terraform/terraform-google-kubernetes-engine:fleet-app-operator-permissions/v30.1.0" + } +} + diff --git a/test/fixtures/simple_fleet_app_operator_permissions/main.tf b/test/fixtures/simple_fleet_app_operator_permissions/main.tf new file mode 100644 index 0000000000..e0f036feea --- /dev/null +++ b/test/fixtures/simple_fleet_app_operator_permissions/main.tf @@ -0,0 +1,26 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +locals { + project_id = var.project_ids[3] # gke-project-fleet +} + +module "example" { + source = "../../../examples/simple_fleet_app_operator_permissions" + + fleet_project_id = local.project_id +} + diff --git a/test/fixtures/simple_fleet_app_operator_permissions/outputs.tf b/test/fixtures/simple_fleet_app_operator_permissions/outputs.tf new file mode 100644 index 0000000000..eb65499ec3 --- /dev/null +++ b/test/fixtures/simple_fleet_app_operator_permissions/outputs.tf @@ -0,0 +1,20 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "project_id" { + value = local.project_id +} + diff --git a/test/fixtures/simple_fleet_app_operator_permissions/variables.tf b/test/fixtures/simple_fleet_app_operator_permissions/variables.tf new file mode 100644 index 0000000000..27b7df966e --- /dev/null +++ b/test/fixtures/simple_fleet_app_operator_permissions/variables.tf @@ -0,0 +1,21 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "project_ids" { + type = list(string) + description = "The GCP projects to use for integration tests" +} + diff --git a/test/integration/simple_fleet_app_operator_permissions/simple_fleet_app_operator_permissions_test.go b/test/integration/simple_fleet_app_operator_permissions/simple_fleet_app_operator_permissions_test.go new file mode 100644 index 0000000000..23d9b46ec7 --- /dev/null +++ b/test/integration/simple_fleet_app_operator_permissions/simple_fleet_app_operator_permissions_test.go @@ -0,0 +1,61 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package simple_fleet_app_operator_permissions + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/gcloud" + "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/tft" + "github.com/stretchr/testify/assert" + "github.com/terraform-google-modules/terraform-google-kubernetes-engine/test/integration/testutils" + ) + +func TestSimpleFleetAppOperatorPermissions(t *testing.T) { + appOppT := tft.NewTFBlueprintTest(t, + tft.WithRetryableTerraformErrors(testutils.RetryableTransientErrors, 3, 2*time.Minute), + ) + appOppT.DefineVerify(func(assert *assert.Assertions) { + projectId := appOppT.GetStringOutput("project_id") + scopeId := "app-operator-team" + appOperatorEmail := fmt.Sprintf("app-operator-id@%s.iam.gserviceaccount.com", projectId) + appOperatorPrincipal := fmt.Sprintf("serviceAccount:%s", appOperatorEmail) + scopeLevelRole := "roles/gkehub.scopeViewer" + projectLevelRole := "roles/gkehub.scopeViewerProjectLevel" + logViewRole := "roles/logging.viewAccessor" + logViewContainerBucket := fmt.Sprintf("projects/%s/locations/global/buckets/fleet-o11y-scope-%s/views/fleet-o11y-scope-%s-k8s_container", projectId, scopeId, scopeId) + logViewPodBucket := fmt.Sprintf("projects/%s/locations/global/buckets/fleet-o11y-scope-%s/views/fleet-o11y-scope-%s-k8s_pod", projectId, scopeId, scopeId) + + scopeRrbList := gcloud.Runf(t, "container fleet scopes rbacrolebindings list --scope %s --project %s", scopeId, projectId).String() + assert.Equal(strings.Contains(scopeRrbList, appOperatorEmail), true, "app operator email should be in the list of Scope RBAC Role Bindings") + + scopeIam := gcloud.Runf(t, "container fleet scopes get-iam-policy %s --project %s", scopeId, projectId).String() + assert.Equal(strings.Contains(scopeIam, appOperatorPrincipal), true, "app operator principal should be in the Scope IAM policy") + assert.Equal(strings.Contains(scopeIam, scopeLevelRole), true, "app operator Scope role should be in the Scope IAM policy") + + projectIam := gcloud.Runf(t, "projects get-iam-policy %s", projectId).String() + assert.Equal(strings.Contains(projectIam, appOperatorPrincipal), true, "app operator principal should be in the project IAM policy") + assert.Equal(strings.Contains(projectIam, projectLevelRole), true, "app operator Scope role should be in the project IAM policy") + assert.Equal(strings.Contains(projectIam, logViewRole), true, "app operator log view role should be in the project IAM policy") + assert.Equal(strings.Contains(projectIam, logViewContainerBucket), true, "app operator log view container bucket should be in the project IAM policy") + assert.Equal(strings.Contains(projectIam, logViewPodBucket), true, "app operator log view pod bucket should be in the project IAM policy") + }) + + appOppT.Test() +} + diff --git a/test/setup/iam.tf b/test/setup/iam.tf index 45c4268327..fe97685dd8 100644 --- a/test/setup/iam.tf +++ b/test/setup/iam.tf @@ -104,6 +104,14 @@ resource "google_project_iam_member" "int_test_asm" { member = "serviceAccount:${google_service_account.int_test.email}" } +resource "google_project_iam_member" "int_test_fleet" { + for_each = toset(local.int_required_roles) + + project = module.gke-project-fleet.project_id + role = each.value + member = "serviceAccount:${google_service_account.int_test.email}" +} + resource "google_service_account_key" "int_test" { service_account_id = google_service_account.int_test.id } diff --git a/test/setup/main.tf b/test/setup/main.tf index f0b3e278a6..fb10513a64 100644 --- a/test/setup/main.tf +++ b/test/setup/main.tf @@ -103,3 +103,19 @@ module "gke-project-asm" { activate_apis = local.apis } + +module "gke-project-fleet" { + source = "terraform-google-modules/project-factory/google" + version = "~> 15.0" + + name = "ci-gke-fleet-${random_id.random_project_id_suffix.hex}" + random_project_id = true + org_id = var.org_id + folder_id = var.folder_id + billing_account = var.billing_account + # due to https://github.com/hashicorp/terraform-provider-google/issues/9505 for AP + default_service_account = "keep" + + activate_apis = local.apis +} + diff --git a/test/setup/outputs.tf b/test/setup/outputs.tf index b14d5daac6..54c01df2e7 100644 --- a/test/setup/outputs.tf +++ b/test/setup/outputs.tf @@ -15,7 +15,7 @@ */ output "project_ids" { - value = [module.gke-project-1.project_id, module.gke-project-2.project_id, module.gke-project-asm.project_id] + value = [module.gke-project-1.project_id, module.gke-project-2.project_id, module.gke-project-asm.project_id, module.gke-project-fleet.project_id] } output "sa_key" {