From 6dba89ab900f56d0bac069cd6f18c1a3d76b661f Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Tue, 29 Nov 2022 18:59:20 +0000 Subject: [PATCH] datafusion - add args + iam support (#6270) Closes https://github.com/hashicorp/terraform-provider-google/issues/10536 Closes https://github.com/hashicorp/terraform-provider-google/issues/9065 Closes https://github.com/hashicorp/terraform-provider-google/issues/9068 Signed-off-by: Modular Magician --- .changelog/6270.txt | 15 ++ google-beta/iam_data_fusion_instance.go | 223 +++++++++++++++++ ...iam_data_fusion_instance_generated_test.go | 235 ++++++++++++++++++ google-beta/provider.go | 7 +- google-beta/resource_data_fusion_instance.go | 166 ++++++++++++- ...rce_data_fusion_instance_generated_test.go | 125 ++++++++-- .../docs/r/data_fusion_instance.html.markdown | 114 +++++++-- .../r/data_fusion_instance_iam.html.markdown | 153 ++++++++++++ 8 files changed, 1007 insertions(+), 31 deletions(-) create mode 100644 .changelog/6270.txt create mode 100644 google-beta/iam_data_fusion_instance.go create mode 100644 google-beta/iam_data_fusion_instance_generated_test.go create mode 100644 website/docs/r/data_fusion_instance_iam.html.markdown diff --git a/.changelog/6270.txt b/.changelog/6270.txt new file mode 100644 index 0000000000..00f7647ac8 --- /dev/null +++ b/.changelog/6270.txt @@ -0,0 +1,15 @@ +```release-note:enhancement +datafusion: add `zone`, `display_name`, `crypto_key_config`, `event_publish_config`, and `enable_rbac` args to `google_data_fusion_instance` +``` +```release-note:enhancement +datafusion: add `api_endpoint` and `p4_service_account ` attributes to `google_data_fusion_instance` +``` +```release-note:new-resource +google_data_fusion_instance_iam_member +``` +```release-note:new-resource +google_data_fusion_instance_iam_binding +``` +```release-note:new-resource +google_data_fusion_instance_iam_policy +``` diff --git a/google-beta/iam_data_fusion_instance.go b/google-beta/iam_data_fusion_instance.go new file mode 100644 index 0000000000..ab466fec3b --- /dev/null +++ b/google-beta/iam_data_fusion_instance.go @@ -0,0 +1,223 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + + "github.com/hashicorp/errwrap" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "google.golang.org/api/cloudresourcemanager/v1" +) + +var DataFusionInstanceIamSchema = map[string]*schema.Schema{ + "project": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + }, + "region": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + }, +} + +type DataFusionInstanceIamUpdater struct { + project string + region string + name string + d TerraformResourceData + Config *Config +} + +func DataFusionInstanceIamUpdaterProducer(d TerraformResourceData, config *Config) (ResourceIamUpdater, error) { + values := make(map[string]string) + + project, _ := getProject(d, config) + if project != "" { + if err := d.Set("project", project); err != nil { + return nil, fmt.Errorf("Error setting project: %s", err) + } + } + values["project"] = project + region, _ := getRegion(d, config) + if region != "" { + if err := d.Set("region", region); err != nil { + return nil, fmt.Errorf("Error setting region: %s", err) + } + } + values["region"] = region + if v, ok := d.GetOk("name"); ok { + values["name"] = v.(string) + } + + // We may have gotten either a long or short name, so attempt to parse long name if possible + m, err := getImportIdQualifiers([]string{"projects/(?P[^/]+)/locations/(?P[^/]+)/instances/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)"}, d, config, d.Get("name").(string)) + if err != nil { + return nil, err + } + + for k, v := range m { + values[k] = v + } + + u := &DataFusionInstanceIamUpdater{ + project: values["project"], + region: values["region"], + name: values["name"], + d: d, + Config: config, + } + + if err := d.Set("project", u.project); err != nil { + return nil, fmt.Errorf("Error setting project: %s", err) + } + if err := d.Set("region", u.region); err != nil { + return nil, fmt.Errorf("Error setting region: %s", err) + } + if err := d.Set("name", u.GetResourceId()); err != nil { + return nil, fmt.Errorf("Error setting name: %s", err) + } + + return u, nil +} + +func DataFusionInstanceIdParseFunc(d *schema.ResourceData, config *Config) error { + values := make(map[string]string) + + project, _ := getProject(d, config) + if project != "" { + values["project"] = project + } + + region, _ := getRegion(d, config) + if region != "" { + values["region"] = region + } + + m, err := getImportIdQualifiers([]string{"projects/(?P[^/]+)/locations/(?P[^/]+)/instances/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)"}, d, config, d.Id()) + if err != nil { + return err + } + + for k, v := range m { + values[k] = v + } + + u := &DataFusionInstanceIamUpdater{ + project: values["project"], + region: values["region"], + name: values["name"], + d: d, + Config: config, + } + if err := d.Set("name", u.GetResourceId()); err != nil { + return fmt.Errorf("Error setting name: %s", err) + } + d.SetId(u.GetResourceId()) + return nil +} + +func (u *DataFusionInstanceIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) { + url, err := u.qualifyInstanceUrl("getIamPolicy") + if err != nil { + return nil, err + } + + project, err := getProject(u.d, u.Config) + if err != nil { + return nil, err + } + var obj map[string]interface{} + + userAgent, err := generateUserAgentString(u.d, u.Config.userAgent) + if err != nil { + return nil, err + } + + policy, err := sendRequest(u.Config, "GET", project, url, userAgent, obj) + if err != nil { + return nil, errwrap.Wrapf(fmt.Sprintf("Error retrieving IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + out := &cloudresourcemanager.Policy{} + err = Convert(policy, out) + if err != nil { + return nil, errwrap.Wrapf("Cannot convert a policy to a resource manager policy: {{err}}", err) + } + + return out, nil +} + +func (u *DataFusionInstanceIamUpdater) SetResourceIamPolicy(policy *cloudresourcemanager.Policy) error { + json, err := ConvertToMap(policy) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + obj["policy"] = json + + url, err := u.qualifyInstanceUrl("setIamPolicy") + if err != nil { + return err + } + project, err := getProject(u.d, u.Config) + if err != nil { + return err + } + + userAgent, err := generateUserAgentString(u.d, u.Config.userAgent) + if err != nil { + return err + } + + _, err = sendRequestWithTimeout(u.Config, "POST", project, url, userAgent, obj, u.d.Timeout(schema.TimeoutCreate)) + if err != nil { + return errwrap.Wrapf(fmt.Sprintf("Error setting IAM policy for %s: {{err}}", u.DescribeResource()), err) + } + + return nil +} + +func (u *DataFusionInstanceIamUpdater) qualifyInstanceUrl(methodIdentifier string) (string, error) { + urlTemplate := fmt.Sprintf("{{DataFusionBasePath}}%s:%s", fmt.Sprintf("projects/%s/locations/%s/instances/%s", u.project, u.region, u.name), methodIdentifier) + url, err := replaceVars(u.d, u.Config, urlTemplate) + if err != nil { + return "", err + } + return url, nil +} + +func (u *DataFusionInstanceIamUpdater) GetResourceId() string { + return fmt.Sprintf("projects/%s/locations/%s/instances/%s", u.project, u.region, u.name) +} + +func (u *DataFusionInstanceIamUpdater) GetMutexKey() string { + return fmt.Sprintf("iam-datafusion-instance-%s", u.GetResourceId()) +} + +func (u *DataFusionInstanceIamUpdater) DescribeResource() string { + return fmt.Sprintf("datafusion instance %q", u.GetResourceId()) +} diff --git a/google-beta/iam_data_fusion_instance_generated_test.go b/google-beta/iam_data_fusion_instance_generated_test.go new file mode 100644 index 0000000000..850190bb05 --- /dev/null +++ b/google-beta/iam_data_fusion_instance_generated_test.go @@ -0,0 +1,235 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: MMv1 *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataFusionInstanceIamBindingGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + "role": "roles/viewer", + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataFusionInstanceIamBinding_basicGenerated(context), + }, + { + ResourceName: "google_data_fusion_instance_iam_binding.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/%s/instances/%s roles/viewer", getTestProjectFromEnv(), getTestRegionFromEnv(), fmt.Sprintf("tf-test-my-instance%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + { + // Test Iam Binding update + Config: testAccDataFusionInstanceIamBinding_updateGenerated(context), + }, + { + ResourceName: "google_data_fusion_instance_iam_binding.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/%s/instances/%s roles/viewer", getTestProjectFromEnv(), getTestRegionFromEnv(), fmt.Sprintf("tf-test-my-instance%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccDataFusionInstanceIamMemberGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + "role": "roles/viewer", + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + // Test Iam Member creation (no update for member, no need to test) + Config: testAccDataFusionInstanceIamMember_basicGenerated(context), + }, + { + ResourceName: "google_data_fusion_instance_iam_member.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/%s/instances/%s roles/viewer user:admin@hashicorptest.com", getTestProjectFromEnv(), getTestRegionFromEnv(), fmt.Sprintf("tf-test-my-instance%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccDataFusionInstanceIamPolicyGenerated(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + "role": "roles/viewer", + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccDataFusionInstanceIamPolicy_basicGenerated(context), + }, + { + ResourceName: "google_data_fusion_instance_iam_policy.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/%s/instances/%s", getTestProjectFromEnv(), getTestRegionFromEnv(), fmt.Sprintf("tf-test-my-instance%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccDataFusionInstanceIamPolicy_emptyBinding(context), + }, + { + ResourceName: "google_data_fusion_instance_iam_policy.foo", + ImportStateId: fmt.Sprintf("projects/%s/locations/%s/instances/%s", getTestProjectFromEnv(), getTestRegionFromEnv(), fmt.Sprintf("tf-test-my-instance%s", context["random_suffix"])), + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccDataFusionInstanceIamMember_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_data_fusion_instance" "basic_instance" { + name = "tf-test-my-instance%{random_suffix}" + region = "us-central1" + type = "BASIC" + # Mark for testing to avoid service networking connection usage that is not cleaned up + options = { + prober_test_run = "true" + } +} + +resource "google_data_fusion_instance_iam_member" "foo" { + project = google_data_fusion_instance.basic_instance.project + region = google_data_fusion_instance.basic_instance.region + name = google_data_fusion_instance.basic_instance.name + role = "%{role}" + member = "user:admin@hashicorptest.com" +} +`, context) +} + +func testAccDataFusionInstanceIamPolicy_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_data_fusion_instance" "basic_instance" { + name = "tf-test-my-instance%{random_suffix}" + region = "us-central1" + type = "BASIC" + # Mark for testing to avoid service networking connection usage that is not cleaned up + options = { + prober_test_run = "true" + } +} + +data "google_iam_policy" "foo" { + binding { + role = "%{role}" + members = ["user:admin@hashicorptest.com"] + } +} + +resource "google_data_fusion_instance_iam_policy" "foo" { + project = google_data_fusion_instance.basic_instance.project + region = google_data_fusion_instance.basic_instance.region + name = google_data_fusion_instance.basic_instance.name + policy_data = data.google_iam_policy.foo.policy_data +} +`, context) +} + +func testAccDataFusionInstanceIamPolicy_emptyBinding(context map[string]interface{}) string { + return Nprintf(` +resource "google_data_fusion_instance" "basic_instance" { + name = "tf-test-my-instance%{random_suffix}" + region = "us-central1" + type = "BASIC" + # Mark for testing to avoid service networking connection usage that is not cleaned up + options = { + prober_test_run = "true" + } +} + +data "google_iam_policy" "foo" { +} + +resource "google_data_fusion_instance_iam_policy" "foo" { + project = google_data_fusion_instance.basic_instance.project + region = google_data_fusion_instance.basic_instance.region + name = google_data_fusion_instance.basic_instance.name + policy_data = data.google_iam_policy.foo.policy_data +} +`, context) +} + +func testAccDataFusionInstanceIamBinding_basicGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_data_fusion_instance" "basic_instance" { + name = "tf-test-my-instance%{random_suffix}" + region = "us-central1" + type = "BASIC" + # Mark for testing to avoid service networking connection usage that is not cleaned up + options = { + prober_test_run = "true" + } +} + +resource "google_data_fusion_instance_iam_binding" "foo" { + project = google_data_fusion_instance.basic_instance.project + region = google_data_fusion_instance.basic_instance.region + name = google_data_fusion_instance.basic_instance.name + role = "%{role}" + members = ["user:admin@hashicorptest.com"] +} +`, context) +} + +func testAccDataFusionInstanceIamBinding_updateGenerated(context map[string]interface{}) string { + return Nprintf(` +resource "google_data_fusion_instance" "basic_instance" { + name = "tf-test-my-instance%{random_suffix}" + region = "us-central1" + type = "BASIC" + # Mark for testing to avoid service networking connection usage that is not cleaned up + options = { + prober_test_run = "true" + } +} + +resource "google_data_fusion_instance_iam_binding" "foo" { + project = google_data_fusion_instance.basic_instance.project + region = google_data_fusion_instance.basic_instance.region + name = google_data_fusion_instance.basic_instance.name + role = "%{role}" + members = ["user:admin@hashicorptest.com", "user:gterraformtest1@gmail.com"] +} +`, context) +} diff --git a/google-beta/provider.go b/google-beta/provider.go index 18a1d53a76..ecfc8f833e 100644 --- a/google-beta/provider.go +++ b/google-beta/provider.go @@ -1032,8 +1032,8 @@ func Provider() *schema.Provider { } // Generated resources: 285 -// Generated IAM resources: 186 -// Total generated resources: 471 +// Generated IAM resources: 189 +// Total generated resources: 474 func ResourceMap() map[string]*schema.Resource { resourceMap, _ := ResourceMapWithErrors() return resourceMap @@ -1282,6 +1282,9 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_data_catalog_policy_tag_iam_policy": ResourceIamPolicy(DataCatalogPolicyTagIamSchema, DataCatalogPolicyTagIamUpdaterProducer, DataCatalogPolicyTagIdParseFunc), "google_dataform_repository": resourceDataformRepository(), "google_data_fusion_instance": resourceDataFusionInstance(), + "google_data_fusion_instance_iam_binding": ResourceIamBinding(DataFusionInstanceIamSchema, DataFusionInstanceIamUpdaterProducer, DataFusionInstanceIdParseFunc), + "google_data_fusion_instance_iam_member": ResourceIamMember(DataFusionInstanceIamSchema, DataFusionInstanceIamUpdaterProducer, DataFusionInstanceIdParseFunc), + "google_data_fusion_instance_iam_policy": ResourceIamPolicy(DataFusionInstanceIamSchema, DataFusionInstanceIamUpdaterProducer, DataFusionInstanceIdParseFunc), "google_data_loss_prevention_job_trigger": resourceDataLossPreventionJobTrigger(), "google_data_loss_prevention_inspect_template": resourceDataLossPreventionInspectTemplate(), "google_data_loss_prevention_stored_info_type": resourceDataLossPreventionStoredInfoType(), diff --git a/google-beta/resource_data_fusion_instance.go b/google-beta/resource_data_fusion_instance.go index ffb587309f..2dabae8833 100644 --- a/google-beta/resource_data_fusion_instance.go +++ b/google-beta/resource_data_fusion_instance.go @@ -36,7 +36,7 @@ func resourceDataFusionInstance() *schema.Resource { }, Timeouts: &schema.ResourceTimeout{ - Create: schema.DefaultTimeout(60 * time.Minute), + Create: schema.DefaultTimeout(90 * time.Minute), Update: schema.DefaultTimeout(25 * time.Minute), Delete: schema.DefaultTimeout(50 * time.Minute), }, @@ -93,6 +93,12 @@ pipelines at low cost. Possible values: ["BASIC", "ENTERPRISE", "DEVELOPER"]`, ForceNew: true, Description: `An optional description of the instance.`, }, + "display_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: `Display name for an instance.`, + }, "enable_rbac": { Type: schema.TypeBool, Optional: true, @@ -108,6 +114,27 @@ pipelines at low cost. Possible values: ["BASIC", "ENTERPRISE", "DEVELOPER"]`, Optional: true, Description: `Option to enable Stackdriver Monitoring.`, }, + "event_publish_config": { + Type: schema.TypeList, + Optional: true, + Description: `Option to enable and pass metadata for event publishing.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Required: true, + Description: `Option to enable Event Publishing.`, + }, + "topic": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The resource name of the Pub/Sub topic. Format: projects/{projectId}/topics/{topic_id}`, + }, + }, + }, + }, "labels": { Type: schema.TypeMap, Optional: true, @@ -170,6 +197,18 @@ able to access the public internet.`, ForceNew: true, Description: `Current version of the Data Fusion.`, }, + "zone": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + Description: `Name of the zone in which the Data Fusion instance will be created. Only DEVELOPER instances use this field.`, + }, + "api_endpoint": { + Type: schema.TypeString, + Computed: true, + Description: `Endpoint on which the REST APIs is accessible.`, + }, "create_time": { Type: schema.TypeString, Computed: true, @@ -180,6 +219,11 @@ able to access the public internet.`, Computed: true, Description: `Cloud Storage bucket generated by Data Fusion in the customer project.`, }, + "p4_service_account": { + Type: schema.TypeString, + Computed: true, + Description: `P4 service account for the customer project.`, + }, "service_account": { Type: schema.TypeString, Computed: true, @@ -308,12 +352,30 @@ func resourceDataFusionInstanceCreate(d *schema.ResourceData, meta interface{}) } else if v, ok := d.GetOkExists("network_config"); !isEmptyValue(reflect.ValueOf(networkConfigProp)) && (ok || !reflect.DeepEqual(v, networkConfigProp)) { obj["networkConfig"] = networkConfigProp } + zoneProp, err := expandDataFusionInstanceZone(d.Get("zone"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("zone"); !isEmptyValue(reflect.ValueOf(zoneProp)) && (ok || !reflect.DeepEqual(v, zoneProp)) { + obj["zone"] = zoneProp + } + displayNameProp, err := expandDataFusionInstanceDisplayName(d.Get("display_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("display_name"); !isEmptyValue(reflect.ValueOf(displayNameProp)) && (ok || !reflect.DeepEqual(v, displayNameProp)) { + obj["displayName"] = displayNameProp + } cryptoKeyConfigProp, err := expandDataFusionInstanceCryptoKeyConfig(d.Get("crypto_key_config"), d, config) if err != nil { return err } else if v, ok := d.GetOkExists("crypto_key_config"); !isEmptyValue(reflect.ValueOf(cryptoKeyConfigProp)) && (ok || !reflect.DeepEqual(v, cryptoKeyConfigProp)) { obj["cryptoKeyConfig"] = cryptoKeyConfigProp } + eventPublishConfigProp, err := expandDataFusionInstanceEventPublishConfig(d.Get("event_publish_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("event_publish_config"); !isEmptyValue(reflect.ValueOf(eventPublishConfigProp)) && (ok || !reflect.DeepEqual(v, eventPublishConfigProp)) { + obj["eventPublishConfig"] = eventPublishConfigProp + } url, err := replaceVars(d, config, "{{DataFusionBasePath}}projects/{{project}}/locations/{{region}}/instances?instanceId={{name}}") if err != nil { @@ -477,9 +539,24 @@ func resourceDataFusionInstanceRead(d *schema.ResourceData, meta interface{}) er if err := d.Set("network_config", flattenDataFusionInstanceNetworkConfig(res["networkConfig"], d, config)); err != nil { return fmt.Errorf("Error reading Instance: %s", err) } + if err := d.Set("zone", flattenDataFusionInstanceZone(res["zone"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("display_name", flattenDataFusionInstanceDisplayName(res["displayName"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("api_endpoint", flattenDataFusionInstanceApiEndpoint(res["apiEndpoint"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("p4_service_account", flattenDataFusionInstanceP4ServiceAccount(res["p4ServiceAccount"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } if err := d.Set("crypto_key_config", flattenDataFusionInstanceCryptoKeyConfig(res["cryptoKeyConfig"], d, config)); err != nil { return fmt.Errorf("Error reading Instance: %s", err) } + if err := d.Set("event_publish_config", flattenDataFusionInstanceEventPublishConfig(res["eventPublishConfig"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } return nil } @@ -524,6 +601,12 @@ func resourceDataFusionInstanceUpdate(d *schema.ResourceData, meta interface{}) } else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, labelsProp)) { obj["labels"] = labelsProp } + eventPublishConfigProp, err := expandDataFusionInstanceEventPublishConfig(d.Get("event_publish_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("event_publish_config"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, eventPublishConfigProp)) { + obj["eventPublishConfig"] = eventPublishConfigProp + } url, err := replaceVars(d, config, "{{DataFusionBasePath}}projects/{{project}}/locations/{{region}}/instances/{{name}}") if err != nil { @@ -745,6 +828,22 @@ func flattenDataFusionInstanceNetworkConfigNetwork(v interface{}, d *schema.Reso return v } +func flattenDataFusionInstanceZone(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenDataFusionInstanceDisplayName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenDataFusionInstanceApiEndpoint(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenDataFusionInstanceP4ServiceAccount(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + func flattenDataFusionInstanceCryptoKeyConfig(v interface{}, d *schema.ResourceData, config *Config) interface{} { if v == nil { return nil @@ -762,6 +861,29 @@ func flattenDataFusionInstanceCryptoKeyConfigKeyReference(v interface{}, d *sche return v } +func flattenDataFusionInstanceEventPublishConfig(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["enabled"] = + flattenDataFusionInstanceEventPublishConfigEnabled(original["enabled"], d, config) + transformed["topic"] = + flattenDataFusionInstanceEventPublishConfigTopic(original["topic"], d, config) + return []interface{}{transformed} +} +func flattenDataFusionInstanceEventPublishConfigEnabled(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenDataFusionInstanceEventPublishConfigTopic(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + func expandDataFusionInstanceName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return replaceVars(d, config, "projects/{{project}}/locations/{{region}}/instances/{{name}}") } @@ -854,6 +976,14 @@ func expandDataFusionInstanceNetworkConfigNetwork(v interface{}, d TerraformReso return v, nil } +func expandDataFusionInstanceZone(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandDataFusionInstanceDisplayName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandDataFusionInstanceCryptoKeyConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { l := v.([]interface{}) if len(l) == 0 || l[0] == nil { @@ -876,3 +1006,37 @@ func expandDataFusionInstanceCryptoKeyConfig(v interface{}, d TerraformResourceD func expandDataFusionInstanceCryptoKeyConfigKeyReference(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } + +func expandDataFusionInstanceEventPublishConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + if len(l) == 0 || l[0] == nil { + return nil, nil + } + raw := l[0] + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedEnabled, err := expandDataFusionInstanceEventPublishConfigEnabled(original["enabled"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEnabled); val.IsValid() && !isEmptyValue(val) { + transformed["enabled"] = transformedEnabled + } + + transformedTopic, err := expandDataFusionInstanceEventPublishConfigTopic(original["topic"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTopic); val.IsValid() && !isEmptyValue(val) { + transformed["topic"] = transformedTopic + } + + return transformed, nil +} + +func expandDataFusionInstanceEventPublishConfigEnabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandDataFusionInstanceEventPublishConfigTopic(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} diff --git a/google-beta/resource_data_fusion_instance_generated_test.go b/google-beta/resource_data_fusion_instance_generated_test.go index 1591810007..3842604c85 100644 --- a/google-beta/resource_data_fusion_instance_generated_test.go +++ b/google-beta/resource_data_fusion_instance_generated_test.go @@ -51,9 +51,9 @@ func TestAccDataFusionInstance_dataFusionInstanceBasicExample(t *testing.T) { func testAccDataFusionInstance_dataFusionInstanceBasicExample(context map[string]interface{}) string { return Nprintf(` resource "google_data_fusion_instance" "basic_instance" { - name = "tf-test-my-instance%{random_suffix}" + name = "tf-test-my-instance%{random_suffix}" region = "us-central1" - type = "BASIC" + type = "BASIC" # Mark for testing to avoid service networking connection usage that is not cleaned up options = { prober_test_run = "true" @@ -90,22 +90,26 @@ func TestAccDataFusionInstance_dataFusionInstanceFullExample(t *testing.T) { func testAccDataFusionInstance_dataFusionInstanceFullExample(context map[string]interface{}) string { return Nprintf(` resource "google_data_fusion_instance" "extended_instance" { - name = "tf-test-my-instance%{random_suffix}" - description = "My Data Fusion instance" - region = "us-central1" - type = "BASIC" - enable_stackdriver_logging = true + name = "tf-test-my-instance%{random_suffix}" + description = "My Data Fusion instance" + display_name = "My Data Fusion instance" + region = "us-central1" + type = "BASIC" + enable_stackdriver_logging = true enable_stackdriver_monitoring = true + private_instance = true + version = "6.6.0" + dataproc_service_account = data.google_app_engine_default_service_account.default.email + labels = { example_key = "example_value" } - private_instance = true + network_config { - network = "default" - ip_allocation = "10.89.48.0/22" + network = "default" + ip_allocation = "${google_compute_global_address.private_ip_alloc.address}/${google_compute_global_address.private_ip_alloc.prefix_length}" } - version = "6.3.0" - dataproc_service_account = data.google_app_engine_default_service_account.default.email + # Mark for testing to avoid service networking connection usage that is not cleaned up options = { prober_test_run = "true" @@ -114,6 +118,18 @@ resource "google_data_fusion_instance" "extended_instance" { data "google_app_engine_default_service_account" "default" { } + +resource "google_compute_network" "network" { + name = "tf-test-datafusion-full-network%{random_suffix}" +} + +resource "google_compute_global_address" "private_ip_alloc" { + name = "tf-test-datafusion-ip-alloc%{random_suffix}" + address_type = "INTERNAL" + purpose = "VPC_PEERING" + prefix_length = 22 + network = google_compute_network.network.id +} `, context) } @@ -133,7 +149,7 @@ func TestAccDataFusionInstance_dataFusionInstanceCmekExample(t *testing.T) { Config: testAccDataFusionInstance_dataFusionInstanceCmekExample(context), }, { - ResourceName: "google_data_fusion_instance.basic_cmek", + ResourceName: "google_data_fusion_instance.cmek", ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{"region"}, @@ -144,7 +160,7 @@ func TestAccDataFusionInstance_dataFusionInstanceCmekExample(t *testing.T) { func testAccDataFusionInstance_dataFusionInstanceCmekExample(context map[string]interface{}) string { return Nprintf(` -resource "google_data_fusion_instance" "basic_cmek" { +resource "google_data_fusion_instance" "cmek" { name = "tf-test-my-instance%{random_suffix}" region = "us-central1" type = "BASIC" @@ -219,6 +235,87 @@ resource "google_data_fusion_instance" "enterprise_instance" { `, context) } +func TestAccDataFusionInstance_dataFusionInstanceEventExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDataFusionInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccDataFusionInstance_dataFusionInstanceEventExample(context), + }, + { + ResourceName: "google_data_fusion_instance.event", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"region"}, + }, + }, + }) +} + +func testAccDataFusionInstance_dataFusionInstanceEventExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_data_fusion_instance" "event" { + name = "tf-test-my-instance%{random_suffix}" + region = "us-central1" + type = "BASIC" + version = "6.7.0" + + event_publish_config { + enabled = true + topic = google_pubsub_topic.event.id + } +} + +resource "google_pubsub_topic" "event" { + name = "tf-test-my-instance%{random_suffix}" +} +`, context) +} + +func TestAccDataFusionInstance_dataFusionInstanceZoneExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDataFusionInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccDataFusionInstance_dataFusionInstanceZoneExample(context), + }, + { + ResourceName: "google_data_fusion_instance.zone", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"region"}, + }, + }, + }) +} + +func testAccDataFusionInstance_dataFusionInstanceZoneExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_data_fusion_instance" "zone" { + name = "tf-test-my-instance%{random_suffix}" + region = "us-central1" + zone = "us-central1-a" + type = "DEVELOPER" +} +`, context) +} + func testAccCheckDataFusionInstanceDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { for name, rs := range s.RootModule().Resources { diff --git a/website/docs/r/data_fusion_instance.html.markdown b/website/docs/r/data_fusion_instance.html.markdown index 340a8370aa..572948bd3c 100644 --- a/website/docs/r/data_fusion_instance.html.markdown +++ b/website/docs/r/data_fusion_instance.html.markdown @@ -39,9 +39,9 @@ To get more information about Instance, see: ```hcl resource "google_data_fusion_instance" "basic_instance" { - name = "my-instance" + name = "my-instance" region = "us-central1" - type = "BASIC" + type = "BASIC" # Mark for testing to avoid service networking connection usage that is not cleaned up options = { prober_test_run = "true" @@ -58,22 +58,26 @@ resource "google_data_fusion_instance" "basic_instance" { ```hcl resource "google_data_fusion_instance" "extended_instance" { - name = "my-instance" - description = "My Data Fusion instance" - region = "us-central1" - type = "BASIC" - enable_stackdriver_logging = true + name = "my-instance" + description = "My Data Fusion instance" + display_name = "My Data Fusion instance" + region = "us-central1" + type = "BASIC" + enable_stackdriver_logging = true enable_stackdriver_monitoring = true + private_instance = true + version = "6.6.0" + dataproc_service_account = data.google_app_engine_default_service_account.default.email + labels = { example_key = "example_value" } - private_instance = true + network_config { - network = "default" - ip_allocation = "10.89.48.0/22" + network = "default" + ip_allocation = "${google_compute_global_address.private_ip_alloc.address}/${google_compute_global_address.private_ip_alloc.prefix_length}" } - version = "6.3.0" - dataproc_service_account = data.google_app_engine_default_service_account.default.email + # Mark for testing to avoid service networking connection usage that is not cleaned up options = { prober_test_run = "true" @@ -82,6 +86,18 @@ resource "google_data_fusion_instance" "extended_instance" { data "google_app_engine_default_service_account" "default" { } + +resource "google_compute_network" "network" { + name = "datafusion-full-network" +} + +resource "google_compute_global_address" "private_ip_alloc" { + name = "datafusion-ip-alloc" + address_type = "INTERNAL" + purpose = "VPC_PEERING" + prefix_length = 22 + network = google_compute_network.network.id +} ```
@@ -92,7 +108,7 @@ data "google_app_engine_default_service_account" "default" { ```hcl -resource "google_data_fusion_instance" "basic_cmek" { +resource "google_data_fusion_instance" "cmek" { name = "my-instance" region = "us-central1" type = "BASIC" @@ -145,6 +161,47 @@ resource "google_data_fusion_instance" "enterprise_instance" { } } ``` + +## Example Usage - Data Fusion Instance Event + + +```hcl +resource "google_data_fusion_instance" "event" { + name = "my-instance" + region = "us-central1" + type = "BASIC" + version = "6.7.0" + + event_publish_config { + enabled = true + topic = google_pubsub_topic.event.id + } +} + +resource "google_pubsub_topic" "event" { + name = "my-instance" +} +``` + +## Example Usage - Data Fusion Instance Zone + + +```hcl +resource "google_data_fusion_instance" "zone" { + name = "my-instance" + region = "us-central1" + zone = "us-central1-a" + type = "DEVELOPER" +} +``` ## Argument Reference @@ -217,11 +274,24 @@ The following arguments are supported: Network configuration options. These are required when a private Data Fusion instance is to be created. Structure is [documented below](#nested_network_config). +* `zone` - + (Optional) + Name of the zone in which the Data Fusion instance will be created. Only DEVELOPER instances use this field. + +* `display_name` - + (Optional) + Display name for an instance. + * `crypto_key_config` - (Optional) The crypto key configuration. This field is used by the Customer-Managed Encryption Keys (CMEK) feature. Structure is [documented below](#nested_crypto_key_config). +* `event_publish_config` - + (Optional) + Option to enable and pass metadata for event publishing. + Structure is [documented below](#nested_event_publish_config). + * `region` - (Optional) The region of the Data Fusion instance. @@ -249,6 +319,16 @@ The following arguments are supported: (Required) The name of the key which is used to encrypt/decrypt customer data. For key in Cloud KMS, the key should be in the format of projects/*/locations/*/keyRings/*/cryptoKeys/*. +The `event_publish_config` block supports: + +* `enabled` - + (Required) + Option to enable Event Publishing. + +* `topic` - + (Required) + The resource name of the Pub/Sub topic. Format: projects/{projectId}/topics/{topic_id} + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are exported: @@ -286,13 +366,19 @@ In addition to the arguments listed above, the following computed attributes are * `gcs_bucket` - Cloud Storage bucket generated by Data Fusion in the customer project. +* `api_endpoint` - + Endpoint on which the REST APIs is accessible. + +* `p4_service_account` - + P4 service account for the customer project. + ## Timeouts This resource provides the following [Timeouts](/docs/configuration/resources.html#timeouts) configuration options: -- `create` - Default is 60 minutes. +- `create` - Default is 90 minutes. - `update` - Default is 25 minutes. - `delete` - Default is 50 minutes. diff --git a/website/docs/r/data_fusion_instance_iam.html.markdown b/website/docs/r/data_fusion_instance_iam.html.markdown new file mode 100644 index 0000000000..84ec5cc224 --- /dev/null +++ b/website/docs/r/data_fusion_instance_iam.html.markdown @@ -0,0 +1,153 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** Type: MMv1 *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in +# .github/CONTRIBUTING.md. +# +# ---------------------------------------------------------------------------- +subcategory: "Cloud Data Fusion" +page_title: "Google: google_data_fusion_instance_iam" +description: |- + Collection of resources to manage IAM policy for Cloud Data Fusion Instance +--- + +# IAM policy for Cloud Data Fusion Instance +Three different resources help you manage your IAM policy for Cloud Data Fusion Instance. Each of these resources serves a different use case: + +* `google_data_fusion_instance_iam_policy`: Authoritative. Sets the IAM policy for the instance and replaces any existing policy already attached. +* `google_data_fusion_instance_iam_binding`: Authoritative for a given role. Updates the IAM policy to grant a role to a list of members. Other roles within the IAM policy for the instance are preserved. +* `google_data_fusion_instance_iam_member`: Non-authoritative. Updates the IAM policy to grant a role to a new member. Other members for the role for the instance are preserved. + +~> **Note:** `google_data_fusion_instance_iam_policy` **cannot** be used in conjunction with `google_data_fusion_instance_iam_binding` and `google_data_fusion_instance_iam_member` or they will fight over what your policy should be. + +~> **Note:** `google_data_fusion_instance_iam_binding` resources **can be** used in conjunction with `google_data_fusion_instance_iam_member` resources **only if** they do not grant privilege to the same role. + + + + +## google\_data\_fusion\_instance\_iam\_policy + +```hcl +data "google_iam_policy" "admin" { + binding { + role = "roles/viewer" + members = [ + "user:jane@example.com", + ] + } +} + +resource "google_data_fusion_instance_iam_policy" "policy" { + project = google_data_fusion_instance.basic_instance.project + region = google_data_fusion_instance.basic_instance.region + name = google_data_fusion_instance.basic_instance.name + policy_data = data.google_iam_policy.admin.policy_data +} +``` + +## google\_data\_fusion\_instance\_iam\_binding + +```hcl +resource "google_data_fusion_instance_iam_binding" "binding" { + project = google_data_fusion_instance.basic_instance.project + region = google_data_fusion_instance.basic_instance.region + name = google_data_fusion_instance.basic_instance.name + role = "roles/viewer" + members = [ + "user:jane@example.com", + ] +} +``` + +## google\_data\_fusion\_instance\_iam\_member + +```hcl +resource "google_data_fusion_instance_iam_member" "member" { + project = google_data_fusion_instance.basic_instance.project + region = google_data_fusion_instance.basic_instance.region + name = google_data_fusion_instance.basic_instance.name + role = "roles/viewer" + member = "user:jane@example.com" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) Used to find the parent resource to bind the IAM policy to +* `region` - (Optional) The region of the Data Fusion instance. + Used to find the parent resource to bind the IAM policy to. If not specified, + the value will be parsed from the identifier of the parent resource. If no region is provided in the parent identifier and no + region is specified, it is taken from the provider configuration. + +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the project will be parsed from the identifier of the parent resource. If no project is provided in the parent identifier and no project is specified, the provider project is used. + +* `member/members` - (Required) Identities that will be granted the privilege in `role`. + Each entry can have one of the following values: + * **allUsers**: A special identifier that represents anyone who is on the internet; with or without a Google account. + * **allAuthenticatedUsers**: A special identifier that represents anyone who is authenticated with a Google account or a service account. + * **user:{emailid}**: An email address that represents a specific Google account. For example, alice@gmail.com or joe@example.com. + * **serviceAccount:{emailid}**: An email address that represents a service account. For example, my-other-app@appspot.gserviceaccount.com. + * **group:{emailid}**: An email address that represents a Google group. For example, admins@example.com. + * **domain:{domain}**: A G Suite domain (primary, instead of alias) name that represents all the users of that domain. For example, google.com or example.com. + * **projectOwner:projectid**: Owners of the given project. For example, "projectOwner:my-example-project" + * **projectEditor:projectid**: Editors of the given project. For example, "projectEditor:my-example-project" + * **projectViewer:projectid**: Viewers of the given project. For example, "projectViewer:my-example-project" + +* `role` - (Required) The role that should be applied. Only one + `google_data_fusion_instance_iam_binding` can be used per role. Note that custom roles must be of the format + `[projects|organizations]/{parent-name}/roles/{role-name}`. + +* `policy_data` - (Required only by `google_data_fusion_instance_iam_policy`) The policy data generated by + a `google_iam_policy` data source. + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are +exported: + +* `etag` - (Computed) The etag of the IAM policy. + +## Import + +For all import syntaxes, the "resource in question" can take any of the following forms: + +* projects/{{project}}/locations/{{location}}/instances/{{name}} +* {{project}}/{{location}}/{{name}} +* {{location}}/{{name}} +* {{name}} + +Any variables not passed in the import command will be taken from the provider configuration. + +Cloud Data Fusion instance IAM resources can be imported using the resource identifiers, role, and member. + +IAM member imports use space-delimited identifiers: the resource in question, the role, and the member identity, e.g. +``` +$ terraform import google_data_fusion_instance_iam_member.editor "projects/{{project}}/locations/{{location}}/instances/{{instance}} roles/viewer user:jane@example.com" +``` + +IAM binding imports use space-delimited identifiers: the resource in question and the role, e.g. +``` +$ terraform import google_data_fusion_instance_iam_binding.editor "projects/{{project}}/locations/{{location}}/instances/{{instance}} roles/viewer" +``` + +IAM policy imports use the identifier of the resource in question, e.g. +``` +$ terraform import google_data_fusion_instance_iam_policy.editor projects/{{project}}/locations/{{location}}/instances/{{instance}} +``` + +-> **Custom Roles**: If you're importing a IAM resource with a custom role, make sure to use the + full name of the custom role, e.g. `[projects/my-project|organizations/my-org]/roles/my-custom-role`. + +## User Project Overrides + +This resource supports [User Project Overrides](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/provider_reference#user_project_override).