From 1f27296639fdeac8d32b0829a067c500e3e85020 Mon Sep 17 00:00:00 2001 From: nifflets <5343516+nifflets@users.noreply.github.com> Date: Wed, 29 May 2024 17:52:41 +0000 Subject: [PATCH] Add buildServiceAccount to cloudfunctions --- .../cloudfunctions/CloudFunction.yaml | 4 + .../resource_cloudfunctions_function.go | 19 ++++ ...source_cloudfunctions_function_test.go.erb | 99 +++++++++++++++++++ 3 files changed, 122 insertions(+) diff --git a/mmv1/products/cloudfunctions/CloudFunction.yaml b/mmv1/products/cloudfunctions/CloudFunction.yaml index 03d28a182c84..c4b92107f2fa 100644 --- a/mmv1/products/cloudfunctions/CloudFunction.yaml +++ b/mmv1/products/cloudfunctions/CloudFunction.yaml @@ -118,6 +118,10 @@ properties: name: 'serviceAccountEmail' output: true description: 'The email of the service account for this function.' + - !ruby/object:Api::Type::String + name: 'buildServiceAccount' + default_from_api: true + description: 'The fully-qualified name of the service account to be used for the build step of deploying this function' - !ruby/object:Api::Type::String name: 'updateTime' output: true diff --git a/mmv1/third_party/terraform/services/cloudfunctions/resource_cloudfunctions_function.go b/mmv1/third_party/terraform/services/cloudfunctions/resource_cloudfunctions_function.go index 4c3c1489dd66..0be8a90f4676 100644 --- a/mmv1/third_party/terraform/services/cloudfunctions/resource_cloudfunctions_function.go +++ b/mmv1/third_party/terraform/services/cloudfunctions/resource_cloudfunctions_function.go @@ -298,6 +298,13 @@ func ResourceCloudFunctionsFunction() *schema.Resource { Description: ` If provided, the self-provided service account to run the function with.`, }, + "build_service_account": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: `The fully-qualified name of the service account to be used for the build step of deploying this function`, + }, + "vpc_connector": { Type: schema.TypeString, Optional: true, @@ -627,6 +634,10 @@ func resourceCloudFunctionsCreate(d *schema.ResourceData, meta interface{}) erro function.MinInstances = int64(v.(int)) } + if v, ok := d.GetOk("build_service_account"); ok { + function.BuildServiceAccount = v.(string) + } + log.Printf("[DEBUG] Creating cloud function: %s", function.Name) // We retry the whole create-and-wait because Cloud Functions @@ -714,6 +725,9 @@ func resourceCloudFunctionsRead(d *schema.ResourceData, meta interface{}) error if err := d.Set("service_account_email", function.ServiceAccountEmail); err != nil { return fmt.Errorf("Error setting service_account_email: %s", err) } + if err := d.Set("build_service_account", function.BuildServiceAccount); err != nil { + return fmt.Errorf("Error setting build_service_account: %s", err) + } if err := d.Set("environment_variables", function.EnvironmentVariables); err != nil { return fmt.Errorf("Error setting environment_variables: %s", err) } @@ -945,6 +959,11 @@ func resourceCloudFunctionsUpdate(d *schema.ResourceData, meta interface{}) erro updateMaskArr = append(updateMaskArr, "minInstances") } + if d.HasChange("build_service_account") { + function.BuildServiceAccount = d.Get("build_service_account").(string) + updateMaskArr = append(updateMaskArr, "buildServiceAccount") + } + if len(updateMaskArr) > 0 { log.Printf("[DEBUG] Send Patch CloudFunction Configuration request: %#v", function) updateMask := strings.Join(updateMaskArr, ",") diff --git a/mmv1/third_party/terraform/services/cloudfunctions/resource_cloudfunctions_function_test.go.erb b/mmv1/third_party/terraform/services/cloudfunctions/resource_cloudfunctions_function_test.go.erb index b63b04ab3b57..594bad359a51 100644 --- a/mmv1/third_party/terraform/services/cloudfunctions/resource_cloudfunctions_function_test.go.erb +++ b/mmv1/third_party/terraform/services/cloudfunctions/resource_cloudfunctions_function_test.go.erb @@ -531,6 +531,40 @@ func TestAccCloudFunctionsFunction_secretMount(t *testing.T) { }) } +func TestAccCloudFunctionsFunction_buildServiceAccount(t *testing.T) { + t.Parallel() + + funcResourceName := "google_cloudfunctions_function.function" + functionName := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10)) + bucketName := fmt.Sprintf("tf-test-bucket-%d", acctest.RandInt(t)) + zipFilePath := acctest.CreateZIPArchiveForCloudFunctionSource(t, testHTTPTriggerPath) + proj := envvar.GetTestProjectFromEnv() + serviceAccount := fmt.Sprintf("tf-test-build--%s", acctest.RandString(t, 10)) + defer os.Remove(zipFilePath) // clean up + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + ExternalProviders: map[string]resource.ExternalProvider{ + "time": {}, + }, + CheckDestroy: testAccCheckCloudFunctionsFunctionDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccCloudFunctionsFunction_buildServiceAccount(functionName, bucketName, zipFilePath, serviceAccount), + Check: resource.TestCheckResourceAttr(funcResourceName, + "build_service_account", fmt.Sprintf("projects/%[1]s/serviceAccounts/%s@%[1]s.iam.gserviceaccount.com", proj, serviceAccount)), + }, + { + ResourceName: funcResourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"build_environment_variables"}, + }, + }, + }) +} + func testAccCheckCloudFunctionsFunctionDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { config := acctest.GoogleProviderConfig(t) @@ -1292,3 +1326,68 @@ resource "google_cloudfunctions_function" "function" { } `, accountId, secretName, versionName, bucketName, zipFilePath, functionName, projectNumber, versionNumber) } + +func testAccCloudFunctionsFunction_buildServiceAccount(functionName, bucketName, zipFilePath, serviceAccount string) string { + return fmt.Sprintf(` +data "google_project" "project" {} + +resource "google_storage_bucket" "bucket" { + name = "%s" + location = "US" + uniform_bucket_level_access = true +} + +resource "google_storage_bucket_object" "archive" { + name = "index.zip" + bucket = google_storage_bucket.bucket.name + source = "%s" +} + +resource "google_service_account" "cloud_function_build_account" { + account_id = "%s" + display_name = "Testing Cloud Function build service account" +} + +resource "google_project_iam_member" "storage_object_admin" { + project = data.google_project.project.project_id + role = "roles/storage.objectAdmin" + member = "serviceAccount:${google_service_account.cloud_function_build_account.email}" +} + +resource "google_project_iam_member" "artifact_registry_writer" { + project = data.google_project.project.project_id + role = "roles/artifactregistry.writer" + member = "serviceAccount:${google_service_account.cloud_function_build_account.email}" +} + +resource "google_project_iam_member" "log_writer" { + project = data.google_project.project.project_id + role = "roles/logging.logWriter" + member = "serviceAccount:${google_service_account.cloud_function_build_account.email}" +} + +resource "time_sleep" "wait_iam_roles" { + depends_on = [ + google_project_iam_member.storage_object_admin, + google_project_iam_member.artifact_registry_writer, + google_project_iam_member.log_writer, + ] + create_duration = "60s" +} + +resource "google_cloudfunctions_function" "function" { + depends_on = [time_sleep.wait_iam_roles] + name = "%s" + runtime = "nodejs10" + + source_archive_bucket = google_storage_bucket.bucket.name + source_archive_object = google_storage_bucket_object.archive.name + + build_service_account = google_service_account.cloud_function_build_account.name + + trigger_http = true + entry_point = "helloGET" +} +`, bucketName, zipFilePath, serviceAccount, functionName) +} +