diff --git a/.changelog/6529.txt b/.changelog/6529.txt new file mode 100644 index 00000000000..076d7928083 --- /dev/null +++ b/.changelog/6529.txt @@ -0,0 +1,6 @@ +```release-note:new-resource +google_eventarc_channel +``` +```release-note:enhancement +google_eventarc_trigger: Added field `channel` to `google_eventarc_trigger` +``` diff --git a/google/provider_dcl_resources.go b/google/provider_dcl_resources.go index 4fb2bf356fb..4738364a9bb 100644 --- a/google/provider_dcl_resources.go +++ b/google/provider_dcl_resources.go @@ -42,6 +42,7 @@ var dclResources = map[string]*schema.Resource{ "google_dataplex_lake": resourceDataplexLake(), "google_dataplex_zone": resourceDataplexZone(), "google_dataproc_workflow_template": resourceDataprocWorkflowTemplate(), + "google_eventarc_channel": resourceEventarcChannel(), "google_eventarc_trigger": resourceEventarcTrigger(), "google_firebaserules_release": resourceFirebaserulesRelease(), "google_firebaserules_ruleset": resourceFirebaserulesRuleset(), diff --git a/google/resource_eventarc_channel.go b/google/resource_eventarc_channel.go new file mode 100644 index 00000000000..c94ba827756 --- /dev/null +++ b/google/resource_eventarc_channel.go @@ -0,0 +1,355 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: DCL *** +// +// ---------------------------------------------------------------------------- +// +// This file is managed by Magic Modules (https://github.com/GoogleCloudPlatform/magic-modules) +// and is based on the DCL (https://github.com/GoogleCloudPlatform/declarative-resource-client-library). +// Changes will need to be made to the DCL or Magic Modules instead of here. +// +// We are not currently able to accept contributions to this file. If changes +// are required, please file an issue at https://github.com/hashicorp/terraform-provider-google/issues/new/choose +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + dcl "github.com/GoogleCloudPlatform/declarative-resource-client-library/dcl" + eventarc "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/eventarc" +) + +func resourceEventarcChannel() *schema.Resource { + return &schema.Resource{ + Create: resourceEventarcChannelCreate, + Read: resourceEventarcChannelRead, + Update: resourceEventarcChannelUpdate, + Delete: resourceEventarcChannelDelete, + + Importer: &schema.ResourceImporter{ + State: resourceEventarcChannelImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(20 * time.Minute), + Update: schema.DefaultTimeout(20 * time.Minute), + Delete: schema.DefaultTimeout(20 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "location": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The location for the resource", + }, + + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Required. The resource name of the channel. Must be unique within the location on the project.", + }, + + "crypto_key_name": { + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: "Optional. Resource name of a KMS crypto key (managed by the user) used to encrypt/decrypt their event data. It must match the pattern `projects/*/locations/*/keyRings/*/cryptoKeys/*`.", + }, + + "project": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: "The project for the resource", + }, + + "third_party_provider": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "The name of the event provider (e.g. Eventarc SaaS partner) associated with the channel. This provider will be granted permissions to publish events to the channel. Format: `projects/{project}/locations/{location}/providers/{provider_id}`.", + }, + + "activation_token": { + Type: schema.TypeString, + Computed: true, + Description: "Output only. The activation token for the channel. The token must be used by the provider to register the channel for publishing.", + }, + + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: "Output only. The creation time.", + }, + + "pubsub_topic": { + Type: schema.TypeString, + Computed: true, + Description: "Output only. The name of the Pub/Sub topic created and managed by Eventarc system as a transport for the event delivery. Format: `projects/{project}/topics/{topic_id}`.", + }, + + "state": { + Type: schema.TypeString, + Computed: true, + Description: "Output only. The state of a Channel. Possible values: STATE_UNSPECIFIED, PENDING, ACTIVE, INACTIVE", + }, + + "uid": { + Type: schema.TypeString, + Computed: true, + Description: "Output only. Server assigned unique identifier for the channel. The value is a UUID4 string and guaranteed to remain unchanged until the resource is deleted.", + }, + + "update_time": { + Type: schema.TypeString, + Computed: true, + Description: "Output only. The last-modified time.", + }, + }, + } +} + +func resourceEventarcChannelCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + project, err := getProject(d, config) + if err != nil { + return err + } + + obj := &eventarc.Channel{ + Location: dcl.String(d.Get("location").(string)), + Name: dcl.String(d.Get("name").(string)), + CryptoKeyName: dcl.String(d.Get("crypto_key_name").(string)), + Project: dcl.String(project), + ThirdPartyProvider: dcl.String(d.Get("third_party_provider").(string)), + } + + id, err := obj.ID() + if err != nil { + return fmt.Errorf("error constructing id: %s", err) + } + d.SetId(id) + directive := CreateDirective + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + billingProject := project + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + client := NewDCLEventarcClient(config, userAgent, billingProject, d.Timeout(schema.TimeoutCreate)) + if bp, err := replaceVars(d, config, client.Config.BasePath); err != nil { + d.SetId("") + return fmt.Errorf("Could not format %q: %w", client.Config.BasePath, err) + } else { + client.Config.BasePath = bp + } + res, err := client.ApplyChannel(context.Background(), obj, directive...) + + if _, ok := err.(dcl.DiffAfterApplyError); ok { + log.Printf("[DEBUG] Diff after apply returned from the DCL: %s", err) + } else if err != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error creating Channel: %s", err) + } + + log.Printf("[DEBUG] Finished creating Channel %q: %#v", d.Id(), res) + + return resourceEventarcChannelRead(d, meta) +} + +func resourceEventarcChannelRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + project, err := getProject(d, config) + if err != nil { + return err + } + + obj := &eventarc.Channel{ + Location: dcl.String(d.Get("location").(string)), + Name: dcl.String(d.Get("name").(string)), + CryptoKeyName: dcl.String(d.Get("crypto_key_name").(string)), + Project: dcl.String(project), + ThirdPartyProvider: dcl.String(d.Get("third_party_provider").(string)), + } + + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + billingProject := project + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + client := NewDCLEventarcClient(config, userAgent, billingProject, d.Timeout(schema.TimeoutRead)) + if bp, err := replaceVars(d, config, client.Config.BasePath); err != nil { + d.SetId("") + return fmt.Errorf("Could not format %q: %w", client.Config.BasePath, err) + } else { + client.Config.BasePath = bp + } + res, err := client.GetChannel(context.Background(), obj) + if err != nil { + resourceName := fmt.Sprintf("EventarcChannel %q", d.Id()) + return handleNotFoundDCLError(err, d, resourceName) + } + + if err = d.Set("location", res.Location); err != nil { + return fmt.Errorf("error setting location in state: %s", err) + } + if err = d.Set("name", res.Name); err != nil { + return fmt.Errorf("error setting name in state: %s", err) + } + if err = d.Set("crypto_key_name", res.CryptoKeyName); err != nil { + return fmt.Errorf("error setting crypto_key_name in state: %s", err) + } + if err = d.Set("project", res.Project); err != nil { + return fmt.Errorf("error setting project in state: %s", err) + } + if err = d.Set("third_party_provider", res.ThirdPartyProvider); err != nil { + return fmt.Errorf("error setting third_party_provider in state: %s", err) + } + if err = d.Set("activation_token", res.ActivationToken); err != nil { + return fmt.Errorf("error setting activation_token in state: %s", err) + } + if err = d.Set("create_time", res.CreateTime); err != nil { + return fmt.Errorf("error setting create_time in state: %s", err) + } + if err = d.Set("pubsub_topic", res.PubsubTopic); err != nil { + return fmt.Errorf("error setting pubsub_topic in state: %s", err) + } + if err = d.Set("state", res.State); err != nil { + return fmt.Errorf("error setting state in state: %s", err) + } + if err = d.Set("uid", res.Uid); err != nil { + return fmt.Errorf("error setting uid in state: %s", err) + } + if err = d.Set("update_time", res.UpdateTime); err != nil { + return fmt.Errorf("error setting update_time in state: %s", err) + } + + return nil +} +func resourceEventarcChannelUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + project, err := getProject(d, config) + if err != nil { + return err + } + + obj := &eventarc.Channel{ + Location: dcl.String(d.Get("location").(string)), + Name: dcl.String(d.Get("name").(string)), + CryptoKeyName: dcl.String(d.Get("crypto_key_name").(string)), + Project: dcl.String(project), + ThirdPartyProvider: dcl.String(d.Get("third_party_provider").(string)), + } + directive := UpdateDirective + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + + billingProject := "" + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + client := NewDCLEventarcClient(config, userAgent, billingProject, d.Timeout(schema.TimeoutUpdate)) + if bp, err := replaceVars(d, config, client.Config.BasePath); err != nil { + d.SetId("") + return fmt.Errorf("Could not format %q: %w", client.Config.BasePath, err) + } else { + client.Config.BasePath = bp + } + res, err := client.ApplyChannel(context.Background(), obj, directive...) + + if _, ok := err.(dcl.DiffAfterApplyError); ok { + log.Printf("[DEBUG] Diff after apply returned from the DCL: %s", err) + } else if err != nil { + // The resource didn't actually create + d.SetId("") + return fmt.Errorf("Error updating Channel: %s", err) + } + + log.Printf("[DEBUG] Finished creating Channel %q: %#v", d.Id(), res) + + return resourceEventarcChannelRead(d, meta) +} + +func resourceEventarcChannelDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + project, err := getProject(d, config) + if err != nil { + return err + } + + obj := &eventarc.Channel{ + Location: dcl.String(d.Get("location").(string)), + Name: dcl.String(d.Get("name").(string)), + CryptoKeyName: dcl.String(d.Get("crypto_key_name").(string)), + Project: dcl.String(project), + ThirdPartyProvider: dcl.String(d.Get("third_party_provider").(string)), + } + + log.Printf("[DEBUG] Deleting Channel %q", d.Id()) + userAgent, err := generateUserAgentString(d, config.userAgent) + if err != nil { + return err + } + billingProject := project + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + client := NewDCLEventarcClient(config, userAgent, billingProject, d.Timeout(schema.TimeoutDelete)) + if bp, err := replaceVars(d, config, client.Config.BasePath); err != nil { + d.SetId("") + return fmt.Errorf("Could not format %q: %w", client.Config.BasePath, err) + } else { + client.Config.BasePath = bp + } + if err := client.DeleteChannel(context.Background(), obj); err != nil { + return fmt.Errorf("Error deleting Channel: %s", err) + } + + log.Printf("[DEBUG] Finished deleting Channel %q", d.Id()) + return nil +} + +func resourceEventarcChannelImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + + if err := parseImportId([]string{ + "projects/(?P[^/]+)/locations/(?P[^/]+)/channels/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := replaceVarsForId(d, config, "projects/{{project}}/locations/{{location}}/channels/{{name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} diff --git a/google/resource_eventarc_channel_sweeper_test.go b/google/resource_eventarc_channel_sweeper_test.go new file mode 100644 index 00000000000..c7c5099a981 --- /dev/null +++ b/google/resource_eventarc_channel_sweeper_test.go @@ -0,0 +1,71 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** Type: DCL *** +// +// ---------------------------------------------------------------------------- +// +// This file is managed by Magic Modules (https://github.com/GoogleCloudPlatform/magic-modules) +// and is based on the DCL (https://github.com/GoogleCloudPlatform/declarative-resource-client-library). +// Changes will need to be made to the DCL or Magic Modules instead of here. +// +// We are not currently able to accept contributions to this file. If changes +// are required, please file an issue at https://github.com/hashicorp/terraform-provider-google/issues/new/choose +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "context" + "log" + "testing" + + eventarc "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/eventarc" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func init() { + resource.AddTestSweepers("EventarcChannel", &resource.Sweeper{ + Name: "EventarcChannel", + F: testSweepEventarcChannel, + }) +} + +func testSweepEventarcChannel(region string) error { + log.Print("[INFO][SWEEPER_LOG] Starting sweeper for EventarcChannel") + + config, err := sharedConfigForRegion(region) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error getting shared config for region: %s", err) + return err + } + + err = config.LoadAndValidate(context.Background()) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error loading: %s", err) + return err + } + + t := &testing.T{} + billingId := getTestBillingAccountFromEnv(t) + + // Setup variables to be used for Delete arguments. + d := map[string]string{ + "project": config.Project, + "region": region, + "location": region, + "zone": "-", + "billing_account": billingId, + } + + client := NewDCLEventarcClient(config, config.userAgent, "", 0) + err = client.DeleteAllChannel(context.Background(), d["project"], d["location"], isDeletableEventarcChannel) + if err != nil { + return err + } + return nil +} + +func isDeletableEventarcChannel(r *eventarc.Channel) bool { + return isSweepableTestResource(*r.Name) +} diff --git a/google/resource_eventarc_channel_test.go b/google/resource_eventarc_channel_test.go new file mode 100644 index 00000000000..7253ef7a089 --- /dev/null +++ b/google/resource_eventarc_channel_test.go @@ -0,0 +1,207 @@ +package google + +import ( + "context" + "fmt" + "strings" + "testing" + + dcl "github.com/GoogleCloudPlatform/declarative-resource-client-library/dcl" + eventarc "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/eventarc" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccEventarcChannel_basic(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "region": getTestRegionFromEnv(), + "project_name": getTestProjectFromEnv(), + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckEventarcChannelDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccEventarcChannel_basic(context), + }, + { + ResourceName: "google_eventarc_channel.primary", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccEventarcChannel_cryptoKeyUpdate(t *testing.T) { + t.Parallel() + + region := getTestRegionFromEnv() + key1 := BootstrapKMSKeyWithPurposeInLocationAndName(t, "ENCRYPT_DECRYPT", region, "tf-bootstrap-key1") + key2 := BootstrapKMSKeyWithPurposeInLocationAndName(t, "ENCRYPT_DECRYPT", region, "tf-bootstrap-key2") + + context := map[string]interface{}{ + "region": region, + "project_name": getTestProjectFromEnv(), + "key_ring": GetResourceNameFromSelfLink(key1.KeyRing.Name), + "key1": GetResourceNameFromSelfLink(key1.CryptoKey.Name), + "key2": GetResourceNameFromSelfLink(key2.CryptoKey.Name), + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckEventarcChannelDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccEventarcChannel_setCryptoKey(context), + }, + { + ResourceName: "google_eventarc_channel.primary", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccEventarcChannel_cryptoKeyUpdate(context), + }, + { + ResourceName: "google_eventarc_channel.primary", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccEventarcChannel_basic(context map[string]interface{}) string { + return Nprintf(` +data "google_project" "test_project" { + project_id = "%{project_name}" +} + +resource "google_eventarc_channel" "primary" { + location = "%{region}" + name = "tf-test-name%{random_suffix}" + third_party_provider = "projects/${data.google_project.test_project.project_id}/locations/%{region}/providers/datadog" +} +`, context) +} + +func testAccEventarcChannel_setCryptoKey(context map[string]interface{}) string { + return Nprintf(` +data "google_project" "test_project" { + project_id = "%{project_name}" +} + +data "google_kms_key_ring" "test_key_ring" { + name = "%{key_ring}" + location = "us-central1" +} + +data "google_kms_crypto_key" "key1" { + name = "%{key1}" + key_ring = data.google_kms_key_ring.test_key_ring.id +} + + +resource "google_kms_crypto_key_iam_binding" "key1_binding" { + crypto_key_id = data.google_kms_crypto_key.key1.id + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + + members = [ + "serviceAccount:service-${data.google_project.test_project.number}@gcp-sa-eventarc.iam.gserviceaccount.com", + ] +} + +resource "google_eventarc_channel" "primary" { + location = "%{region}" + name = "tf-test-name%{random_suffix}" + crypto_key_name = data.google_kms_crypto_key.key1.id + third_party_provider = "projects/${data.google_project.test_project.project_id}/locations/%{region}/providers/datadog" + depends_on = [google_kms_crypto_key_iam_binding.key1_binding] +} +`, context) +} + +func testAccEventarcChannel_cryptoKeyUpdate(context map[string]interface{}) string { + return Nprintf(` +data "google_project" "test_project" { + project_id = "%{project_name}" +} + +data "google_kms_key_ring" "test_key_ring" { + name = "%{key_ring}" + location = "us-central1" +} + +data "google_kms_crypto_key" "key2" { + name = "%{key2}" + key_ring = data.google_kms_key_ring.test_key_ring.id +} + +resource "google_kms_crypto_key_iam_binding" "key2_binding" { + crypto_key_id = data.google_kms_crypto_key.key2.id + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + + members = [ + "serviceAccount:service-${data.google_project.test_project.number}@gcp-sa-eventarc.iam.gserviceaccount.com", + ] +} + +resource "google_eventarc_channel" "primary" { + location = "%{region}" + name = "tf-test-name%{random_suffix}" + crypto_key_name= data.google_kms_crypto_key.key2.id + third_party_provider = "projects/${data.google_project.test_project.project_id}/locations/%{region}/providers/datadog" + depends_on = [google_kms_crypto_key_iam_binding.key2_binding] +} +`, context) +} + +func testAccCheckEventarcChannelDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "rs.google_eventarc_channel" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + billingProject := "" + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + obj := &eventarc.Channel{ + Location: dcl.String(rs.Primary.Attributes["location"]), + Name: dcl.String(rs.Primary.Attributes["name"]), + CryptoKeyName: dcl.String(rs.Primary.Attributes["crypto_key_name"]), + Project: dcl.StringOrNil(rs.Primary.Attributes["project"]), + ThirdPartyProvider: dcl.String(rs.Primary.Attributes["third_party_provider"]), + ActivationToken: dcl.StringOrNil(rs.Primary.Attributes["activation_token"]), + CreateTime: dcl.StringOrNil(rs.Primary.Attributes["create_time"]), + PubsubTopic: dcl.StringOrNil(rs.Primary.Attributes["pubsub_topic"]), + State: eventarc.ChannelStateEnumRef(rs.Primary.Attributes["state"]), + Uid: dcl.StringOrNil(rs.Primary.Attributes["uid"]), + UpdateTime: dcl.StringOrNil(rs.Primary.Attributes["update_time"]), + } + + client := NewDCLEventarcClient(config, config.userAgent, billingProject, 0) + _, err := client.GetChannel(context.Background(), obj) + if err == nil { + return fmt.Errorf("google_eventarc_channel still exists %v", obj) + } + } + return nil + } +} diff --git a/google/resource_eventarc_trigger.go b/google/resource_eventarc_trigger.go index 8c32aac2e0a..76e6bb6a99c 100644 --- a/google/resource_eventarc_trigger.go +++ b/google/resource_eventarc_trigger.go @@ -75,6 +75,14 @@ func resourceEventarcTrigger() *schema.Resource { Description: "Required. The resource name of the trigger. Must be unique within the location on the project.", }, + "channel": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: "Optional. The name of the channel associated with the trigger in `projects/{project}/locations/{location}/channels/{channel}` format. You must provide a channel to receive events from Eventarc SaaS partners.", + }, + "labels": { Type: schema.TypeMap, Optional: true, @@ -312,6 +320,7 @@ func resourceEventarcTriggerCreate(d *schema.ResourceData, meta interface{}) err Location: dcl.String(d.Get("location").(string)), MatchingCriteria: expandEventarcTriggerMatchingCriteriaArray(d.Get("matching_criteria")), Name: dcl.String(d.Get("name").(string)), + Channel: dcl.String(d.Get("channel").(string)), Labels: checkStringMap(d.Get("labels")), Project: dcl.String(project), ServiceAccount: dcl.String(d.Get("service_account").(string)), @@ -367,6 +376,7 @@ func resourceEventarcTriggerRead(d *schema.ResourceData, meta interface{}) error Location: dcl.String(d.Get("location").(string)), MatchingCriteria: expandEventarcTriggerMatchingCriteriaArray(d.Get("matching_criteria")), Name: dcl.String(d.Get("name").(string)), + Channel: dcl.String(d.Get("channel").(string)), Labels: checkStringMap(d.Get("labels")), Project: dcl.String(project), ServiceAccount: dcl.String(d.Get("service_account").(string)), @@ -407,6 +417,9 @@ func resourceEventarcTriggerRead(d *schema.ResourceData, meta interface{}) error if err = d.Set("name", res.Name); err != nil { return fmt.Errorf("error setting name in state: %s", err) } + if err = d.Set("channel", res.Channel); err != nil { + return fmt.Errorf("error setting channel in state: %s", err) + } if err = d.Set("labels", res.Labels); err != nil { return fmt.Errorf("error setting labels in state: %s", err) } @@ -449,6 +462,7 @@ func resourceEventarcTriggerUpdate(d *schema.ResourceData, meta interface{}) err Location: dcl.String(d.Get("location").(string)), MatchingCriteria: expandEventarcTriggerMatchingCriteriaArray(d.Get("matching_criteria")), Name: dcl.String(d.Get("name").(string)), + Channel: dcl.String(d.Get("channel").(string)), Labels: checkStringMap(d.Get("labels")), Project: dcl.String(project), ServiceAccount: dcl.String(d.Get("service_account").(string)), @@ -499,6 +513,7 @@ func resourceEventarcTriggerDelete(d *schema.ResourceData, meta interface{}) err Location: dcl.String(d.Get("location").(string)), MatchingCriteria: expandEventarcTriggerMatchingCriteriaArray(d.Get("matching_criteria")), Name: dcl.String(d.Get("name").(string)), + Channel: dcl.String(d.Get("channel").(string)), Labels: checkStringMap(d.Get("labels")), Project: dcl.String(project), ServiceAccount: dcl.String(d.Get("service_account").(string)), diff --git a/google/resource_eventarc_trigger_generated_test.go b/google/resource_eventarc_trigger_generated_test.go index ce1cdcd2237..3db3670ef6b 100644 --- a/google/resource_eventarc_trigger_generated_test.go +++ b/google/resource_eventarc_trigger_generated_test.go @@ -317,6 +317,7 @@ func testAccCheckEventarcTriggerDestroyProducer(t *testing.T) func(s *terraform. obj := &eventarc.Trigger{ Location: dcl.String(rs.Primary.Attributes["location"]), Name: dcl.String(rs.Primary.Attributes["name"]), + Channel: dcl.String(rs.Primary.Attributes["channel"]), Project: dcl.StringOrNil(rs.Primary.Attributes["project"]), ServiceAccount: dcl.String(rs.Primary.Attributes["service_account"]), CreateTime: dcl.StringOrNil(rs.Primary.Attributes["create_time"]), diff --git a/google/resource_eventarc_trigger_test.go b/google/resource_eventarc_trigger_test.go new file mode 100644 index 00000000000..4320dbd19af --- /dev/null +++ b/google/resource_eventarc_trigger_test.go @@ -0,0 +1,170 @@ +package google + +import ( + "context" + "fmt" + "strings" + "testing" + + dcl "github.com/GoogleCloudPlatform/declarative-resource-client-library/dcl" + eventarc "github.com/GoogleCloudPlatform/declarative-resource-client-library/services/google/eventarc" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccEventarcTrigger_channel(t *testing.T) { + t.Parallel() + + region := getTestRegionFromEnv() + key1 := BootstrapKMSKeyWithPurposeInLocationAndName(t, "ENCRYPT_DECRYPT", region, "tf-bootstrap-key1") + key2 := BootstrapKMSKeyWithPurposeInLocationAndName(t, "ENCRYPT_DECRYPT", region, "tf-bootstrap-key2") + + context := map[string]interface{}{ + "region": region, + "project_name": getTestProjectFromEnv(), + "service_account": getTestServiceAccountFromEnv(t), + "key_ring": GetResourceNameFromSelfLink(key1.KeyRing.Name), + "key1": GetResourceNameFromSelfLink(key1.CryptoKey.Name), + "key2": GetResourceNameFromSelfLink(key2.CryptoKey.Name), + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckEventarcChannelTriggerDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccEventarcTrigger_createTriggerWithChannelName(context), + }, + { + ResourceName: "google_eventarc_trigger.primary", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccEventarcTrigger_createTriggerWithChannelName(context map[string]interface{}) string { + return Nprintf(` +data "google_project" "test_project" { + project_id = "%{project_name}" +} + +data "google_kms_key_ring" "test_key_ring" { + name = "%{key_ring}" + location = "us-central1" +} + +data "google_kms_crypto_key" "key1" { + name = "%{key1}" + key_ring = data.google_kms_key_ring.test_key_ring.id +} + + +resource "google_kms_crypto_key_iam_binding" "key1_binding" { + crypto_key_id = data.google_kms_crypto_key.key1.id + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + + members = [ + "serviceAccount:service-${data.google_project.test_project.number}@gcp-sa-eventarc.iam.gserviceaccount.com", + ] +} + +resource "google_eventarc_channel" "test_channel" { + location = "%{region}" + name = "tf-test-channel%{random_suffix}" + crypto_key_name = data.google_kms_crypto_key.key1.id + third_party_provider = "projects/${data.google_project.test_project.project_id}/locations/%{region}/providers/datadog" + depends_on = [google_kms_crypto_key_iam_binding.key1_binding] +} + +resource "google_cloud_run_service" "default" { + name = "tf-test-eventarc-service%{random_suffix}" + location = "%{region}" + + metadata { + namespace = "%{project_name}" + } + + template { + spec { + containers { + image = "gcr.io/cloudrun/hello" + ports { + container_port = 8080 + } + } + container_concurrency = 50 + timeout_seconds = 100 + } + } + + traffic { + percent = 100 + latest_revision = true + } +} + +resource "google_eventarc_trigger" "primary" { + name = "tf-test-trigger%{random_suffix}" + location = "%{region}" + matching_criteria { + attribute = "type" + value = "datadog.v1.alert" + } + destination { + cloud_run_service { + service = google_cloud_run_service.default.name + region = "%{region}" + } + } + service_account= "%{service_account}" + + channel = "projects/${data.google_project.test_project.project_id}/locations/%{region}/channels/${google_eventarc_channel.test_channel.name}" + + depends_on =[google_cloud_run_service.default,google_eventarc_channel.test_channel] +} +`, context) +} + +func testAccCheckEventarcChannelTriggerDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "rs.google_eventarc_trigger" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := googleProviderConfig(t) + + billingProject := "" + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + obj := &eventarc.Trigger{ + Location: dcl.String(rs.Primary.Attributes["location"]), + Name: dcl.String(rs.Primary.Attributes["name"]), + Project: dcl.StringOrNil(rs.Primary.Attributes["project"]), + ServiceAccount: dcl.String(rs.Primary.Attributes["service_account"]), + CreateTime: dcl.StringOrNil(rs.Primary.Attributes["create_time"]), + Etag: dcl.StringOrNil(rs.Primary.Attributes["etag"]), + Uid: dcl.StringOrNil(rs.Primary.Attributes["uid"]), + UpdateTime: dcl.StringOrNil(rs.Primary.Attributes["update_time"]), + Channel: dcl.StringOrNil(rs.Primary.Attributes["channel"]), + } + + client := NewDCLEventarcClient(config, config.userAgent, billingProject, 0) + _, err := client.GetTrigger(context.Background(), obj) + if err == nil { + return fmt.Errorf("google_eventarc_trigger still exists %v", obj) + } + } + return nil + } +} diff --git a/website/docs/r/eventarc_channel.html.markdown b/website/docs/r/eventarc_channel.html.markdown new file mode 100644 index 00000000000..a8281e4fdd3 --- /dev/null +++ b/website/docs/r/eventarc_channel.html.markdown @@ -0,0 +1,135 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** Type: DCL *** +# +# ---------------------------------------------------------------------------- +# +# This file is managed by Magic Modules (https:#github.com/GoogleCloudPlatform/magic-modules) +# and is based on the DCL (https:#github.com/GoogleCloudPlatform/declarative-resource-client-library). +# Changes will need to be made to the DCL or Magic Modules instead of here. +# +# We are not currently able to accept contributions to this file. If changes +# are required, please file an issue at https:#github.com/hashicorp/terraform-provider-google/issues/new/choose +# +# ---------------------------------------------------------------------------- +subcategory: "Eventarc" +page_title: "Google: google_eventarc_channel" +description: |- + The Eventarc Channel resource +--- + +# google_eventarc_channel + +The Eventarc Channel resource + +## Example Usage - basic +```hcl +data "google_project" "test_project" { + project_id = "my-project-name" +} + +data "google_kms_key_ring" "test_key_ring" { + name = "keyring" + location = "us-west1" +} + +data "google_kms_crypto_key" "key" { + name = "key" + key_ring = data.google_kms_key_ring.test_key_ring.id +} + +resource "google_kms_crypto_key_iam_binding" "key1_binding" { + crypto_key_id = data.google_kms_crypto_key.key1.id + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + + members = [ + "serviceAccount:service-${data.google_project.test_project.number}@gcp-sa-eventarc.iam.gserviceaccount.com", + ] +} + +resource "google_eventarc_channel" "primary" { + location = "us-west1" + name = "channel" + project = "${data.google_project.test_project.project_id}" + crypto_key_name = "${data.google_kms_crypto_key.key1.id}" + third_party_provider = "projects/${data.google_project.test_project.project_id}/locations/us-west1/providers/datadog" + depends_on = [google_kms_crypto_key_iam_binding.key1_binding] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `location` - + (Required) + The location for the resource + +* `name` - + (Required) + Required. The resource name of the channel. Must be unique within the location on the project. + + + +- - - + +* `crypto_key_name` - + (Optional) + Optional. Resource name of a KMS crypto key (managed by the user) used to encrypt/decrypt their event data. It must match the pattern `projects/*/locations/*/keyRings/*/cryptoKeys/*`. + +* `project` - + (Optional) + The project for the resource + +* `third_party_provider` - + (Optional) + The name of the event provider (e.g. Eventarc SaaS partner) associated with the channel. This provider will be granted permissions to publish events to the channel. Format: `projects/{project}/locations/{location}/providers/{provider_id}`. + + + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `id` - an identifier for the resource with format `projects/{{project}}/locations/{{location}}/channels/{{name}}` + +* `activation_token` - + Output only. The activation token for the channel. The token must be used by the provider to register the channel for publishing. + +* `create_time` - + Output only. The creation time. + +* `pubsub_topic` - + Output only. The name of the Pub/Sub topic created and managed by Eventarc system as a transport for the event delivery. Format: `projects/{project}/topics/{topic_id}`. + +* `state` - + Output only. The state of a Channel. Possible values: STATE_UNSPECIFIED, PENDING, ACTIVE, INACTIVE + +* `uid` - + Output only. Server assigned unique identifier for the channel. The value is a UUID4 string and guaranteed to remain unchanged until the resource is deleted. + +* `update_time` - + Output only. The last-modified time. + +## Timeouts + +This resource provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - Default is 20 minutes. +- `update` - Default is 20 minutes. +- `delete` - Default is 20 minutes. + +## Import + +Channel can be imported using any of these accepted formats: + +``` +$ terraform import google_eventarc_channel.default projects/{{project}}/locations/{{location}}/channels/{{name}} +$ terraform import google_eventarc_channel.default {{project}}/{{location}}/{{name}} +$ terraform import google_eventarc_channel.default {{location}}/{{name}} +``` + + + diff --git a/website/docs/r/eventarc_trigger.html.markdown b/website/docs/r/eventarc_trigger.html.markdown index 506864e7f37..ca686e4f16e 100644 --- a/website/docs/r/eventarc_trigger.html.markdown +++ b/website/docs/r/eventarc_trigger.html.markdown @@ -132,6 +132,10 @@ The `matching_criteria` block supports: - - - +* `channel` - + (Optional) + Optional. The name of the channel associated with the trigger in `projects/{project}/locations/{location}/channels/{channel}` format. You must provide a channel to receive events from Eventarc SaaS partners. + * `labels` - (Optional) Optional. User labels attached to the triggers that can be used to group resources.