diff --git a/.changelog/6523.txt b/.changelog/6523.txt new file mode 100644 index 0000000000..f4fbe2e5ab --- /dev/null +++ b/.changelog/6523.txt @@ -0,0 +1,3 @@ +```release-note:new-datasource +`google_cloudfunctions2_function` +``` diff --git a/google/data_source_google_cloudfunctions2_function.go b/google/data_source_google_cloudfunctions2_function.go new file mode 100644 index 0000000000..f4e1afb221 --- /dev/null +++ b/google/data_source_google_cloudfunctions2_function.go @@ -0,0 +1,41 @@ +package google + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceGoogleCloudFunctions2Function() *schema.Resource { + // Generate datasource schema from resource + dsSchema := datasourceSchemaFromResourceSchema(resourceCloudfunctions2function().Schema) + + // Set 'Required' schema elements + addRequiredFieldsToSchema(dsSchema, "name", "location") + + // Set 'Optional' schema elements + addOptionalFieldsToSchema(dsSchema, "project") + + return &schema.Resource{ + Read: dataSourceGoogleCloudFunctions2FunctionRead, + Schema: dsSchema, + } +} + +func dataSourceGoogleCloudFunctions2FunctionRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + project, err := getProject(d, config) + if err != nil { + return err + } + + d.SetId(fmt.Sprintf("projects/%s/locations/%s/functions/%s", project, d.Get("location").(string), d.Get("name").(string))) + + err = resourceCloudfunctions2functionRead(d, meta) + if err != nil { + return err + } + + return nil +} diff --git a/google/data_source_google_cloudfunctions2_function_test.go b/google/data_source_google_cloudfunctions2_function_test.go new file mode 100644 index 0000000000..8c6eb2ca0d --- /dev/null +++ b/google/data_source_google_cloudfunctions2_function_test.go @@ -0,0 +1,75 @@ +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceGoogleCloudFunctions2Function_basic(t *testing.T) { + t.Parallel() + + funcDataNameHttp := "data.google_cloudfunctions2_function.function_http_v2" + functionName := fmt.Sprintf("tf-test-%s", randString(t, 10)) + bucketName := fmt.Sprintf("tf-test-bucket-%d", randInt(t)) + zipFilePath := "./test-fixtures/cloudfunctions2/function-source.zip" + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudfunctions2functionDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccDataSourceGoogleCloudFunctions2FunctionConfig(functionName, + bucketName, zipFilePath), + Check: resource.ComposeTestCheckFunc( + checkDataSourceStateMatchesResourceStateWithIgnores(funcDataNameHttp, + "google_cloudfunctions2_function.function_http_v2", map[string]struct{}{"build_config.0.source.0.storage_source.0.bucket": {}, "build_config.0.source.0.storage_source.0.object": {}}), + ), + }, + }, + }) +} + +func testAccDataSourceGoogleCloudFunctions2FunctionConfig(functionName, bucketName, zipFilePath string) string { + return fmt.Sprintf(` +resource "google_storage_bucket" "bucket" { + name = "%s" + location = "US" +} + +resource "google_storage_bucket_object" "object" { + name = "function-source.zip" + bucket = google_storage_bucket.bucket.name + source = "%s" +} + +resource "google_cloudfunctions2_function" "function_http_v2" { + name = "%s" + location = "us-central1" + description = "a new function" + + build_config { + runtime = "nodejs12" + entry_point = "helloHttp" + source { + storage_source { + bucket = google_storage_bucket.bucket.name + object = google_storage_bucket_object.object.name + } + } + } + + service_config { + max_instance_count = 1 + available_memory = "256Mi" + timeout_seconds = 60 + } +} +data "google_cloudfunctions2_function" "function_http_v2" { + name = google_cloudfunctions2_function.function_http_v2.name + location = "us-central1" +} +`, bucketName, zipFilePath, functionName) +} diff --git a/google/provider.go b/google/provider.go index 3f325c8f4f..d4a063ced6 100644 --- a/google/provider.go +++ b/google/provider.go @@ -780,6 +780,7 @@ func Provider() *schema.Provider { "google_client_config": dataSourceGoogleClientConfig(), "google_client_openid_userinfo": dataSourceGoogleClientOpenIDUserinfo(), "google_cloudfunctions_function": dataSourceGoogleCloudFunctionsFunction(), + "google_cloudfunctions2_function": dataSourceGoogleCloudFunctions2Function(), "google_cloud_identity_groups": dataSourceGoogleCloudIdentityGroups(), "google_cloud_identity_group_memberships": dataSourceGoogleCloudIdentityGroupMemberships(), "google_cloud_run_locations": dataSourceGoogleCloudRunLocations(), diff --git a/google/resource_cloudfunctions2_function.go b/google/resource_cloudfunctions2_function.go index 3aa80b07c3..06584f54d1 100644 --- a/google/resource_cloudfunctions2_function.go +++ b/google/resource_cloudfunctions2_function.go @@ -905,11 +905,37 @@ func flattenCloudfunctions2functionBuildConfigSourceStorageSource(v interface{}, } func flattenCloudfunctions2functionBuildConfigSourceStorageSourceBucket(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return d.Get("build_config.0.source.0.storage_source.0.bucket") + // This flatten function is shared between the resource and the datasource. + // TF Input format: {bucket-name} + // GET Response format: gcf-v2-sources-{Project-number}-{location} + // As TF Input and GET response values have different format, + // we will return TF Input value to prevent state drift. + + if bVal, ok := d.GetOk("build_config.0.source.0.storage_source.0.bucket"); ok { + return bVal + } + + // For the datasource, there is no prior TF Input for this attribute. + // Hence, GET Response value is returned. + + return v } func flattenCloudfunctions2functionBuildConfigSourceStorageSourceObject(v interface{}, d *schema.ResourceData, config *Config) interface{} { - return d.Get("build_config.0.source.0.storage_source.0.object") + // This flatten function is shared between the resource and the datasource. + // TF Input format: {object-name} + // GET Response format: {function-name}/{object-name} + // As TF Input and GET response values have different format, + // we will return TF Input value to prevent state drift. + + if ObjVal, ok := d.GetOk("build_config.0.source.0.storage_source.0.object"); ok { + return ObjVal + } + + // For the datasource, there is no prior TF Input for this attribute. + // Hence, GET Response value is returned. + + return v } func flattenCloudfunctions2functionBuildConfigSourceStorageSourceGeneration(v interface{}, d *schema.ResourceData, config *Config) interface{} { diff --git a/website/docs/d/cloudfunctions2_function.html.markdown b/website/docs/d/cloudfunctions2_function.html.markdown new file mode 100644 index 0000000000..1610258bee --- /dev/null +++ b/website/docs/d/cloudfunctions2_function.html.markdown @@ -0,0 +1,38 @@ +--- +subcategory: "Cloud Functions (2nd gen)" +page_title: "Google: google_cloudfunctions2_function" +description: |- + Get information about a Google Cloud Function (2nd gen). +--- + +# google\_cloudfunctions2\_function + +Get information about a Google Cloud Function (2nd gen). For more information see: + +* [API documentation](https://cloud.google.com/functions/docs/reference/rest/v2beta/projects.locations.functions). + +## Example Usage + +```hcl +data "google_cloudfunctions2_function" "my-function" { + name = "function" + location = "us-central1" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of a Cloud Function (2nd gen). + +* `location` - (Required) The location in which the resource belongs. + +- - - + +* `project` - (Optional) The project in which the resource belongs. If it + is not provided, the provider project is used. + +## Attributes Reference + +See [google_cloudfunctions2_function](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloudfunctions2_function) resource for details of all the available attributes. \ No newline at end of file