From b9239cdd5f3d77556353f91f16c9eedc1bb31ecd Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Mon, 10 Apr 2023 22:02:10 +0000 Subject: [PATCH] Add links support (#7630) Signed-off-by: Modular Magician --- .changelog/7630.txt | 3 + google/logging_operation.go | 73 ++++ google/provider.go | 5 +- google/resource_logging_linked_dataset.go | 353 ++++++++++++++++++ ...e_logging_linked_dataset_generated_test.go | 151 ++++++++ ...rce_logging_linked_dataset_sweeper_test.go | 128 +++++++ .../r/logging_linked_dataset.html.markdown | 153 ++++++++ 7 files changed, 864 insertions(+), 2 deletions(-) create mode 100644 .changelog/7630.txt create mode 100644 google/logging_operation.go create mode 100644 google/resource_logging_linked_dataset.go create mode 100644 google/resource_logging_linked_dataset_generated_test.go create mode 100644 google/resource_logging_linked_dataset_sweeper_test.go create mode 100644 website/docs/r/logging_linked_dataset.html.markdown diff --git a/.changelog/7630.txt b/.changelog/7630.txt new file mode 100644 index 00000000000..acd33e7f169 --- /dev/null +++ b/.changelog/7630.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +`google_logging_linked_dataset` +``` diff --git a/google/logging_operation.go b/google/logging_operation.go new file mode 100644 index 00000000000..91a04d5cc40 --- /dev/null +++ b/google/logging_operation.go @@ -0,0 +1,73 @@ +// ---------------------------------------------------------------------------- +// +// *** 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 ( + "encoding/json" + "fmt" + "time" +) + +type LoggingOperationWaiter struct { + Config *Config + UserAgent string + CommonOperationWaiter +} + +func (w *LoggingOperationWaiter) QueryOp() (interface{}, error) { + if w == nil { + return nil, fmt.Errorf("Cannot query operation, it's unset or nil.") + } + // Returns the proper get. + url := fmt.Sprintf("%s%s", w.Config.LoggingBasePath, w.CommonOperationWaiter.Op.Name) + + return SendRequest(w.Config, "GET", "", url, w.UserAgent, nil) +} + +func createLoggingWaiter(config *Config, op map[string]interface{}, activity, userAgent string) (*LoggingOperationWaiter, error) { + w := &LoggingOperationWaiter{ + Config: config, + UserAgent: userAgent, + } + if err := w.CommonOperationWaiter.SetOp(op); err != nil { + return nil, err + } + return w, nil +} + +// nolint: deadcode,unused +func LoggingOperationWaitTimeWithResponse(config *Config, op map[string]interface{}, response *map[string]interface{}, activity, userAgent string, timeout time.Duration) error { + w, err := createLoggingWaiter(config, op, activity, userAgent) + if err != nil { + return err + } + if err := OperationWait(w, activity, timeout, config.PollInterval); err != nil { + return err + } + return json.Unmarshal([]byte(w.CommonOperationWaiter.Op.Response), response) +} + +func LoggingOperationWaitTime(config *Config, op map[string]interface{}, activity, userAgent string, timeout time.Duration) error { + if val, ok := op["name"]; !ok || val == "" { + // This was a synchronous call - there is no operation to wait for. + return nil + } + w, err := createLoggingWaiter(config, op, activity, userAgent) + if err != nil { + // If w is nil, the op was synchronous. + return err + } + return OperationWait(w, activity, timeout, config.PollInterval) +} diff --git a/google/provider.go b/google/provider.go index cb4848a8546..b866a33d4b1 100644 --- a/google/provider.go +++ b/google/provider.go @@ -712,9 +712,9 @@ func Provider() *schema.Provider { return provider } -// Generated resources: 276 +// Generated resources: 277 // Generated IAM resources: 186 -// Total generated resources: 462 +// Total generated resources: 463 func ResourceMap() map[string]*schema.Resource { resourceMap, _ := ResourceMapWithErrors() return resourceMap @@ -1070,6 +1070,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_kms_key_ring": ResourceKMSKeyRing(), "google_kms_key_ring_import_job": ResourceKMSKeyRingImportJob(), "google_kms_secret_ciphertext": ResourceKMSSecretCiphertext(), + "google_logging_linked_dataset": ResourceLoggingLinkedDataset(), "google_logging_log_view": ResourceLoggingLogView(), "google_logging_metric": ResourceLoggingMetric(), "google_memcache_instance": ResourceMemcacheInstance(), diff --git a/google/resource_logging_linked_dataset.go b/google/resource_logging_linked_dataset.go new file mode 100644 index 00000000000..195b63bdf33 --- /dev/null +++ b/google/resource_logging_linked_dataset.go @@ -0,0 +1,353 @@ +// ---------------------------------------------------------------------------- +// +// *** 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" + "log" + "reflect" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func ResourceLoggingLinkedDataset() *schema.Resource { + return &schema.Resource{ + Create: resourceLoggingLinkedDatasetCreate, + Read: resourceLoggingLinkedDatasetRead, + Delete: resourceLoggingLinkedDatasetDelete, + + Importer: &schema.ResourceImporter{ + State: resourceLoggingLinkedDatasetImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(20 * time.Minute), + Delete: schema.DefaultTimeout(20 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "bucket": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: compareResourceNames, + Description: `The bucket to which the linked dataset is attached.`, + }, + "link_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The id of the linked dataset.`, + }, + "description": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: `Describes this link. The maximum length of the description is 8000 characters.`, + }, + "location": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + Description: `The location of the linked dataset.`, + }, + "parent": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + Description: `The parent of the linked dataset.`, + }, + "bigquery_dataset": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Description: `The information of a BigQuery Dataset. When a link is created, a BigQuery dataset is created along +with it, in the same project as the LogBucket it's linked to. This dataset will also have BigQuery +Views corresponding to the LogViews in the bucket.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dataset_id": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. The full resource name of the BigQuery dataset. The DATASET_ID will match the ID +of the link, so the link must match the naming restrictions of BigQuery datasets +(alphanumeric characters and underscores only). The dataset will have a resource path of +"bigquery.googleapis.com/projects/[PROJECT_ID]/datasets/[DATASET_ID]"`, + }, + }, + }, + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. The creation timestamp of the link. A timestamp in RFC3339 UTC "Zulu" format, +with nanosecond resolution and up to nine fractional digits. Examples: "2014-10-02T15:01:23Z" +and "2014-10-02T15:01:23.045123456Z".`, + }, + "lifecycle_state": { + Type: schema.TypeString, + Computed: true, + Description: `Output only. The linked dataset lifecycle state.`, + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: `The resource name of the linked dataset. The name can have up to 100 characters. A valid link id +(at the end of the link name) must only have alphanumeric characters and underscores within it.`, + }, + }, + UseJSONNumber: true, + } +} + +func resourceLoggingLinkedDatasetCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + descriptionProp, err := expandLoggingLinkedDatasetDescription(d.Get("description"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) { + obj["description"] = descriptionProp + } + + obj, err = resourceLoggingLinkedDatasetEncoder(d, meta, obj) + if err != nil { + return err + } + + url, err := ReplaceVars(d, config, "{{LoggingBasePath}}{{parent}}/locations/{{location}}/buckets/{{bucket}}/links?linkId={{link_id}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new LinkedDataset: %#v", obj) + billingProject := "" + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := SendRequestWithTimeout(config, "POST", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating LinkedDataset: %s", err) + } + + // Store the ID now + id, err := ReplaceVars(d, config, "{{parent}}/locations/{{location}}/buckets/{{bucket}}/links/{{link_id}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + // Use the resource in the operation response to populate + // identity fields and d.Id() before read + var opRes map[string]interface{} + err = LoggingOperationWaitTimeWithResponse( + config, res, &opRes, "Creating LinkedDataset", userAgent, + d.Timeout(schema.TimeoutCreate)) + if err != nil { + // The resource didn't actually create + d.SetId("") + + return fmt.Errorf("Error waiting to create LinkedDataset: %s", err) + } + + if err := d.Set("name", flattenLoggingLinkedDatasetName(opRes["name"], d, config)); err != nil { + return err + } + + // This may have caused the ID to update - update it if so. + id, err = ReplaceVars(d, config, "{{parent}}/locations/{{location}}/buckets/{{bucket}}/links/{{link_id}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + log.Printf("[DEBUG] Finished creating LinkedDataset %q: %#v", d.Id(), res) + + return resourceLoggingLinkedDatasetRead(d, meta) +} + +func resourceLoggingLinkedDatasetRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + url, err := ReplaceVars(d, config, "{{LoggingBasePath}}{{parent}}/locations/{{location}}/buckets/{{bucket}}/links/{{link_id}}") + 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 + } + + res, err := SendRequest(config, "GET", billingProject, url, userAgent, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("LoggingLinkedDataset %q", d.Id())) + } + + if err := d.Set("name", flattenLoggingLinkedDatasetName(res["name"], d, config)); err != nil { + return fmt.Errorf("Error reading LinkedDataset: %s", err) + } + if err := d.Set("description", flattenLoggingLinkedDatasetDescription(res["description"], d, config)); err != nil { + return fmt.Errorf("Error reading LinkedDataset: %s", err) + } + if err := d.Set("create_time", flattenLoggingLinkedDatasetCreateTime(res["createTime"], d, config)); err != nil { + return fmt.Errorf("Error reading LinkedDataset: %s", err) + } + if err := d.Set("lifecycle_state", flattenLoggingLinkedDatasetLifecycleState(res["lifecycleState"], d, config)); err != nil { + return fmt.Errorf("Error reading LinkedDataset: %s", err) + } + if err := d.Set("bigquery_dataset", flattenLoggingLinkedDatasetBigqueryDataset(res["bigqueryDataset"], d, config)); err != nil { + return fmt.Errorf("Error reading LinkedDataset: %s", err) + } + + return nil +} + +func resourceLoggingLinkedDatasetDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + userAgent, err := generateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + billingProject := "" + + url, err := ReplaceVars(d, config, "{{LoggingBasePath}}{{parent}}/locations/{{location}}/buckets/{{bucket}}/links/{{link_id}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting LinkedDataset %q", d.Id()) + + // err == nil indicates that the billing_project value was found + if bp, err := getBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := SendRequestWithTimeout(config, "DELETE", billingProject, url, userAgent, obj, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return handleNotFoundError(err, d, "LinkedDataset") + } + + err = LoggingOperationWaitTime( + config, res, "Deleting LinkedDataset", userAgent, + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting LinkedDataset %q: %#v", d.Id(), res) + return nil +} + +func resourceLoggingLinkedDatasetImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*Config) + if err := ParseImportId([]string{ + "(?P.+)/locations/(?P[^/]+)/buckets/(?P[^/]+)/links/(?P[^/]+)", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := ReplaceVars(d, config, "{{parent}}/locations/{{location}}/buckets/{{bucket}}/links/{{link_id}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenLoggingLinkedDatasetName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenLoggingLinkedDatasetDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenLoggingLinkedDatasetCreateTime(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenLoggingLinkedDatasetLifecycleState(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenLoggingLinkedDatasetBigqueryDataset(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["dataset_id"] = + flattenLoggingLinkedDatasetBigqueryDatasetDatasetId(original["datasetId"], d, config) + return []interface{}{transformed} +} +func flattenLoggingLinkedDatasetBigqueryDatasetDatasetId(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func expandLoggingLinkedDatasetDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func resourceLoggingLinkedDatasetEncoder(d *schema.ResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) { + // Extract any empty fields from the bucket field. + parent := d.Get("parent").(string) + bucket := d.Get("bucket").(string) + parent, err := ExtractFieldByPattern("parent", parent, bucket, "((projects|folders|organizations|billingAccounts)/[a-z0-9A-Z-]*)/locations/.*") + if err != nil { + return nil, fmt.Errorf("error extracting parent field: %s", err) + } + location := d.Get("location").(string) + location, err = ExtractFieldByPattern("location", location, bucket, "[a-zA-Z]*/[a-z0-9A-Z-]*/locations/([a-z0-9-]*)/buckets/.*") + if err != nil { + return nil, fmt.Errorf("error extracting location field: %s", err) + } + // Set parent to the extracted value. + d.Set("parent", parent) + // Set all the other fields to their short forms before forming url and setting ID. + bucket = GetResourceNameFromSelfLink(bucket) + name := d.Get("name").(string) + name = GetResourceNameFromSelfLink(name) + d.Set("location", location) + d.Set("bucket", bucket) + d.Set("name", name) + return obj, nil +} diff --git a/google/resource_logging_linked_dataset_generated_test.go b/google/resource_logging_linked_dataset_generated_test.go new file mode 100644 index 00000000000..322ee8ffe35 --- /dev/null +++ b/google/resource_logging_linked_dataset_generated_test.go @@ -0,0 +1,151 @@ +// ---------------------------------------------------------------------------- +// +// *** 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" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccLoggingLinkedDataset_loggingLinkedDatasetBasicExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "project": GetTestProjectFromEnv(), + "random_suffix": RandString(t, 10), + } + + VcrTest(t, resource.TestCase{ + PreCheck: func() { AccTestPreCheck(t) }, + ProtoV5ProviderFactories: ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckLoggingLinkedDatasetDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccLoggingLinkedDataset_loggingLinkedDatasetBasicExample(context), + }, + { + ResourceName: "google_logging_linked_dataset.logging_linked_dataset", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"link_id", "parent", "location", "bucket"}, + }, + }, + }) +} + +func testAccLoggingLinkedDataset_loggingLinkedDatasetBasicExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_logging_project_bucket_config" "logging_linked_dataset" { + location = "global" + project = "%{project}" + enable_analytics = true + bucket_id = "tftest%{random_suffix}" +} + +resource "google_logging_linked_dataset" "logging_linked_dataset" { + link_id = "tftest%{random_suffix}" + bucket = google_logging_project_bucket_config.logging_linked_dataset.id + description = "Linked dataset test" +} +`, context) +} + +func TestAccLoggingLinkedDataset_loggingLinkedDatasetAllParamsExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "project": GetTestProjectFromEnv(), + "random_suffix": RandString(t, 10), + } + + VcrTest(t, resource.TestCase{ + PreCheck: func() { AccTestPreCheck(t) }, + ProtoV5ProviderFactories: ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckLoggingLinkedDatasetDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccLoggingLinkedDataset_loggingLinkedDatasetAllParamsExample(context), + }, + { + ResourceName: "google_logging_linked_dataset.logging_linked_dataset", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"link_id", "parent", "location", "bucket"}, + }, + }, + }) +} + +func testAccLoggingLinkedDataset_loggingLinkedDatasetAllParamsExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_logging_project_bucket_config" "logging_linked_dataset" { + location = "global" + project = "%{project}" + enable_analytics = true + bucket_id = "tftest%{random_suffix}" +} + +resource "google_logging_linked_dataset" "logging_linked_dataset" { + link_id = "tftest%{random_suffix}" + bucket = "tftest%{random_suffix}" + parent = "projects/%{project}" + location = "global" + description = "Linked dataset test" + + # Using forced dependency in order to test use of the bucket name by itself without + # referencing the entire resource link, which is what is outputted by the + # google_logging_project_bucket_config resource. Use of the outputted ID is tested in + # the basic example. + depends_on = ["google_logging_project_bucket_config.logging_linked_dataset"] +} +`, context) +} + +func testAccCheckLoggingLinkedDatasetDestroyProducer(t *testing.T) func(s *terraform.State) error { + return func(s *terraform.State) error { + for name, rs := range s.RootModule().Resources { + if rs.Type != "google_logging_linked_dataset" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := GoogleProviderConfig(t) + + url, err := replaceVarsForTest(config, rs, "{{LoggingBasePath}}{{parent}}/locations/{{location}}/buckets/{{bucket}}/links/{{link_id}}") + if err != nil { + return err + } + + billingProject := "" + + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + _, err = SendRequest(config, "GET", billingProject, url, config.UserAgent, nil) + if err == nil { + return fmt.Errorf("LoggingLinkedDataset still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/resource_logging_linked_dataset_sweeper_test.go b/google/resource_logging_linked_dataset_sweeper_test.go new file mode 100644 index 00000000000..3136c0384bd --- /dev/null +++ b/google/resource_logging_linked_dataset_sweeper_test.go @@ -0,0 +1,128 @@ +// ---------------------------------------------------------------------------- +// +// *** 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 ( + "context" + "log" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func init() { + resource.AddTestSweepers("LoggingLinkedDataset", &resource.Sweeper{ + Name: "LoggingLinkedDataset", + F: testSweepLoggingLinkedDataset, + }) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepLoggingLinkedDataset(region string) error { + resourceName := "LoggingLinkedDataset" + log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s", resourceName) + + 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 replace in list template + d := &ResourceDataMock{ + FieldsInSchema: map[string]interface{}{ + "project": config.Project, + "region": region, + "location": region, + "zone": "-", + "billing_account": billingId, + }, + } + + listTemplate := strings.Split("https://logging.googleapis.com/v2/{{parent}}/locations/{{location}}/buckets/{{bucket}}/links", "?")[0] + listUrl, err := ReplaceVars(d, config, listTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err) + return nil + } + + res, err := SendRequest(config, "GET", config.Project, listUrl, config.UserAgent, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil + } + + resourceList, ok := res["linkedDatasets"] + if !ok { + log.Printf("[INFO][SWEEPER_LOG] Nothing found in response.") + return nil + } + + rl := resourceList.([]interface{}) + + log.Printf("[INFO][SWEEPER_LOG] Found %d items in %s list response.", len(rl), resourceName) + // Keep count of items that aren't sweepable for logging. + nonPrefixCount := 0 + for _, ri := range rl { + obj := ri.(map[string]interface{}) + var name string + // Id detected in the delete URL, attempt to use id. + if obj["id"] != nil { + name = GetResourceNameFromSelfLink(obj["id"].(string)) + } else if obj["name"] != nil { + name = GetResourceNameFromSelfLink(obj["name"].(string)) + } else { + log.Printf("[INFO][SWEEPER_LOG] %s resource name and id were nil", resourceName) + return nil + } + // Skip resources that shouldn't be sweeped + if !IsSweepableTestResource(name) { + nonPrefixCount++ + continue + } + + deleteTemplate := "https://logging.googleapis.com/v2/{{parent}}/locations/{{location}}/buckets/{{bucket}}/links/{{link_id}}" + deleteUrl, err := ReplaceVars(d, config, deleteTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing delete url: %s", err) + return nil + } + deleteUrl = deleteUrl + name + + // Don't wait on operations as we may have a lot to delete + _, err = SendRequest(config, "DELETE", config.Project, deleteUrl, config.UserAgent, nil) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error deleting for url %s : %s", deleteUrl, err) + } else { + log.Printf("[INFO][SWEEPER_LOG] Sent delete request for %s resource: %s", resourceName, name) + } + } + + if nonPrefixCount > 0 { + log.Printf("[INFO][SWEEPER_LOG] %d items were non-sweepable and skipped.", nonPrefixCount) + } + + return nil +} diff --git a/website/docs/r/logging_linked_dataset.html.markdown b/website/docs/r/logging_linked_dataset.html.markdown new file mode 100644 index 00000000000..1086b8d6a66 --- /dev/null +++ b/website/docs/r/logging_linked_dataset.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 (Stackdriver) Logging" +description: |- + Describes a BigQuery linked dataset +--- + +# google\_logging\_linked\_dataset + +Describes a BigQuery linked dataset + + +To get more information about LinkedDataset, see: + +* [API documentation](https://cloud.google.com/logging/docs/reference/v2/rest/v2/locations.buckets.links) +* How-to Guides + * [Official Documentation](https://cloud.google.com/logging/docs/apis) + +## Example Usage - Logging Linked Dataset Basic + + +```hcl +resource "google_logging_project_bucket_config" "logging_linked_dataset" { + location = "global" + project = "my-project-name" + enable_analytics = true + bucket_id = "tftest%{random_suffix}" +} + +resource "google_logging_linked_dataset" "logging_linked_dataset" { + link_id = "tftest%{random_suffix}" + bucket = google_logging_project_bucket_config.logging_linked_dataset.id + description = "Linked dataset test" +} +``` +## Example Usage - Logging Linked Dataset All Params + + +```hcl +resource "google_logging_project_bucket_config" "logging_linked_dataset" { + location = "global" + project = "my-project-name" + enable_analytics = true + bucket_id = "tftest%{random_suffix}" +} + +resource "google_logging_linked_dataset" "logging_linked_dataset" { + link_id = "tftest%{random_suffix}" + bucket = "tftest%{random_suffix}" + parent = "projects/my-project-name" + location = "global" + description = "Linked dataset test" + + # Using forced dependency in order to test use of the bucket name by itself without + # referencing the entire resource link, which is what is outputted by the + # google_logging_project_bucket_config resource. Use of the outputted ID is tested in + # the basic example. + depends_on = ["google_logging_project_bucket_config.logging_linked_dataset"] +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `link_id` - + (Required) + The id of the linked dataset. + +* `bucket` - + (Required) + The bucket to which the linked dataset is attached. + + +- - - + + +* `description` - + (Optional) + Describes this link. The maximum length of the description is 8000 characters. + +* `parent` - + (Optional) + The parent of the linked dataset. + +* `location` - + (Optional) + The location of the linked dataset. + + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + +* `id` - an identifier for the resource with format `{{parent}}/locations/{{location}}/buckets/{{bucket}}/links/{{link_id}}` + +* `name` - + The resource name of the linked dataset. The name can have up to 100 characters. A valid link id + (at the end of the link name) must only have alphanumeric characters and underscores within it. + +* `create_time` - + Output only. The creation timestamp of the link. A timestamp in RFC3339 UTC "Zulu" format, + with nanosecond resolution and up to nine fractional digits. Examples: "2014-10-02T15:01:23Z" + and "2014-10-02T15:01:23.045123456Z". + +* `lifecycle_state` - + Output only. The linked dataset lifecycle state. + +* `bigquery_dataset` - + The information of a BigQuery Dataset. When a link is created, a BigQuery dataset is created along + with it, in the same project as the LogBucket it's linked to. This dataset will also have BigQuery + Views corresponding to the LogViews in the bucket. + Structure is [documented below](#nested_bigquery_dataset). + + +The `bigquery_dataset` block contains: + +* `dataset_id` - + (Output) + Output only. The full resource name of the BigQuery dataset. The DATASET_ID will match the ID + of the link, so the link must match the naming restrictions of BigQuery datasets + (alphanumeric characters and underscores only). The dataset will have a resource path of + "bigquery.googleapis.com/projects/[PROJECT_ID]/datasets/[DATASET_ID]" + +## Timeouts + +This resource provides the following +[Timeouts](https://developer.hashicorp.com/terraform/plugin/sdkv2/resources/retries-and-customizable-timeouts) configuration options: + +- `create` - Default is 20 minutes. +- `delete` - Default is 20 minutes. + +## Import + + +LinkedDataset can be imported using any of these accepted formats: + +``` +$ terraform import google_logging_linked_dataset.default {{parent}}/locations/{{location}}/buckets/{{bucket}}/links/{{link_id}} +```