From c0c679698e2f608d09aec3071577bc9f4f01022e Mon Sep 17 00:00:00 2001 From: The Magician Date: Tue, 12 Sep 2023 14:00:55 -0400 Subject: [PATCH] Add storageinsights product and ReportConfig resource type. (#8799) (#15819) Signed-off-by: Modular Magician --- .changelog/8799.txt | 3 + .teamcity/components/generated/services.kt | 5 + google/fwmodels/provider_model.go | 1 + google/fwprovider/framework_provider.go | 6 + google/fwtransport/framework_config.go | 10 + google/provider/provider.go | 12 +- ...resource_storage_insights_report_config.go | 1052 +++++++++++++++++ ...e_insights_report_config_generated_test.go | 148 +++ ..._storage_insights_report_config_sweeper.go | 139 +++ ...rce_storage_insights_report_config_test.go | 158 +++ google/sweeper/gcp_sweeper_test.go | 1 + google/transport/config.go | 9 + ...orage_insights_report_config.html.markdown | 252 ++++ 13 files changed, 1794 insertions(+), 2 deletions(-) create mode 100644 .changelog/8799.txt create mode 100644 google/services/storageinsights/resource_storage_insights_report_config.go create mode 100644 google/services/storageinsights/resource_storage_insights_report_config_generated_test.go create mode 100644 google/services/storageinsights/resource_storage_insights_report_config_sweeper.go create mode 100644 google/services/storageinsights/resource_storage_insights_report_config_test.go create mode 100644 website/docs/r/storage_insights_report_config.html.markdown diff --git a/.changelog/8799.txt b/.changelog/8799.txt new file mode 100644 index 00000000000..96723d5ad2f --- /dev/null +++ b/.changelog/8799.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +`google_storage_insights_report_config` +``` diff --git a/.teamcity/components/generated/services.kt b/.teamcity/components/generated/services.kt index 1288b1a1280..e7c8c54dc89 100644 --- a/.teamcity/components/generated/services.kt +++ b/.teamcity/components/generated/services.kt @@ -576,6 +576,11 @@ var services = mapOf( "displayName" to "Storage", "path" to "./google/services/storage" ), + "storageinsights" to mapOf( + "name" to "storageinsights", + "displayName" to "Storageinsights", + "path" to "./google/services/storageinsights" + ), "storagetransfer" to mapOf( "name" to "storagetransfer", "displayName" to "Storagetransfer", diff --git a/google/fwmodels/provider_model.go b/google/fwmodels/provider_model.go index e81dad5bbe0..4a7db47b4b7 100644 --- a/google/fwmodels/provider_model.go +++ b/google/fwmodels/provider_model.go @@ -115,6 +115,7 @@ type ProviderModel struct { SpannerCustomEndpoint types.String `tfsdk:"spanner_custom_endpoint"` SQLCustomEndpoint types.String `tfsdk:"sql_custom_endpoint"` StorageCustomEndpoint types.String `tfsdk:"storage_custom_endpoint"` + StorageInsightsCustomEndpoint types.String `tfsdk:"storage_insights_custom_endpoint"` StorageTransferCustomEndpoint types.String `tfsdk:"storage_transfer_custom_endpoint"` TagsCustomEndpoint types.String `tfsdk:"tags_custom_endpoint"` TPUCustomEndpoint types.String `tfsdk:"tpu_custom_endpoint"` diff --git a/google/fwprovider/framework_provider.go b/google/fwprovider/framework_provider.go index 4cfb2a29ce3..b327648bf36 100644 --- a/google/fwprovider/framework_provider.go +++ b/google/fwprovider/framework_provider.go @@ -659,6 +659,12 @@ func (p *FrameworkProvider) Schema(_ context.Context, _ provider.SchemaRequest, transport_tpg.CustomEndpointValidator(), }, }, + "storage_insights_custom_endpoint": &schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + transport_tpg.CustomEndpointValidator(), + }, + }, "storage_transfer_custom_endpoint": &schema.StringAttribute{ Optional: true, Validators: []validator.String{ diff --git a/google/fwtransport/framework_config.go b/google/fwtransport/framework_config.go index 0f4ca365f51..6f2ff38a2c2 100644 --- a/google/fwtransport/framework_config.go +++ b/google/fwtransport/framework_config.go @@ -139,6 +139,7 @@ type FrameworkProviderConfig struct { SpannerBasePath string SQLBasePath string StorageBasePath string + StorageInsightsBasePath string StorageTransferBasePath string TagsBasePath string TPUBasePath string @@ -274,6 +275,7 @@ func (p *FrameworkProviderConfig) LoadAndValidateFramework(ctx context.Context, p.SpannerBasePath = data.SpannerCustomEndpoint.ValueString() p.SQLBasePath = data.SQLCustomEndpoint.ValueString() p.StorageBasePath = data.StorageCustomEndpoint.ValueString() + p.StorageInsightsBasePath = data.StorageInsightsCustomEndpoint.ValueString() p.StorageTransferBasePath = data.StorageTransferCustomEndpoint.ValueString() p.TagsBasePath = data.TagsCustomEndpoint.ValueString() p.TPUBasePath = data.TPUCustomEndpoint.ValueString() @@ -1133,6 +1135,14 @@ func (p *FrameworkProviderConfig) HandleDefaults(ctx context.Context, data *fwmo data.StorageCustomEndpoint = types.StringValue(customEndpoint.(string)) } } + if data.StorageInsightsCustomEndpoint.IsNull() { + customEndpoint := transport_tpg.MultiEnvDefault([]string{ + "GOOGLE_STORAGE_INSIGHTS_CUSTOM_ENDPOINT", + }, transport_tpg.DefaultBasePaths[transport_tpg.StorageInsightsBasePathKey]) + if customEndpoint != nil { + data.StorageInsightsCustomEndpoint = types.StringValue(customEndpoint.(string)) + } + } if data.StorageTransferCustomEndpoint.IsNull() { customEndpoint := transport_tpg.MultiEnvDefault([]string{ "GOOGLE_STORAGE_TRANSFER_CUSTOM_ENDPOINT", diff --git a/google/provider/provider.go b/google/provider/provider.go index 67dbf95a4b1..c51d0bc760a 100644 --- a/google/provider/provider.go +++ b/google/provider/provider.go @@ -102,6 +102,7 @@ import ( "github.com/hashicorp/terraform-provider-google/google/services/spanner" "github.com/hashicorp/terraform-provider-google/google/services/sql" "github.com/hashicorp/terraform-provider-google/google/services/storage" + "github.com/hashicorp/terraform-provider-google/google/services/storageinsights" "github.com/hashicorp/terraform-provider-google/google/services/storagetransfer" "github.com/hashicorp/terraform-provider-google/google/services/tags" "github.com/hashicorp/terraform-provider-google/google/services/tpu" @@ -678,6 +679,11 @@ func Provider() *schema.Provider { Optional: true, ValidateFunc: transport_tpg.ValidateCustomEndpoint, }, + "storage_insights_custom_endpoint": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: transport_tpg.ValidateCustomEndpoint, + }, "storage_transfer_custom_endpoint": { Type: schema.TypeString, Optional: true, @@ -980,9 +986,9 @@ func DatasourceMapWithErrors() (map[string]*schema.Resource, error) { }) } -// Generated resources: 320 +// Generated resources: 321 // Generated IAM resources: 207 -// Total generated resources: 527 +// Total generated resources: 528 func ResourceMap() map[string]*schema.Resource { resourceMap, _ := ResourceMapWithErrors() return resourceMap @@ -1482,6 +1488,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_storage_default_object_access_control": storage.ResourceStorageDefaultObjectAccessControl(), "google_storage_hmac_key": storage.ResourceStorageHmacKey(), "google_storage_object_access_control": storage.ResourceStorageObjectAccessControl(), + "google_storage_insights_report_config": storageinsights.ResourceStorageInsightsReportConfig(), "google_storage_transfer_agent_pool": storagetransfer.ResourceStorageTransferAgentPool(), "google_tags_tag_binding": tags.ResourceTagsTagBinding(), "google_tags_tag_key": tags.ResourceTagsTagKey(), @@ -1834,6 +1841,7 @@ func ProviderConfigure(ctx context.Context, d *schema.ResourceData, p *schema.Pr config.SpannerBasePath = d.Get("spanner_custom_endpoint").(string) config.SQLBasePath = d.Get("sql_custom_endpoint").(string) config.StorageBasePath = d.Get("storage_custom_endpoint").(string) + config.StorageInsightsBasePath = d.Get("storage_insights_custom_endpoint").(string) config.StorageTransferBasePath = d.Get("storage_transfer_custom_endpoint").(string) config.TagsBasePath = d.Get("tags_custom_endpoint").(string) config.TPUBasePath = d.Get("tpu_custom_endpoint").(string) diff --git a/google/services/storageinsights/resource_storage_insights_report_config.go b/google/services/storageinsights/resource_storage_insights_report_config.go new file mode 100644 index 00000000000..82336661039 --- /dev/null +++ b/google/services/storageinsights/resource_storage_insights_report_config.go @@ -0,0 +1,1052 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** 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 storageinsights + +import ( + "fmt" + "log" + "reflect" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" + "github.com/hashicorp/terraform-provider-google/google/verify" +) + +func ResourceStorageInsightsReportConfig() *schema.Resource { + return &schema.Resource{ + Create: resourceStorageInsightsReportConfigCreate, + Read: resourceStorageInsightsReportConfigRead, + Update: resourceStorageInsightsReportConfigUpdate, + Delete: resourceStorageInsightsReportConfigDelete, + + Importer: &schema.ResourceImporter{ + State: resourceStorageInsightsReportConfigImport, + }, + + 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{ + "csv_options": { + Type: schema.TypeList, + Required: true, + Description: `Options for configuring the format of the inventory report CSV file.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "delimiter": { + Type: schema.TypeString, + Optional: true, + Description: `The delimiter used to separate the fields in the inventory report CSV file.`, + }, + "header_required": { + Type: schema.TypeBool, + Optional: true, + Description: `The boolean that indicates whether or not headers are included in the inventory report CSV file.`, + }, + "record_separator": { + Type: schema.TypeString, + Optional: true, + Description: `The character used to separate the records in the inventory report CSV file.`, + }, + }, + }, + }, + "location": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The location of the ReportConfig. The source and destination buckets specified in the ReportConfig +must be in the same location.`, + }, + "display_name": { + Type: schema.TypeString, + Optional: true, + Description: `The editable display name of the inventory report configuration. Has a limit of 256 characters. Can be empty.`, + }, + "frequency_options": { + Type: schema.TypeList, + Optional: true, + Description: `Options for configuring how inventory reports are generated.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "end_date": { + Type: schema.TypeList, + Required: true, + Description: `The date to stop generating inventory reports. For example, {"day": 15, "month": 9, "year": 2022}.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "day": { + Type: schema.TypeInt, + Required: true, + Description: `The day of the month to stop generating inventory reports.`, + }, + "month": { + Type: schema.TypeInt, + Required: true, + Description: `The month to stop generating inventory reports.`, + }, + "year": { + Type: schema.TypeInt, + Required: true, + Description: `The year to stop generating inventory reports`, + }, + }, + }, + }, + "frequency": { + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidateEnum([]string{"DAILY", "WEEKLY"}), + Description: `The frequency in which inventory reports are generated. Values are DAILY or WEEKLY. Possible values: ["DAILY", "WEEKLY"]`, + }, + "start_date": { + Type: schema.TypeList, + Required: true, + Description: `The date to start generating inventory reports. For example, {"day": 15, "month": 8, "year": 2022}.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "day": { + Type: schema.TypeInt, + Required: true, + Description: `The day of the month to start generating inventory reports.`, + }, + "month": { + Type: schema.TypeInt, + Required: true, + Description: `The month to start generating inventory reports.`, + }, + "year": { + Type: schema.TypeInt, + Required: true, + Description: `The year to start generating inventory reports`, + }, + }, + }, + }, + }, + }, + }, + "object_metadata_report_options": { + Type: schema.TypeList, + Optional: true, + Description: `Options for including metadata in an inventory report.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "metadata_fields": { + Type: schema.TypeList, + Required: true, + Description: `The metadata fields included in an inventory report.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "storage_destination_options": { + Type: schema.TypeList, + Required: true, + Description: `Options for where the inventory reports are stored.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bucket": { + Type: schema.TypeString, + Required: true, + Description: `The destination bucket that stores the generated inventory reports.`, + }, + "destination_path": { + Type: schema.TypeString, + Optional: true, + Description: `The path within the destination bucket to store generated inventory reports.`, + }, + }, + }, + }, + "storage_filters": { + Type: schema.TypeList, + Optional: true, + Description: `A nested object resource`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bucket": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: `The filter to use when specifying which bucket to generate inventory reports for.`, + }, + }, + }, + }, + }, + }, + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: `The UUID of the inventory report configuration.`, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + UseJSONNumber: true, + } +} + +func resourceStorageInsightsReportConfigCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + obj := make(map[string]interface{}) + frequencyOptionsProp, err := expandStorageInsightsReportConfigFrequencyOptions(d.Get("frequency_options"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("frequency_options"); !tpgresource.IsEmptyValue(reflect.ValueOf(frequencyOptionsProp)) && (ok || !reflect.DeepEqual(v, frequencyOptionsProp)) { + obj["frequencyOptions"] = frequencyOptionsProp + } + csvOptionsProp, err := expandStorageInsightsReportConfigCsvOptions(d.Get("csv_options"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("csv_options"); !tpgresource.IsEmptyValue(reflect.ValueOf(csvOptionsProp)) && (ok || !reflect.DeepEqual(v, csvOptionsProp)) { + obj["csvOptions"] = csvOptionsProp + } + objectMetadataReportOptionsProp, err := expandStorageInsightsReportConfigObjectMetadataReportOptions(d.Get("object_metadata_report_options"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("object_metadata_report_options"); !tpgresource.IsEmptyValue(reflect.ValueOf(objectMetadataReportOptionsProp)) && (ok || !reflect.DeepEqual(v, objectMetadataReportOptionsProp)) { + obj["objectMetadataReportOptions"] = objectMetadataReportOptionsProp + } + displayNameProp, err := expandStorageInsightsReportConfigDisplayName(d.Get("display_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("display_name"); !tpgresource.IsEmptyValue(reflect.ValueOf(displayNameProp)) && (ok || !reflect.DeepEqual(v, displayNameProp)) { + obj["displayName"] = displayNameProp + } + + url, err := tpgresource.ReplaceVars(d, config, "{{StorageInsightsBasePath}}projects/{{project}}/locations/{{location}}/reportConfigs") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new ReportConfig: %#v", obj) + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for ReportConfig: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "POST", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutCreate), + }) + if err != nil { + return fmt.Errorf("Error creating ReportConfig: %s", err) + } + if err := d.Set("name", flattenStorageInsightsReportConfigName(res["name"], d, config)); err != nil { + return fmt.Errorf(`Error setting computed identity field "name": %s`, err) + } + + // Store the ID now + id, err := tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{location}}/reportConfigs/{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + log.Printf("[DEBUG] Finished creating ReportConfig %q: %#v", d.Id(), res) + + return resourceStorageInsightsReportConfigRead(d, meta) +} + +func resourceStorageInsightsReportConfigRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + url, err := tpgresource.ReplaceVars(d, config, "{{StorageInsightsBasePath}}projects/{{project}}/locations/{{location}}/reportConfigs/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for ReportConfig: %s", err) + } + billingProject = project + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("StorageInsightsReportConfig %q", d.Id())) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading ReportConfig: %s", err) + } + + if err := d.Set("name", flattenStorageInsightsReportConfigName(res["name"], d, config)); err != nil { + return fmt.Errorf("Error reading ReportConfig: %s", err) + } + if err := d.Set("frequency_options", flattenStorageInsightsReportConfigFrequencyOptions(res["frequencyOptions"], d, config)); err != nil { + return fmt.Errorf("Error reading ReportConfig: %s", err) + } + if err := d.Set("csv_options", flattenStorageInsightsReportConfigCsvOptions(res["csvOptions"], d, config)); err != nil { + return fmt.Errorf("Error reading ReportConfig: %s", err) + } + if err := d.Set("object_metadata_report_options", flattenStorageInsightsReportConfigObjectMetadataReportOptions(res["objectMetadataReportOptions"], d, config)); err != nil { + return fmt.Errorf("Error reading ReportConfig: %s", err) + } + if err := d.Set("display_name", flattenStorageInsightsReportConfigDisplayName(res["displayName"], d, config)); err != nil { + return fmt.Errorf("Error reading ReportConfig: %s", err) + } + + return nil +} + +func resourceStorageInsightsReportConfigUpdate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for ReportConfig: %s", err) + } + billingProject = project + + obj := make(map[string]interface{}) + frequencyOptionsProp, err := expandStorageInsightsReportConfigFrequencyOptions(d.Get("frequency_options"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("frequency_options"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, frequencyOptionsProp)) { + obj["frequencyOptions"] = frequencyOptionsProp + } + csvOptionsProp, err := expandStorageInsightsReportConfigCsvOptions(d.Get("csv_options"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("csv_options"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, csvOptionsProp)) { + obj["csvOptions"] = csvOptionsProp + } + objectMetadataReportOptionsProp, err := expandStorageInsightsReportConfigObjectMetadataReportOptions(d.Get("object_metadata_report_options"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("object_metadata_report_options"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, objectMetadataReportOptionsProp)) { + obj["objectMetadataReportOptions"] = objectMetadataReportOptionsProp + } + displayNameProp, err := expandStorageInsightsReportConfigDisplayName(d.Get("display_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("display_name"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, displayNameProp)) { + obj["displayName"] = displayNameProp + } + + url, err := tpgresource.ReplaceVars(d, config, "{{StorageInsightsBasePath}}projects/{{project}}/locations/{{location}}/reportConfigs/{{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating ReportConfig %q: %#v", d.Id(), obj) + updateMask := []string{} + + if d.HasChange("frequency_options") { + updateMask = append(updateMask, "frequencyOptions") + } + + if d.HasChange("csv_options") { + updateMask = append(updateMask, "csvOptions") + } + + if d.HasChange("object_metadata_report_options") { + updateMask = append(updateMask, "objectMetadataReportOptions.metadataFields", + "objectMetadataReportOptions.storageDestinationOptions.bucket", + "objectMetadataReportOptions.storageDestinationOptions.destinationPath") + } + + if d.HasChange("display_name") { + updateMask = append(updateMask, "displayName") + } + // updateMask is a URL parameter but not present in the schema, so ReplaceVars + // won't set it + url, err = transport_tpg.AddQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")}) + if err != nil { + return err + } + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "PATCH", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutUpdate), + }) + + if err != nil { + return fmt.Errorf("Error updating ReportConfig %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating ReportConfig %q: %#v", d.Id(), res) + } + + return resourceStorageInsightsReportConfigRead(d, meta) +} + +func resourceStorageInsightsReportConfigDelete(d *schema.ResourceData, meta interface{}) error { + config := meta.(*transport_tpg.Config) + userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent) + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for ReportConfig: %s", err) + } + billingProject = project + + url, err := tpgresource.ReplaceVars(d, config, "{{StorageInsightsBasePath}}projects/{{project}}/locations/{{location}}/reportConfigs/{{name}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting ReportConfig %q", d.Id()) + + // err == nil indicates that the billing_project value was found + if bp, err := tpgresource.GetBillingProject(d, config); err == nil { + billingProject = bp + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "DELETE", + Project: billingProject, + RawURL: url, + UserAgent: userAgent, + Body: obj, + Timeout: d.Timeout(schema.TimeoutDelete), + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, "ReportConfig") + } + + log.Printf("[DEBUG] Finished deleting ReportConfig %q: %#v", d.Id(), res) + return nil +} + +func resourceStorageInsightsReportConfigImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*transport_tpg.Config) + if err := tpgresource.ParseImportId([]string{ + "projects/(?P[^/]+)/locations/(?P[^/]+)/reportConfigs/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)/(?P[^/]+)", + "(?P[^/]+)/(?P[^/]+)", + }, d, config); err != nil { + return nil, err + } + + // Replace import id for the resource id + id, err := tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{location}}/reportConfigs/{{name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenStorageInsightsReportConfigName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return v + } + return tpgresource.NameFromSelfLinkStateFunc(v) +} + +func flattenStorageInsightsReportConfigFrequencyOptions(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["frequency"] = + flattenStorageInsightsReportConfigFrequencyOptionsFrequency(original["frequency"], d, config) + transformed["start_date"] = + flattenStorageInsightsReportConfigFrequencyOptionsStartDate(original["startDate"], d, config) + transformed["end_date"] = + flattenStorageInsightsReportConfigFrequencyOptionsEndDate(original["endDate"], d, config) + return []interface{}{transformed} +} +func flattenStorageInsightsReportConfigFrequencyOptionsFrequency(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenStorageInsightsReportConfigFrequencyOptionsStartDate(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["day"] = + flattenStorageInsightsReportConfigFrequencyOptionsStartDateDay(original["day"], d, config) + transformed["month"] = + flattenStorageInsightsReportConfigFrequencyOptionsStartDateMonth(original["month"], d, config) + transformed["year"] = + flattenStorageInsightsReportConfigFrequencyOptionsStartDateYear(original["year"], d, config) + return []interface{}{transformed} +} +func flattenStorageInsightsReportConfigFrequencyOptionsStartDateDay(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := tpgresource.StringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenStorageInsightsReportConfigFrequencyOptionsStartDateMonth(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := tpgresource.StringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenStorageInsightsReportConfigFrequencyOptionsStartDateYear(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := tpgresource.StringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenStorageInsightsReportConfigFrequencyOptionsEndDate(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["day"] = + flattenStorageInsightsReportConfigFrequencyOptionsEndDateDay(original["day"], d, config) + transformed["month"] = + flattenStorageInsightsReportConfigFrequencyOptionsEndDateMonth(original["month"], d, config) + transformed["year"] = + flattenStorageInsightsReportConfigFrequencyOptionsEndDateYear(original["year"], d, config) + return []interface{}{transformed} +} +func flattenStorageInsightsReportConfigFrequencyOptionsEndDateDay(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := tpgresource.StringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenStorageInsightsReportConfigFrequencyOptionsEndDateMonth(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := tpgresource.StringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenStorageInsightsReportConfigFrequencyOptionsEndDateYear(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := tpgresource.StringToFixed64(strVal); err == nil { + return intVal + } + } + + // number values are represented as float64 + if floatVal, ok := v.(float64); ok { + intVal := int(floatVal) + return intVal + } + + return v // let terraform core handle it otherwise +} + +func flattenStorageInsightsReportConfigCsvOptions(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["record_separator"] = + flattenStorageInsightsReportConfigCsvOptionsRecordSeparator(original["recordSeparator"], d, config) + transformed["delimiter"] = + flattenStorageInsightsReportConfigCsvOptionsDelimiter(original["delimiter"], d, config) + transformed["header_required"] = + flattenStorageInsightsReportConfigCsvOptionsHeaderRequired(original["headerRequired"], d, config) + return []interface{}{transformed} +} +func flattenStorageInsightsReportConfigCsvOptionsRecordSeparator(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenStorageInsightsReportConfigCsvOptionsDelimiter(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenStorageInsightsReportConfigCsvOptionsHeaderRequired(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenStorageInsightsReportConfigObjectMetadataReportOptions(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["metadata_fields"] = + flattenStorageInsightsReportConfigObjectMetadataReportOptionsMetadataFields(original["metadataFields"], d, config) + transformed["storage_filters"] = + flattenStorageInsightsReportConfigObjectMetadataReportOptionsStorageFilters(original["storageFilters"], d, config) + transformed["storage_destination_options"] = + flattenStorageInsightsReportConfigObjectMetadataReportOptionsStorageDestinationOptions(original["storageDestinationOptions"], d, config) + return []interface{}{transformed} +} +func flattenStorageInsightsReportConfigObjectMetadataReportOptionsMetadataFields(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenStorageInsightsReportConfigObjectMetadataReportOptionsStorageFilters(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["bucket"] = + flattenStorageInsightsReportConfigObjectMetadataReportOptionsStorageFiltersBucket(original["bucket"], d, config) + return []interface{}{transformed} +} +func flattenStorageInsightsReportConfigObjectMetadataReportOptionsStorageFiltersBucket(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenStorageInsightsReportConfigObjectMetadataReportOptionsStorageDestinationOptions(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + if v == nil { + return nil + } + original := v.(map[string]interface{}) + if len(original) == 0 { + return nil + } + transformed := make(map[string]interface{}) + transformed["bucket"] = + flattenStorageInsightsReportConfigObjectMetadataReportOptionsStorageDestinationOptionsBucket(original["bucket"], d, config) + transformed["destination_path"] = + flattenStorageInsightsReportConfigObjectMetadataReportOptionsStorageDestinationOptionsDestinationPath(original["destinationPath"], d, config) + return []interface{}{transformed} +} +func flattenStorageInsightsReportConfigObjectMetadataReportOptionsStorageDestinationOptionsBucket(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenStorageInsightsReportConfigObjectMetadataReportOptionsStorageDestinationOptionsDestinationPath(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenStorageInsightsReportConfigDisplayName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func expandStorageInsightsReportConfigFrequencyOptions(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.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{}) + + transformedFrequency, err := expandStorageInsightsReportConfigFrequencyOptionsFrequency(original["frequency"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedFrequency); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["frequency"] = transformedFrequency + } + + transformedStartDate, err := expandStorageInsightsReportConfigFrequencyOptionsStartDate(original["start_date"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedStartDate); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["startDate"] = transformedStartDate + } + + transformedEndDate, err := expandStorageInsightsReportConfigFrequencyOptionsEndDate(original["end_date"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedEndDate); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["endDate"] = transformedEndDate + } + + return transformed, nil +} + +func expandStorageInsightsReportConfigFrequencyOptionsFrequency(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandStorageInsightsReportConfigFrequencyOptionsStartDate(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.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{}) + + transformedDay, err := expandStorageInsightsReportConfigFrequencyOptionsStartDateDay(original["day"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDay); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["day"] = transformedDay + } + + transformedMonth, err := expandStorageInsightsReportConfigFrequencyOptionsStartDateMonth(original["month"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMonth); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["month"] = transformedMonth + } + + transformedYear, err := expandStorageInsightsReportConfigFrequencyOptionsStartDateYear(original["year"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedYear); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["year"] = transformedYear + } + + return transformed, nil +} + +func expandStorageInsightsReportConfigFrequencyOptionsStartDateDay(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandStorageInsightsReportConfigFrequencyOptionsStartDateMonth(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandStorageInsightsReportConfigFrequencyOptionsStartDateYear(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandStorageInsightsReportConfigFrequencyOptionsEndDate(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.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{}) + + transformedDay, err := expandStorageInsightsReportConfigFrequencyOptionsEndDateDay(original["day"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDay); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["day"] = transformedDay + } + + transformedMonth, err := expandStorageInsightsReportConfigFrequencyOptionsEndDateMonth(original["month"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMonth); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["month"] = transformedMonth + } + + transformedYear, err := expandStorageInsightsReportConfigFrequencyOptionsEndDateYear(original["year"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedYear); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["year"] = transformedYear + } + + return transformed, nil +} + +func expandStorageInsightsReportConfigFrequencyOptionsEndDateDay(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandStorageInsightsReportConfigFrequencyOptionsEndDateMonth(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandStorageInsightsReportConfigFrequencyOptionsEndDateYear(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandStorageInsightsReportConfigCsvOptions(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.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{}) + + transformedRecordSeparator, err := expandStorageInsightsReportConfigCsvOptionsRecordSeparator(original["record_separator"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedRecordSeparator); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["recordSeparator"] = transformedRecordSeparator + } + + transformedDelimiter, err := expandStorageInsightsReportConfigCsvOptionsDelimiter(original["delimiter"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDelimiter); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["delimiter"] = transformedDelimiter + } + + transformedHeaderRequired, err := expandStorageInsightsReportConfigCsvOptionsHeaderRequired(original["header_required"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHeaderRequired); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["headerRequired"] = transformedHeaderRequired + } + + return transformed, nil +} + +func expandStorageInsightsReportConfigCsvOptionsRecordSeparator(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandStorageInsightsReportConfigCsvOptionsDelimiter(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandStorageInsightsReportConfigCsvOptionsHeaderRequired(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandStorageInsightsReportConfigObjectMetadataReportOptions(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.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{}) + + transformedMetadataFields, err := expandStorageInsightsReportConfigObjectMetadataReportOptionsMetadataFields(original["metadata_fields"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMetadataFields); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["metadataFields"] = transformedMetadataFields + } + + transformedStorageFilters, err := expandStorageInsightsReportConfigObjectMetadataReportOptionsStorageFilters(original["storage_filters"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedStorageFilters); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["storageFilters"] = transformedStorageFilters + } + + transformedStorageDestinationOptions, err := expandStorageInsightsReportConfigObjectMetadataReportOptionsStorageDestinationOptions(original["storage_destination_options"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedStorageDestinationOptions); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["storageDestinationOptions"] = transformedStorageDestinationOptions + } + + return transformed, nil +} + +func expandStorageInsightsReportConfigObjectMetadataReportOptionsMetadataFields(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandStorageInsightsReportConfigObjectMetadataReportOptionsStorageFilters(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.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{}) + + transformedBucket, err := expandStorageInsightsReportConfigObjectMetadataReportOptionsStorageFiltersBucket(original["bucket"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedBucket); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["bucket"] = transformedBucket + } + + return transformed, nil +} + +func expandStorageInsightsReportConfigObjectMetadataReportOptionsStorageFiltersBucket(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandStorageInsightsReportConfigObjectMetadataReportOptionsStorageDestinationOptions(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.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{}) + + transformedBucket, err := expandStorageInsightsReportConfigObjectMetadataReportOptionsStorageDestinationOptionsBucket(original["bucket"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedBucket); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["bucket"] = transformedBucket + } + + transformedDestinationPath, err := expandStorageInsightsReportConfigObjectMetadataReportOptionsStorageDestinationOptionsDestinationPath(original["destination_path"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDestinationPath); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["destinationPath"] = transformedDestinationPath + } + + return transformed, nil +} + +func expandStorageInsightsReportConfigObjectMetadataReportOptionsStorageDestinationOptionsBucket(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandStorageInsightsReportConfigObjectMetadataReportOptionsStorageDestinationOptionsDestinationPath(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandStorageInsightsReportConfigDisplayName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} diff --git a/google/services/storageinsights/resource_storage_insights_report_config_generated_test.go b/google/services/storageinsights/resource_storage_insights_report_config_generated_test.go new file mode 100644 index 00000000000..64e0279f919 --- /dev/null +++ b/google/services/storageinsights/resource_storage_insights_report_config_generated_test.go @@ -0,0 +1,148 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** 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 storageinsights_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +func TestAccStorageInsightsReportConfig_storageInsightsReportConfigExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckStorageInsightsReportConfigDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccStorageInsightsReportConfig_storageInsightsReportConfigExample(context), + }, + { + ResourceName: "google_storage_insights_report_config.config", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location"}, + }, + }, + }) +} + +func testAccStorageInsightsReportConfig_storageInsightsReportConfigExample(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_project" "project" { +} + +resource "google_storage_insights_report_config" "config" { + display_name = "Test Report Config" + location = "us-central1" + frequency_options { + frequency = "WEEKLY" + start_date { + day = 15 + month = 3 + year = 2050 + } + end_date { + day = 15 + month = 4 + year = 2050 + } + } + csv_options { + record_separator = "\n" + delimiter = "," + header_required = false + } + object_metadata_report_options { + metadata_fields = ["bucket", "name", "project"] + storage_filters { + bucket = google_storage_bucket.report_bucket.name + } + storage_destination_options { + bucket = google_storage_bucket.report_bucket.name + destination_path = "test-insights-reports" + } + } +} + +resource "google_storage_bucket" "report_bucket" { + name = "tf-test-my-bucket%{random_suffix}" + location = "us-central1" + force_destroy = true + uniform_bucket_level_access = true +} + +resource "google_storage_bucket_iam_member" "admin" { + bucket = google_storage_bucket.report_bucket.name + role = "roles/storage.admin" + member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-storageinsights.iam.gserviceaccount.com" +} +`, context) +} + +func testAccCheckStorageInsightsReportConfigDestroyProducer(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_storage_insights_report_config" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := acctest.GoogleProviderConfig(t) + + url, err := tpgresource.ReplaceVarsForTest(config, rs, "{{StorageInsightsBasePath}}projects/{{project}}/locations/{{location}}/reportConfigs/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + if config.BillingProject != "" { + billingProject = config.BillingProject + } + + _, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: billingProject, + RawURL: url, + UserAgent: config.UserAgent, + }) + if err == nil { + return fmt.Errorf("StorageInsightsReportConfig still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/services/storageinsights/resource_storage_insights_report_config_sweeper.go b/google/services/storageinsights/resource_storage_insights_report_config_sweeper.go new file mode 100644 index 00000000000..f6dc750c157 --- /dev/null +++ b/google/services/storageinsights/resource_storage_insights_report_config_sweeper.go @@ -0,0 +1,139 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +// ---------------------------------------------------------------------------- +// +// *** 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 storageinsights + +import ( + "context" + "log" + "strings" + "testing" + + "github.com/hashicorp/terraform-provider-google/google/envvar" + "github.com/hashicorp/terraform-provider-google/google/sweeper" + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +func init() { + sweeper.AddTestSweepers("StorageInsightsReportConfig", testSweepStorageInsightsReportConfig) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepStorageInsightsReportConfig(region string) error { + resourceName := "StorageInsightsReportConfig" + log.Printf("[INFO][SWEEPER_LOG] Starting sweeper for %s", resourceName) + + config, err := sweeper.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 := envvar.GetTestBillingAccountFromEnv(t) + + // Setup variables to replace in list template + d := &tpgresource.ResourceDataMock{ + FieldsInSchema: map[string]interface{}{ + "project": config.Project, + "region": region, + "location": region, + "zone": "-", + "billing_account": billingId, + }, + } + + listTemplate := strings.Split("https://storageinsights.googleapis.com/v1/projects/{{project}}/locations/{{location}}/reportConfigs", "?")[0] + listUrl, err := tpgresource.ReplaceVars(d, config, listTemplate) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] error preparing sweeper list url: %s", err) + return nil + } + + res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "GET", + Project: config.Project, + RawURL: listUrl, + UserAgent: config.UserAgent, + }) + if err != nil { + log.Printf("[INFO][SWEEPER_LOG] Error in response from request %s: %s", listUrl, err) + return nil + } + + resourceList, ok := res["reportConfigs"] + 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{}) + if obj["name"] == nil { + log.Printf("[INFO][SWEEPER_LOG] %s resource name was nil", resourceName) + return nil + } + + name := tpgresource.GetResourceNameFromSelfLink(obj["name"].(string)) + // Skip resources that shouldn't be sweeped + if !sweeper.IsSweepableTestResource(name) { + nonPrefixCount++ + continue + } + + deleteTemplate := "https://storageinsights.googleapis.com/v1/projects/{{project}}/locations/{{location}}/reportConfigs/{{name}}" + deleteUrl, err := tpgresource.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 = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: config, + Method: "DELETE", + Project: config.Project, + RawURL: deleteUrl, + UserAgent: config.UserAgent, + }) + 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/google/services/storageinsights/resource_storage_insights_report_config_test.go b/google/services/storageinsights/resource_storage_insights_report_config_test.go new file mode 100644 index 00000000000..00c5344646c --- /dev/null +++ b/google/services/storageinsights/resource_storage_insights_report_config_test.go @@ -0,0 +1,158 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package storageinsights_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccStorageInsightsReportConfig_update(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testAccStorageInsightsReportConfig_full(context), + }, + { + ResourceName: "google_storage_insights_report_config.config", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location"}, + }, + { + Config: testAccStorageInsightsReportConfig_update(context), + }, + { + ResourceName: "google_storage_insights_report_config.config", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"location"}, + }, + }, + }) +} + +func testAccStorageInsightsReportConfig_full(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_project" "project" { +} + +resource "google_storage_insights_report_config" "config" { + display_name = "Test Report Config" + location = "us-central1" + frequency_options { + frequency = "WEEKLY" + start_date { + day = 15 + month = 3 + year = 2050 + } + end_date { + day = 15 + month = 4 + year = 2050 + } + } + csv_options { + record_separator = "\n" + delimiter = "," + header_required = false + } + object_metadata_report_options { + metadata_fields = ["bucket", "name", "project"] + storage_filters { + bucket = google_storage_bucket.report_bucket.name + } + storage_destination_options { + bucket = google_storage_bucket.report_bucket.name + destination_path = "test-insights-reports" + } + } + depends_on = [ + google_storage_bucket_iam_member.admin, + ] +} + +resource "google_storage_bucket" "report_bucket" { + name = "tf-test-my-bucket%{random_suffix}" + location = "us-central1" + force_destroy = true + uniform_bucket_level_access = true +} + +resource "google_storage_bucket_iam_member" "admin" { + bucket = google_storage_bucket.report_bucket.name + role = "roles/storage.admin" + member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-storageinsights.iam.gserviceaccount.com" +} +`, context) +} + +func testAccStorageInsightsReportConfig_update(context map[string]interface{}) string { + return acctest.Nprintf(` +data "google_project" "project" { +} + +resource "google_storage_insights_report_config" "config" { + display_name = "Test Report Config Updated" + location = "us-central1" + frequency_options { + frequency = "DAILY" + start_date { + day = 14 + month = 3 + year = 2040 + } + end_date { + day = 14 + month = 4 + year = 2040 + } + } + csv_options { + record_separator = "\r\n" + delimiter = "." + header_required = true + } + object_metadata_report_options { + metadata_fields = ["bucket", "name", "project"] + storage_filters { + bucket = google_storage_bucket.report_bucket.name + } + storage_destination_options { + bucket = google_storage_bucket.report_bucket.name + destination_path = "test-insights-reports-updated" + } + } + depends_on = [ + google_storage_bucket_iam_member.admin, + ] +} + +resource "google_storage_bucket" "report_bucket" { + name = "tf-test-my-bucket%{random_suffix}" + location = "us-central1" + force_destroy = true + uniform_bucket_level_access = true +} + +resource "google_storage_bucket_iam_member" "admin" { + bucket = google_storage_bucket.report_bucket.name + role = "roles/storage.admin" + member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-storageinsights.iam.gserviceaccount.com" +} +`, context) +} diff --git a/google/sweeper/gcp_sweeper_test.go b/google/sweeper/gcp_sweeper_test.go index 3633a978f12..8e2a805384a 100644 --- a/google/sweeper/gcp_sweeper_test.go +++ b/google/sweeper/gcp_sweeper_test.go @@ -98,6 +98,7 @@ import ( _ "github.com/hashicorp/terraform-provider-google/google/services/spanner" _ "github.com/hashicorp/terraform-provider-google/google/services/sql" _ "github.com/hashicorp/terraform-provider-google/google/services/storage" + _ "github.com/hashicorp/terraform-provider-google/google/services/storageinsights" _ "github.com/hashicorp/terraform-provider-google/google/services/storagetransfer" _ "github.com/hashicorp/terraform-provider-google/google/services/tags" _ "github.com/hashicorp/terraform-provider-google/google/services/tpu" diff --git a/google/transport/config.go b/google/transport/config.go index eafe7ed4f8a..0dd69ec4b84 100644 --- a/google/transport/config.go +++ b/google/transport/config.go @@ -273,6 +273,7 @@ type Config struct { SpannerBasePath string SQLBasePath string StorageBasePath string + StorageInsightsBasePath string StorageTransferBasePath string TagsBasePath string TPUBasePath string @@ -391,6 +392,7 @@ const SourceRepoBasePathKey = "SourceRepo" const SpannerBasePathKey = "Spanner" const SQLBasePathKey = "SQL" const StorageBasePathKey = "Storage" +const StorageInsightsBasePathKey = "StorageInsights" const StorageTransferBasePathKey = "StorageTransfer" const TagsBasePathKey = "Tags" const TPUBasePathKey = "TPU" @@ -503,6 +505,7 @@ var DefaultBasePaths = map[string]string{ SpannerBasePathKey: "https://spanner.googleapis.com/v1/", SQLBasePathKey: "https://sqladmin.googleapis.com/sql/v1beta4/", StorageBasePathKey: "https://storage.googleapis.com/storage/v1/", + StorageInsightsBasePathKey: "https://storageinsights.googleapis.com/v1/", StorageTransferBasePathKey: "https://storagetransfer.googleapis.com/v1/", TagsBasePathKey: "https://cloudresourcemanager.googleapis.com/v3/", TPUBasePathKey: "https://tpu.googleapis.com/v1/", @@ -1042,6 +1045,11 @@ func HandleSDKDefaults(d *schema.ResourceData) error { "GOOGLE_STORAGE_CUSTOM_ENDPOINT", }, DefaultBasePaths[StorageBasePathKey])) } + if d.Get("storage_insights_custom_endpoint") == "" { + d.Set("storage_insights_custom_endpoint", MultiEnvDefault([]string{ + "GOOGLE_STORAGE_INSIGHTS_CUSTOM_ENDPOINT", + }, DefaultBasePaths[StorageInsightsBasePathKey])) + } if d.Get("storage_transfer_custom_endpoint") == "" { d.Set("storage_transfer_custom_endpoint", MultiEnvDefault([]string{ "GOOGLE_STORAGE_TRANSFER_CUSTOM_ENDPOINT", @@ -2003,6 +2011,7 @@ func ConfigureBasePaths(c *Config) { c.SpannerBasePath = DefaultBasePaths[SpannerBasePathKey] c.SQLBasePath = DefaultBasePaths[SQLBasePathKey] c.StorageBasePath = DefaultBasePaths[StorageBasePathKey] + c.StorageInsightsBasePath = DefaultBasePaths[StorageInsightsBasePathKey] c.StorageTransferBasePath = DefaultBasePaths[StorageTransferBasePathKey] c.TagsBasePath = DefaultBasePaths[TagsBasePathKey] c.TPUBasePath = DefaultBasePaths[TPUBasePathKey] diff --git a/website/docs/r/storage_insights_report_config.html.markdown b/website/docs/r/storage_insights_report_config.html.markdown new file mode 100644 index 00000000000..d035e236470 --- /dev/null +++ b/website/docs/r/storage_insights_report_config.html.markdown @@ -0,0 +1,252 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** 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 Storage Insights" +description: |- + Represents an inventory report configuration. +--- + +# google\_storage\_insights\_report\_config + +Represents an inventory report configuration. + + +To get more information about ReportConfig, see: + +* [API documentation](https://cloud.google.com/storage/docs/json_api/v1/reportConfig) +* How-to Guides + * [Official Documentation](https://cloud.google.com/storage/docs/insights/using-storage-insights) + + +## Example Usage - Storage Insights Report Config + + +```hcl +data "google_project" "project" { +} + +resource "google_storage_insights_report_config" "config" { + display_name = "Test Report Config" + location = "us-central1" + frequency_options { + frequency = "WEEKLY" + start_date { + day = 15 + month = 3 + year = 2050 + } + end_date { + day = 15 + month = 4 + year = 2050 + } + } + csv_options { + record_separator = "\n" + delimiter = "," + header_required = false + } + object_metadata_report_options { + metadata_fields = ["bucket", "name", "project"] + storage_filters { + bucket = google_storage_bucket.report_bucket.name + } + storage_destination_options { + bucket = google_storage_bucket.report_bucket.name + destination_path = "test-insights-reports" + } + } +} + +resource "google_storage_bucket" "report_bucket" { + name = "my-bucket" + location = "us-central1" + force_destroy = true + uniform_bucket_level_access = true +} + +resource "google_storage_bucket_iam_member" "admin" { + bucket = google_storage_bucket.report_bucket.name + role = "roles/storage.admin" + member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-storageinsights.iam.gserviceaccount.com" +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `csv_options` - + (Required) + Options for configuring the format of the inventory report CSV file. + Structure is [documented below](#nested_csv_options). + +* `location` - + (Required) + The location of the ReportConfig. The source and destination buckets specified in the ReportConfig + must be in the same location. + + +The `csv_options` block supports: + +* `record_separator` - + (Optional) + The character used to separate the records in the inventory report CSV file. + +* `delimiter` - + (Optional) + The delimiter used to separate the fields in the inventory report CSV file. + +* `header_required` - + (Optional) + The boolean that indicates whether or not headers are included in the inventory report CSV file. + +- - - + + +* `frequency_options` - + (Optional) + Options for configuring how inventory reports are generated. + Structure is [documented below](#nested_frequency_options). + +* `object_metadata_report_options` - + (Optional) + Options for including metadata in an inventory report. + Structure is [documented below](#nested_object_metadata_report_options). + +* `display_name` - + (Optional) + The editable display name of the inventory report configuration. Has a limit of 256 characters. Can be empty. + +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the provider project is used. + + +The `frequency_options` block supports: + +* `frequency` - + (Required) + The frequency in which inventory reports are generated. Values are DAILY or WEEKLY. + Possible values are: `DAILY`, `WEEKLY`. + +* `start_date` - + (Required) + The date to start generating inventory reports. For example, {"day": 15, "month": 8, "year": 2022}. + Structure is [documented below](#nested_start_date). + +* `end_date` - + (Required) + The date to stop generating inventory reports. For example, {"day": 15, "month": 9, "year": 2022}. + Structure is [documented below](#nested_end_date). + + +The `start_date` block supports: + +* `day` - + (Required) + The day of the month to start generating inventory reports. + +* `month` - + (Required) + The month to start generating inventory reports. + +* `year` - + (Required) + The year to start generating inventory reports + +The `end_date` block supports: + +* `day` - + (Required) + The day of the month to stop generating inventory reports. + +* `month` - + (Required) + The month to stop generating inventory reports. + +* `year` - + (Required) + The year to stop generating inventory reports + +The `object_metadata_report_options` block supports: + +* `metadata_fields` - + (Required) + The metadata fields included in an inventory report. + +* `storage_filters` - + (Optional) + A nested object resource + Structure is [documented below](#nested_storage_filters). + +* `storage_destination_options` - + (Required) + Options for where the inventory reports are stored. + Structure is [documented below](#nested_storage_destination_options). + + +The `storage_filters` block supports: + +* `bucket` - + (Optional) + The filter to use when specifying which bucket to generate inventory reports for. + +The `storage_destination_options` block supports: + +* `bucket` - + (Required) + The destination bucket that stores the generated inventory reports. + +* `destination_path` - + (Optional) + The path within the destination bucket to store generated inventory reports. + +## 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}}/reportConfigs/{{name}}` + +* `name` - + The UUID of the inventory report configuration. + + +## 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. +- `update` - Default is 20 minutes. +- `delete` - Default is 20 minutes. + +## Import + + +ReportConfig can be imported using any of these accepted formats: + +``` +$ terraform import google_storage_insights_report_config.default projects/{{project}}/locations/{{location}}/reportConfigs/{{name}} +$ terraform import google_storage_insights_report_config.default {{project}}/{{location}}/{{name}} +$ terraform import google_storage_insights_report_config.default {{location}}/{{name}} +``` + +## User Project Overrides + +This resource supports [User Project Overrides](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/provider_reference#user_project_override).