diff --git a/.changelog/8110.txt b/.changelog/8110.txt new file mode 100644 index 00000000000..92e45d05f7a --- /dev/null +++ b/.changelog/8110.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +`google_looker_instance` +``` diff --git a/.teamcity/components/generated/services.kt b/.teamcity/components/generated/services.kt index 51cf84166f5..c3c1540e5ac 100644 --- a/.teamcity/components/generated/services.kt +++ b/.teamcity/components/generated/services.kt @@ -71,6 +71,7 @@ var services = mapOf( "identityplatform" to "Identityplatform", "kms" to "Kms", "logging" to "Logging", + "looker" to "Looker", "memcache" to "Memcache", "mlengine" to "Mlengine", "monitoring" to "Monitoring", diff --git a/google/config_test_utils.go b/google/config_test_utils.go index c7431eb41b8..5e89d6ba203 100644 --- a/google/config_test_utils.go +++ b/google/config_test_utils.go @@ -85,6 +85,7 @@ func configureTestBasePaths(c *transport_tpg.Config, url string) { c.IdentityPlatformBasePath = url c.KMSBasePath = url c.LoggingBasePath = url + c.LookerBasePath = url c.MemcacheBasePath = url c.MLEngineBasePath = url c.MonitoringBasePath = url diff --git a/google/fwmodels/provider_model.go b/google/fwmodels/provider_model.go index 8029655fea3..21356cb1709 100644 --- a/google/fwmodels/provider_model.go +++ b/google/fwmodels/provider_model.go @@ -86,6 +86,7 @@ type ProviderModel struct { IdentityPlatformCustomEndpoint types.String `tfsdk:"identity_platform_custom_endpoint"` KMSCustomEndpoint types.String `tfsdk:"kms_custom_endpoint"` LoggingCustomEndpoint types.String `tfsdk:"logging_custom_endpoint"` + LookerCustomEndpoint types.String `tfsdk:"looker_custom_endpoint"` MemcacheCustomEndpoint types.String `tfsdk:"memcache_custom_endpoint"` MLEngineCustomEndpoint types.String `tfsdk:"ml_engine_custom_endpoint"` MonitoringCustomEndpoint types.String `tfsdk:"monitoring_custom_endpoint"` diff --git a/google/fwprovider/framework_provider.go b/google/fwprovider/framework_provider.go index fbe670e57d7..48215d1b323 100644 --- a/google/fwprovider/framework_provider.go +++ b/google/fwprovider/framework_provider.go @@ -485,6 +485,12 @@ func (p *FrameworkProvider) Schema(_ context.Context, _ provider.SchemaRequest, transport_tpg.CustomEndpointValidator(), }, }, + "looker_custom_endpoint": &schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + transport_tpg.CustomEndpointValidator(), + }, + }, "memcache_custom_endpoint": &schema.StringAttribute{ Optional: true, Validators: []validator.String{ diff --git a/google/fwtransport/framework_config.go b/google/fwtransport/framework_config.go index 2cdbe1e2e45..31bdf06c2dd 100644 --- a/google/fwtransport/framework_config.go +++ b/google/fwtransport/framework_config.go @@ -110,6 +110,7 @@ type FrameworkProviderConfig struct { IdentityPlatformBasePath string KMSBasePath string LoggingBasePath string + LookerBasePath string MemcacheBasePath string MLEngineBasePath string MonitoringBasePath string @@ -244,6 +245,7 @@ func (p *FrameworkProviderConfig) LoadAndValidateFramework(ctx context.Context, p.IdentityPlatformBasePath = data.IdentityPlatformCustomEndpoint.ValueString() p.KMSBasePath = data.KMSCustomEndpoint.ValueString() p.LoggingBasePath = data.LoggingCustomEndpoint.ValueString() + p.LookerBasePath = data.LookerCustomEndpoint.ValueString() p.MemcacheBasePath = data.MemcacheCustomEndpoint.ValueString() p.MLEngineBasePath = data.MLEngineCustomEndpoint.ValueString() p.MonitoringBasePath = data.MonitoringCustomEndpoint.ValueString() @@ -891,6 +893,14 @@ func (p *FrameworkProviderConfig) HandleDefaults(ctx context.Context, data *fwmo data.LoggingCustomEndpoint = types.StringValue(customEndpoint.(string)) } } + if data.LookerCustomEndpoint.IsNull() { + customEndpoint := transport_tpg.MultiEnvDefault([]string{ + "GOOGLE_LOOKER_CUSTOM_ENDPOINT", + }, transport_tpg.DefaultBasePaths[transport_tpg.LookerBasePathKey]) + if customEndpoint != nil { + data.LookerCustomEndpoint = types.StringValue(customEndpoint.(string)) + } + } if data.MemcacheCustomEndpoint.IsNull() { customEndpoint := transport_tpg.MultiEnvDefault([]string{ "GOOGLE_MEMCACHE_CUSTOM_ENDPOINT", diff --git a/google/gcp_sweeper_test.go b/google/gcp_sweeper_test.go index 00e16f45e8c..9562901efb8 100644 --- a/google/gcp_sweeper_test.go +++ b/google/gcp_sweeper_test.go @@ -69,6 +69,7 @@ import ( _ "github.com/hashicorp/terraform-provider-google/google/services/identityplatform" _ "github.com/hashicorp/terraform-provider-google/google/services/kms" _ "github.com/hashicorp/terraform-provider-google/google/services/logging" + _ "github.com/hashicorp/terraform-provider-google/google/services/looker" _ "github.com/hashicorp/terraform-provider-google/google/services/memcache" _ "github.com/hashicorp/terraform-provider-google/google/services/mlengine" _ "github.com/hashicorp/terraform-provider-google/google/services/monitoring" diff --git a/google/provider/provider.go b/google/provider/provider.go index dcdc2c43840..da2d0bcb755 100644 --- a/google/provider/provider.go +++ b/google/provider/provider.go @@ -74,6 +74,7 @@ import ( "github.com/hashicorp/terraform-provider-google/google/services/identityplatform" "github.com/hashicorp/terraform-provider-google/google/services/kms" "github.com/hashicorp/terraform-provider-google/google/services/logging" + "github.com/hashicorp/terraform-provider-google/google/services/looker" "github.com/hashicorp/terraform-provider-google/google/services/memcache" "github.com/hashicorp/terraform-provider-google/google/services/mlengine" "github.com/hashicorp/terraform-provider-google/google/services/monitoring" @@ -527,6 +528,11 @@ func Provider() *schema.Provider { Optional: true, ValidateFunc: transport_tpg.ValidateCustomEndpoint, }, + "looker_custom_endpoint": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: transport_tpg.ValidateCustomEndpoint, + }, "memcache_custom_endpoint": { Type: schema.TypeString, Optional: true, @@ -939,9 +945,9 @@ func DatasourceMapWithErrors() (map[string]*schema.Resource, error) { }) } -// Generated resources: 300 +// Generated resources: 301 // Generated IAM resources: 198 -// Total generated resources: 498 +// Total generated resources: 499 func ResourceMap() map[string]*schema.Resource { resourceMap, _ := ResourceMapWithErrors() return resourceMap @@ -1327,6 +1333,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_logging_linked_dataset": logging.ResourceLoggingLinkedDataset(), "google_logging_log_view": logging.ResourceLoggingLogView(), "google_logging_metric": logging.ResourceLoggingMetric(), + "google_looker_instance": looker.ResourceLookerInstance(), "google_memcache_instance": memcache.ResourceMemcacheInstance(), "google_ml_engine_model": mlengine.ResourceMLEngineModel(), "google_monitoring_alert_policy": monitoring.ResourceMonitoringAlertPolicy(), @@ -1734,6 +1741,7 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData, p *schema.Pr config.IdentityPlatformBasePath = d.Get("identity_platform_custom_endpoint").(string) config.KMSBasePath = d.Get("kms_custom_endpoint").(string) config.LoggingBasePath = d.Get("logging_custom_endpoint").(string) + config.LookerBasePath = d.Get("looker_custom_endpoint").(string) config.MemcacheBasePath = d.Get("memcache_custom_endpoint").(string) config.MLEngineBasePath = d.Get("ml_engine_custom_endpoint").(string) config.MonitoringBasePath = d.Get("monitoring_custom_endpoint").(string) diff --git a/google/resource_looker_instance_generated_test.go b/google/resource_looker_instance_generated_test.go new file mode 100644 index 00000000000..c038e83c62c --- /dev/null +++ b/google/resource_looker_instance_generated_test.go @@ -0,0 +1,293 @@ +// 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 google + +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 TestAccLookerInstance_lookerInstanceBasicExample(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: testAccCheckLookerInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccLookerInstance_lookerInstanceBasicExample(context), + }, + { + ResourceName: "google_looker_instance.looker-instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "oauth_config", "region"}, + }, + }, + }) +} + +func testAccLookerInstance_lookerInstanceBasicExample(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_looker_instance" "looker-instance" { + name = "tf-test-my-instance%{random_suffix}" + platform_edition = "LOOKER_CORE_STANDARD" + region = "us-central1" + oauth_config { + client_id = "tf-test-my-client-id%{random_suffix}" + client_secret = "tf-test-my-client-secret%{random_suffix}" + } +} +`, context) +} + +func TestAccLookerInstance_lookerInstanceFullExample(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: testAccCheckLookerInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccLookerInstance_lookerInstanceFullExample(context), + }, + { + ResourceName: "google_looker_instance.looker-instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "oauth_config", "region"}, + }, + }, + }) +} + +func testAccLookerInstance_lookerInstanceFullExample(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_looker_instance" "looker-instance" { + name = "tf-test-my-instance%{random_suffix}" + platform_edition = "LOOKER_CORE_STANDARD" + region = "us-central1" + public_ip_enabled = true + admin_settings { + allowed_email_domains = ["google.com"] + } + // User metadata config is only available when platform edition is LOOKER_CORE_STANDARD. + user_metadata { + additional_developer_user_count = 10 + additional_standard_user_count = 10 + additional_viewer_user_count = 10 + } + maintenance_window { + day_of_week = "THURSDAY" + start_time { + hours = 22 + minutes = 0 + seconds = 0 + nanos = 0 + } + } + deny_maintenance_period { + start_date { + year = 2050 + month = 1 + day = 1 + } + end_date { + year = 2050 + month = 2 + day = 1 + } + time { + hours = 10 + minutes = 0 + seconds = 0 + nanos = 0 + } + } + oauth_config { + client_id = "tf-test-my-client-id%{random_suffix}" + client_secret = "tf-test-my-client-secret%{random_suffix}" + } +} +`, context) +} + +func TestAccLookerInstance_lookerInstanceEnterpriseFullExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "kms_key_name": acctest.BootstrapKMSKeyInLocation(t, "us-central1").CryptoKey.Name, + "random_suffix": acctest.RandString(t, 10), + } + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckLookerInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccLookerInstance_lookerInstanceEnterpriseFullExample(context), + }, + { + ResourceName: "google_looker_instance.looker-instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "oauth_config", "region"}, + }, + }, + }) +} + +func testAccLookerInstance_lookerInstanceEnterpriseFullExample(context map[string]interface{}) string { + return acctest.Nprintf(` +resource "google_looker_instance" "looker-instance" { + name = "tf-test-my-instance%{random_suffix}" + platform_edition = "LOOKER_CORE_ENTERPRISE_ANNUAL" + region = "us-central1" + private_ip_enabled = true + public_ip_enabled = false + reserved_range = "${google_compute_global_address.looker_range.name}" + consumer_network = google_compute_network.looker_network.id + admin_settings { + allowed_email_domains = ["google.com"] + } + encryption_config { + kms_key_name = "%{kms_key_name}" + } + maintenance_window { + day_of_week = "THURSDAY" + start_time { + hours = 22 + minutes = 0 + seconds = 0 + nanos = 0 + } + } + deny_maintenance_period { + start_date { + year = 2050 + month = 1 + day = 1 + } + end_date { + year = 2050 + month = 2 + day = 1 + } + time { + hours = 10 + minutes = 0 + seconds = 0 + nanos = 0 + } + } + oauth_config { + client_id = "tf-test-my-client-id%{random_suffix}" + client_secret = "tf-test-my-client-secret%{random_suffix}" + } + depends_on = [ + google_service_networking_connection.looker_vpc_connection + ] +} + +resource "google_service_networking_connection" "looker_vpc_connection" { + network = google_compute_network.looker_network.id + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.looker_range.name] +} + +resource "google_compute_global_address" "looker_range" { + name = "tf-test-looker-range%{random_suffix}" + purpose = "VPC_PEERING" + address_type = "INTERNAL" + prefix_length = 20 + network = google_compute_network.looker_network.id +} + +data "google_project" "project" {} + +resource "google_compute_network" "looker_network" { + name = "tf-test-looker-network%{random_suffix}" + auto_create_subnetworks = false +} + +resource "google_kms_crypto_key_iam_member" "crypto_key" { + crypto_key_id = "%{kms_key_name}" + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-looker.iam.gserviceaccount.com" +} +`, context) +} + +func testAccCheckLookerInstanceDestroyProducer(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_looker_instance" { + continue + } + if strings.HasPrefix(name, "data.") { + continue + } + + config := acctest.GoogleProviderConfig(t) + + url, err := tpgresource.ReplaceVarsForTest(config, rs, "{{LookerBasePath}}projects/{{project}}/locations/{{region}}/instances/{{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, + ErrorAbortPredicates: []transport_tpg.RetryErrorPredicateFunc{transport_tpg.Is429QuotaError}, + }) + if err == nil { + return fmt.Errorf("LookerInstance still exists at %s", url) + } + } + + return nil + } +} diff --git a/google/resource_looker_instance_test.go b/google/resource_looker_instance_test.go new file mode 100644 index 00000000000..9785d930a8f --- /dev/null +++ b/google/resource_looker_instance_test.go @@ -0,0 +1,44 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package google + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccLookerInstance_update(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": RandString(t, 10), + } + + VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckLookerInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccLookerInstance_lookerInstanceBasicExample(context), + }, + { + ResourceName: "google_looker_instance.looker-instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"oauth_config", "region"}, + }, + { + Config: testAccLookerInstance_lookerInstanceFullExample(context), + }, + { + ResourceName: "google_looker_instance.looker-instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"oauth_config", "region"}, + }, + }, + }) +} diff --git a/google/services/looker/looker_operation.go b/google/services/looker/looker_operation.go new file mode 100644 index 00000000000..c8e96c3d6fd --- /dev/null +++ b/google/services/looker/looker_operation.go @@ -0,0 +1,88 @@ +// 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 looker + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/hashicorp/terraform-provider-google/google/tpgresource" + transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" +) + +type LookerOperationWaiter struct { + Config *transport_tpg.Config + UserAgent string + Project string + tpgresource.CommonOperationWaiter +} + +func (w *LookerOperationWaiter) 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.LookerBasePath, w.CommonOperationWaiter.Op.Name) + + return transport_tpg.SendRequest(transport_tpg.SendRequestOptions{ + Config: w.Config, + Method: "GET", + Project: w.Project, + RawURL: url, + UserAgent: w.UserAgent, + ErrorAbortPredicates: []transport_tpg.RetryErrorPredicateFunc{transport_tpg.Is429QuotaError}, + }) +} + +func createLookerWaiter(config *transport_tpg.Config, op map[string]interface{}, project, activity, userAgent string) (*LookerOperationWaiter, error) { + w := &LookerOperationWaiter{ + Config: config, + UserAgent: userAgent, + Project: project, + } + if err := w.CommonOperationWaiter.SetOp(op); err != nil { + return nil, err + } + return w, nil +} + +// nolint: deadcode,unused +func LookerOperationWaitTimeWithResponse(config *transport_tpg.Config, op map[string]interface{}, response *map[string]interface{}, project, activity, userAgent string, timeout time.Duration) error { + w, err := createLookerWaiter(config, op, project, activity, userAgent) + if err != nil { + return err + } + if err := tpgresource.OperationWait(w, activity, timeout, config.PollInterval); err != nil { + return err + } + return json.Unmarshal([]byte(w.CommonOperationWaiter.Op.Response), response) +} + +func LookerOperationWaitTime(config *transport_tpg.Config, op map[string]interface{}, project, 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 := createLookerWaiter(config, op, project, activity, userAgent) + if err != nil { + // If w is nil, the op was synchronous. + return err + } + return tpgresource.OperationWait(w, activity, timeout, config.PollInterval) +} diff --git a/google/services/looker/resource_looker_instance.go b/google/services/looker/resource_looker_instance.go new file mode 100644 index 00000000000..9e52f9fe24c --- /dev/null +++ b/google/services/looker/resource_looker_instance.go @@ -0,0 +1,1841 @@ +// 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 looker + +import ( + "fmt" + "log" + "reflect" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + + "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 ResourceLookerInstance() *schema.Resource { + return &schema.Resource{ + Create: resourceLookerInstanceCreate, + Read: resourceLookerInstanceRead, + Update: resourceLookerInstanceUpdate, + Delete: resourceLookerInstanceDelete, + + Importer: &schema.ResourceImporter{ + State: resourceLookerInstanceImport, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(90 * time.Minute), + Update: schema.DefaultTimeout(90 * time.Minute), + Delete: schema.DefaultTimeout(90 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: verify.ValidateRegexp(`^[a-z][a-z0-9-]{0,39}[a-z0-9]$`), + Description: `The ID of the instance or a fully qualified identifier for the instance.`, + }, + "admin_settings": { + Type: schema.TypeList, + Optional: true, + Description: `Looker instance Admin settings.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allowed_email_domains": { + Type: schema.TypeList, + Optional: true, + Description: `Email domain allowlist for the instance. + +Define the email domains to which your users can deliver Looker (Google Cloud core) content. +Updating this list will restart the instance. Updating the allowed email domains from terraform +means the value provided will be considered as the entire list and not an amendment to the +existing list of allowed email domains.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "consumer_network": { + Type: schema.TypeString, + Optional: true, + Description: `Network name in the consumer project in the format of: projects/{project}/global/networks/{network} +Note that the consumer network may be in a different GCP project than the consumer +project that is hosting the Looker Instance.`, + }, + "deny_maintenance_period": { + Type: schema.TypeList, + Optional: true, + Description: `Maintenance denial period for this instance. + +You must allow at least 14 days of maintenance availability +between any two deny maintenance periods.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "end_date": { + Type: schema.TypeList, + Required: true, + Description: `Required. Start date of the deny maintenance period`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "day": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 32), + Description: `Day of a month. Must be from 1 to 31 and valid for the year and month, or 0 +to specify a year by itself or a year and month where the day isn't significant.`, + }, + "month": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 13), + Description: `Month of a year. Must be from 1 to 12, or 0 to specify a year without a +month and day.`, + }, + "year": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 10000), + Description: `Year of the date. Must be from 1 to 9999, or 0 to specify a date without +a year.`, + }, + }, + }, + }, + "start_date": { + Type: schema.TypeList, + Required: true, + Description: `Required. Start date of the deny maintenance period`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "day": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 32), + Description: `Day of a month. Must be from 1 to 31 and valid for the year and month, or 0 +to specify a year by itself or a year and month where the day isn't significant.`, + }, + "month": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 13), + Description: `Month of a year. Must be from 1 to 12, or 0 to specify a year without a +month and day.`, + }, + "year": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 10000), + Description: `Year of the date. Must be from 1 to 9999, or 0 to specify a date without +a year.`, + }, + }, + }, + }, + "time": { + Type: schema.TypeList, + Required: true, + Description: `Required. Start time of the window in UTC time.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "hours": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 23), + Description: `Hours of day in 24 hour format. Should be from 0 to 23.`, + }, + "minutes": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 60), + Description: `Minutes of hour of day. Must be from 0 to 59.`, + }, + "nanos": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 999999999), + Description: `Fractions of seconds in nanoseconds. Must be from 0 to 999,999,999.`, + }, + "seconds": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 60), + Description: `Seconds of minutes of the time. Must normally be from 0 to 59.`, + }, + }, + }, + }, + }, + }, + }, + "encryption_config": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Description: `Looker instance encryption settings.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "kms_key_name": { + Type: schema.TypeString, + Optional: true, + Description: `Name of the customer managed encryption key (CMEK) in KMS.`, + }, + "kms_key_name_version": { + Type: schema.TypeString, + Computed: true, + Description: `Full name and version of the CMEK key currently in use to encrypt Looker data.`, + }, + "kms_key_state": { + Type: schema.TypeString, + Computed: true, + Description: `Status of the customer managed encryption key (CMEK) in KMS.`, + }, + }, + }, + }, + "maintenance_window": { + Type: schema.TypeList, + Optional: true, + Description: `Maintenance window for an instance. + +Maintenance of your instance takes place once a month, and will require +your instance to be restarted during updates, which will temporarily +disrupt service.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "day_of_week": { + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidateEnum([]string{"MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"}), + Description: `Required. Day of the week for this MaintenanceWindow (in UTC). + +- MONDAY: Monday +- TUESDAY: Tuesday +- WEDNESDAY: Wednesday +- THURSDAY: Thursday +- FRIDAY: Friday +- SATURDAY: Saturday +- SUNDAY: Sunday Possible values: ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"]`, + }, + "start_time": { + Type: schema.TypeList, + Required: true, + Description: `Required. Start time of the window in UTC time.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "hours": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 23), + Description: `Hours of day in 24 hour format. Should be from 0 to 23.`, + }, + "minutes": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 60), + Description: `Minutes of hour of day. Must be from 0 to 59.`, + }, + "nanos": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 999999999), + Description: `Fractions of seconds in nanoseconds. Must be from 0 to 999,999,999.`, + }, + "seconds": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 60), + Description: `Seconds of minutes of the time. Must normally be from 0 to 59.`, + }, + }, + }, + }, + }, + }, + }, + "oauth_config": { + Type: schema.TypeList, + Optional: true, + Description: `Looker Instance OAuth login settings.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "client_id": { + Type: schema.TypeString, + Required: true, + Description: `The client ID for the Oauth config.`, + }, + "client_secret": { + Type: schema.TypeString, + Required: true, + Description: `The client secret for the Oauth config.`, + }, + }, + }, + }, + "platform_edition": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: verify.ValidateEnum([]string{"LOOKER_CORE_TRIAL", "LOOKER_CORE_STANDARD", "LOOKER_CORE_STANDARD_ANNUAL", "LOOKER_CORE_ENTERPRISE_ANNUAL", "LOOKER_CORE_EMBED_ANNUAL", "LOOKER_MODELER", ""}), + Description: `Platform editions for a Looker instance. Each edition maps to a set of instance features, like its size. Must be one of these values: +- LOOKER_CORE_TRIAL: trial instance +- LOOKER_CORE_STANDARD: pay as you go standard instance +- LOOKER_CORE_STANDARD_ANNUAL: subscription standard instance +- LOOKER_CORE_ENTERPRISE_ANNUAL: subscription enterprise instance +- LOOKER_CORE_EMBED_ANNUAL: subscription embed instance +- LOOKER_MODELER: standalone modeling service Default value: "LOOKER_CORE_TRIAL" Possible values: ["LOOKER_CORE_TRIAL", "LOOKER_CORE_STANDARD", "LOOKER_CORE_STANDARD_ANNUAL", "LOOKER_CORE_ENTERPRISE_ANNUAL", "LOOKER_CORE_EMBED_ANNUAL", "LOOKER_MODELER"]`, + Default: "LOOKER_CORE_TRIAL", + }, + "private_ip_enabled": { + Type: schema.TypeBool, + Optional: true, + Description: `Whether private IP is enabled on the Looker instance.`, + Default: false, + }, + "public_ip_enabled": { + Type: schema.TypeBool, + Optional: true, + Description: `Whether public IP is enabled on the Looker instance.`, + Default: true, + }, + "region": { + Type: schema.TypeString, + Computed: true, + Optional: true, + ForceNew: true, + Description: `The name of the Looker region of the instance.`, + }, + "reserved_range": { + Type: schema.TypeString, + Optional: true, + Description: `Name of a reserved IP address range within the consumer network, to be used for +private service access connection. User may or may not specify this in a request.`, + }, + "user_metadata": { + Type: schema.TypeList, + Optional: true, + Description: `Metadata about users for a Looker instance. + +These settings are only available when platform edition LOOKER_CORE_STANDARD is set. + +There are ten Standard and two Developer users included in the cost of the product. +You can allocate additional Standard, Viewer, and Developer users for this instance. +It is an optional step and can be modified later. + +With the Standard edition of Looker (Google Cloud core), you can provision up to 50 +total users, distributed across Viewer, Standard, and Developer.`, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "additional_developer_user_count": { + Type: schema.TypeInt, + Optional: true, + Description: `Number of additional Developer Users to allocate to the Looker Instance.`, + }, + "additional_standard_user_count": { + Type: schema.TypeInt, + Optional: true, + Description: `Number of additional Standard Users to allocate to the Looker Instance.`, + }, + "additional_viewer_user_count": { + Type: schema.TypeInt, + Optional: true, + Description: `Number of additional Viewer Users to allocate to the Looker Instance.`, + }, + }, + }, + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: `The time the instance was created in RFC3339 UTC "Zulu" format, +accurate to nanoseconds.`, + }, + "egress_public_ip": { + Type: schema.TypeString, + Computed: true, + Description: `Public Egress IP (IPv4).`, + }, + "ingress_private_ip": { + Type: schema.TypeString, + Computed: true, + Description: `Private Ingress IP (IPv4).`, + }, + "ingress_public_ip": { + Type: schema.TypeString, + Computed: true, + Description: `Public Ingress IP (IPv4).`, + }, + "looker_uri": { + Type: schema.TypeString, + Computed: true, + Description: `Looker instance URI which can be used to access the Looker Instance UI.`, + }, + "looker_version": { + Type: schema.TypeString, + Computed: true, + Description: `The Looker version that the instance is using.`, + }, + "update_time": { + Type: schema.TypeString, + Computed: true, + Description: `The time the instance was updated in RFC3339 UTC "Zulu" format, +accurate to nanoseconds.`, + }, + "project": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + }, + UseJSONNumber: true, + } +} + +func resourceLookerInstanceCreate(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{}) + adminSettingsProp, err := expandLookerInstanceAdminSettings(d.Get("admin_settings"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("admin_settings"); !tpgresource.IsEmptyValue(reflect.ValueOf(adminSettingsProp)) && (ok || !reflect.DeepEqual(v, adminSettingsProp)) { + obj["adminSettings"] = adminSettingsProp + } + consumerNetworkProp, err := expandLookerInstanceConsumerNetwork(d.Get("consumer_network"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("consumer_network"); !tpgresource.IsEmptyValue(reflect.ValueOf(consumerNetworkProp)) && (ok || !reflect.DeepEqual(v, consumerNetworkProp)) { + obj["consumerNetwork"] = consumerNetworkProp + } + denyMaintenancePeriodProp, err := expandLookerInstanceDenyMaintenancePeriod(d.Get("deny_maintenance_period"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("deny_maintenance_period"); !tpgresource.IsEmptyValue(reflect.ValueOf(denyMaintenancePeriodProp)) && (ok || !reflect.DeepEqual(v, denyMaintenancePeriodProp)) { + obj["denyMaintenancePeriod"] = denyMaintenancePeriodProp + } + encryptionConfigProp, err := expandLookerInstanceEncryptionConfig(d.Get("encryption_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("encryption_config"); !tpgresource.IsEmptyValue(reflect.ValueOf(encryptionConfigProp)) && (ok || !reflect.DeepEqual(v, encryptionConfigProp)) { + obj["encryptionConfig"] = encryptionConfigProp + } + maintenanceWindowProp, err := expandLookerInstanceMaintenanceWindow(d.Get("maintenance_window"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("maintenance_window"); !tpgresource.IsEmptyValue(reflect.ValueOf(maintenanceWindowProp)) && (ok || !reflect.DeepEqual(v, maintenanceWindowProp)) { + obj["maintenanceWindow"] = maintenanceWindowProp + } + oauthConfigProp, err := expandLookerInstanceOauthConfig(d.Get("oauth_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("oauth_config"); !tpgresource.IsEmptyValue(reflect.ValueOf(oauthConfigProp)) && (ok || !reflect.DeepEqual(v, oauthConfigProp)) { + obj["oauthConfig"] = oauthConfigProp + } + platformEditionProp, err := expandLookerInstancePlatformEdition(d.Get("platform_edition"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("platform_edition"); !tpgresource.IsEmptyValue(reflect.ValueOf(platformEditionProp)) && (ok || !reflect.DeepEqual(v, platformEditionProp)) { + obj["platformEdition"] = platformEditionProp + } + privateIpEnabledProp, err := expandLookerInstancePrivateIpEnabled(d.Get("private_ip_enabled"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("private_ip_enabled"); !tpgresource.IsEmptyValue(reflect.ValueOf(privateIpEnabledProp)) && (ok || !reflect.DeepEqual(v, privateIpEnabledProp)) { + obj["privateIpEnabled"] = privateIpEnabledProp + } + publicIpEnabledProp, err := expandLookerInstancePublicIpEnabled(d.Get("public_ip_enabled"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("public_ip_enabled"); !tpgresource.IsEmptyValue(reflect.ValueOf(publicIpEnabledProp)) && (ok || !reflect.DeepEqual(v, publicIpEnabledProp)) { + obj["publicIpEnabled"] = publicIpEnabledProp + } + reservedRangeProp, err := expandLookerInstanceReservedRange(d.Get("reserved_range"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("reserved_range"); !tpgresource.IsEmptyValue(reflect.ValueOf(reservedRangeProp)) && (ok || !reflect.DeepEqual(v, reservedRangeProp)) { + obj["reservedRange"] = reservedRangeProp + } + userMetadataProp, err := expandLookerInstanceUserMetadata(d.Get("user_metadata"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("user_metadata"); !tpgresource.IsEmptyValue(reflect.ValueOf(userMetadataProp)) && (ok || !reflect.DeepEqual(v, userMetadataProp)) { + obj["userMetadata"] = userMetadataProp + } + + url, err := tpgresource.ReplaceVars(d, config, "{{LookerBasePath}}projects/{{project}}/locations/{{region}}/instances?instanceId={{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new Instance: %#v", obj) + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Instance: %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), + ErrorAbortPredicates: []transport_tpg.RetryErrorPredicateFunc{transport_tpg.Is429QuotaError}, + }) + if err != nil { + return fmt.Errorf("Error creating Instance: %s", err) + } + + // Store the ID now + id, err := tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{region}}/instances/{{name}}") + 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 = LookerOperationWaitTimeWithResponse( + config, res, &opRes, project, "Creating Instance", userAgent, + d.Timeout(schema.TimeoutCreate)) + if err != nil { + // The resource didn't actually create + d.SetId("") + + return fmt.Errorf("Error waiting to create Instance: %s", err) + } + + // This may have caused the ID to update - update it if so. + id, err = tpgresource.ReplaceVars(d, config, "projects/{{project}}/locations/{{region}}/instances/{{name}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + log.Printf("[DEBUG] Finished creating Instance %q: %#v", d.Id(), res) + + return resourceLookerInstanceRead(d, meta) +} + +func resourceLookerInstanceRead(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, "{{LookerBasePath}}projects/{{project}}/locations/{{region}}/instances/{{name}}") + if err != nil { + return err + } + + billingProject := "" + + project, err := tpgresource.GetProject(d, config) + if err != nil { + return fmt.Errorf("Error fetching project for Instance: %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, + ErrorAbortPredicates: []transport_tpg.RetryErrorPredicateFunc{transport_tpg.Is429QuotaError}, + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("LookerInstance %q", d.Id())) + } + + if err := d.Set("project", project); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + + if err := d.Set("admin_settings", flattenLookerInstanceAdminSettings(res["adminSettings"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("consumer_network", flattenLookerInstanceConsumerNetwork(res["consumerNetwork"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("create_time", flattenLookerInstanceCreateTime(res["createTime"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("deny_maintenance_period", flattenLookerInstanceDenyMaintenancePeriod(res["denyMaintenancePeriod"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("egress_public_ip", flattenLookerInstanceEgressPublicIp(res["egressPublicIp"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("encryption_config", flattenLookerInstanceEncryptionConfig(res["encryptionConfig"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("ingress_private_ip", flattenLookerInstanceIngressPrivateIp(res["ingressPrivateIp"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("ingress_public_ip", flattenLookerInstanceIngressPublicIp(res["ingressPublicIp"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("looker_version", flattenLookerInstanceLookerVersion(res["lookerVersion"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("looker_uri", flattenLookerInstanceLookerUri(res["lookerUri"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("maintenance_window", flattenLookerInstanceMaintenanceWindow(res["maintenanceWindow"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("platform_edition", flattenLookerInstancePlatformEdition(res["platformEdition"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("private_ip_enabled", flattenLookerInstancePrivateIpEnabled(res["privateIpEnabled"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("public_ip_enabled", flattenLookerInstancePublicIpEnabled(res["publicIpEnabled"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("reserved_range", flattenLookerInstanceReservedRange(res["reservedRange"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("update_time", flattenLookerInstanceUpdateTime(res["updateTime"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + if err := d.Set("user_metadata", flattenLookerInstanceUserMetadata(res["userMetadata"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } + + return nil +} + +func resourceLookerInstanceUpdate(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 Instance: %s", err) + } + billingProject = project + + obj := make(map[string]interface{}) + adminSettingsProp, err := expandLookerInstanceAdminSettings(d.Get("admin_settings"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("admin_settings"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, adminSettingsProp)) { + obj["adminSettings"] = adminSettingsProp + } + consumerNetworkProp, err := expandLookerInstanceConsumerNetwork(d.Get("consumer_network"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("consumer_network"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, consumerNetworkProp)) { + obj["consumerNetwork"] = consumerNetworkProp + } + denyMaintenancePeriodProp, err := expandLookerInstanceDenyMaintenancePeriod(d.Get("deny_maintenance_period"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("deny_maintenance_period"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, denyMaintenancePeriodProp)) { + obj["denyMaintenancePeriod"] = denyMaintenancePeriodProp + } + encryptionConfigProp, err := expandLookerInstanceEncryptionConfig(d.Get("encryption_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("encryption_config"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, encryptionConfigProp)) { + obj["encryptionConfig"] = encryptionConfigProp + } + maintenanceWindowProp, err := expandLookerInstanceMaintenanceWindow(d.Get("maintenance_window"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("maintenance_window"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, maintenanceWindowProp)) { + obj["maintenanceWindow"] = maintenanceWindowProp + } + oauthConfigProp, err := expandLookerInstanceOauthConfig(d.Get("oauth_config"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("oauth_config"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, oauthConfigProp)) { + obj["oauthConfig"] = oauthConfigProp + } + privateIpEnabledProp, err := expandLookerInstancePrivateIpEnabled(d.Get("private_ip_enabled"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("private_ip_enabled"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, privateIpEnabledProp)) { + obj["privateIpEnabled"] = privateIpEnabledProp + } + publicIpEnabledProp, err := expandLookerInstancePublicIpEnabled(d.Get("public_ip_enabled"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("public_ip_enabled"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, publicIpEnabledProp)) { + obj["publicIpEnabled"] = publicIpEnabledProp + } + reservedRangeProp, err := expandLookerInstanceReservedRange(d.Get("reserved_range"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("reserved_range"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, reservedRangeProp)) { + obj["reservedRange"] = reservedRangeProp + } + userMetadataProp, err := expandLookerInstanceUserMetadata(d.Get("user_metadata"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("user_metadata"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, userMetadataProp)) { + obj["userMetadata"] = userMetadataProp + } + + url, err := tpgresource.ReplaceVars(d, config, "{{LookerBasePath}}projects/{{project}}/locations/{{region}}/instances/{{name}}") + if err != nil { + return err + } + + log.Printf("[DEBUG] Updating Instance %q: %#v", d.Id(), obj) + updateMask := []string{} + + if d.HasChange("admin_settings") { + updateMask = append(updateMask, "admin_settings.allowed_email_domains") + } + + if d.HasChange("consumer_network") { + updateMask = append(updateMask, "consumerNetwork") + } + + if d.HasChange("deny_maintenance_period") { + updateMask = append(updateMask, "denyMaintenancePeriod") + } + + if d.HasChange("encryption_config") { + updateMask = append(updateMask, "encryptionConfig") + } + + if d.HasChange("maintenance_window") { + updateMask = append(updateMask, "maintenanceWindow") + } + + if d.HasChange("oauth_config") { + updateMask = append(updateMask, "oauthConfig") + } + + if d.HasChange("private_ip_enabled") { + updateMask = append(updateMask, "privateIpEnabled") + } + + if d.HasChange("public_ip_enabled") { + updateMask = append(updateMask, "publicIpEnabled") + } + + if d.HasChange("reserved_range") { + updateMask = append(updateMask, "reservedRange") + } + + if d.HasChange("user_metadata") { + updateMask = append(updateMask, "userMetadata") + } + // 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), + ErrorAbortPredicates: []transport_tpg.RetryErrorPredicateFunc{transport_tpg.Is429QuotaError}, + }) + + if err != nil { + return fmt.Errorf("Error updating Instance %q: %s", d.Id(), err) + } else { + log.Printf("[DEBUG] Finished updating Instance %q: %#v", d.Id(), res) + } + + err = LookerOperationWaitTime( + config, res, project, "Updating Instance", userAgent, + d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return err + } + + return resourceLookerInstanceRead(d, meta) +} + +func resourceLookerInstanceDelete(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 Instance: %s", err) + } + billingProject = project + + url, err := tpgresource.ReplaceVars(d, config, "{{LookerBasePath}}projects/{{project}}/locations/{{region}}/instances/{{name}}") + if err != nil { + return err + } + + var obj map[string]interface{} + log.Printf("[DEBUG] Deleting Instance %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), + ErrorAbortPredicates: []transport_tpg.RetryErrorPredicateFunc{transport_tpg.Is429QuotaError}, + }) + if err != nil { + return transport_tpg.HandleNotFoundError(err, d, "Instance") + } + + err = LookerOperationWaitTime( + config, res, project, "Deleting Instance", userAgent, + d.Timeout(schema.TimeoutDelete)) + + if err != nil { + return err + } + + log.Printf("[DEBUG] Finished deleting Instance %q: %#v", d.Id(), res) + return nil +} + +func resourceLookerInstanceImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + config := meta.(*transport_tpg.Config) + if err := tpgresource.ParseImportId([]string{ + "projects/(?P[^/]+)/locations/(?P[^/]+)/instances/(?P[^/]+)", + "(?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/{{region}}/instances/{{name}}") + if err != nil { + return nil, fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return []*schema.ResourceData{d}, nil +} + +func flattenLookerInstanceAdminSettings(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["allowed_email_domains"] = + flattenLookerInstanceAdminSettingsAllowedEmailDomains(original["allowedEmailDomains"], d, config) + return []interface{}{transformed} +} +func flattenLookerInstanceAdminSettingsAllowedEmailDomains(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLookerInstanceConsumerNetwork(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLookerInstanceCreateTime(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLookerInstanceDenyMaintenancePeriod(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["start_date"] = + flattenLookerInstanceDenyMaintenancePeriodStartDate(original["startDate"], d, config) + transformed["end_date"] = + flattenLookerInstanceDenyMaintenancePeriodEndDate(original["endDate"], d, config) + transformed["time"] = + flattenLookerInstanceDenyMaintenancePeriodTime(original["time"], d, config) + return []interface{}{transformed} +} +func flattenLookerInstanceDenyMaintenancePeriodStartDate(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["year"] = + flattenLookerInstanceDenyMaintenancePeriodStartDateYear(original["year"], d, config) + transformed["month"] = + flattenLookerInstanceDenyMaintenancePeriodStartDateMonth(original["month"], d, config) + transformed["day"] = + flattenLookerInstanceDenyMaintenancePeriodStartDateDay(original["day"], d, config) + return []interface{}{transformed} +} +func flattenLookerInstanceDenyMaintenancePeriodStartDateYear(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 flattenLookerInstanceDenyMaintenancePeriodStartDateMonth(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 flattenLookerInstanceDenyMaintenancePeriodStartDateDay(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 flattenLookerInstanceDenyMaintenancePeriodEndDate(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["year"] = + flattenLookerInstanceDenyMaintenancePeriodEndDateYear(original["year"], d, config) + transformed["month"] = + flattenLookerInstanceDenyMaintenancePeriodEndDateMonth(original["month"], d, config) + transformed["day"] = + flattenLookerInstanceDenyMaintenancePeriodEndDateDay(original["day"], d, config) + return []interface{}{transformed} +} +func flattenLookerInstanceDenyMaintenancePeriodEndDateYear(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 flattenLookerInstanceDenyMaintenancePeriodEndDateMonth(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 flattenLookerInstanceDenyMaintenancePeriodEndDateDay(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 flattenLookerInstanceDenyMaintenancePeriodTime(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["hours"] = + flattenLookerInstanceDenyMaintenancePeriodTimeHours(original["hours"], d, config) + transformed["minutes"] = + flattenLookerInstanceDenyMaintenancePeriodTimeMinutes(original["minutes"], d, config) + transformed["seconds"] = + flattenLookerInstanceDenyMaintenancePeriodTimeSeconds(original["seconds"], d, config) + transformed["nanos"] = + flattenLookerInstanceDenyMaintenancePeriodTimeNanos(original["nanos"], d, config) + return []interface{}{transformed} +} +func flattenLookerInstanceDenyMaintenancePeriodTimeHours(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 flattenLookerInstanceDenyMaintenancePeriodTimeMinutes(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 flattenLookerInstanceDenyMaintenancePeriodTimeSeconds(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 flattenLookerInstanceDenyMaintenancePeriodTimeNanos(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 flattenLookerInstanceEgressPublicIp(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLookerInstanceEncryptionConfig(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["kms_key_name"] = + flattenLookerInstanceEncryptionConfigKmsKeyName(original["kmsKeyName"], d, config) + transformed["kms_key_state"] = + flattenLookerInstanceEncryptionConfigKmsKeyState(original["kmsKeyState"], d, config) + transformed["kms_key_name_version"] = + flattenLookerInstanceEncryptionConfigKmsKeyNameVersion(original["kmsKeyNameVersion"], d, config) + return []interface{}{transformed} +} +func flattenLookerInstanceEncryptionConfigKmsKeyName(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLookerInstanceEncryptionConfigKmsKeyState(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLookerInstanceEncryptionConfigKmsKeyNameVersion(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLookerInstanceIngressPrivateIp(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLookerInstanceIngressPublicIp(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLookerInstanceLookerVersion(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLookerInstanceLookerUri(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLookerInstanceMaintenanceWindow(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_of_week"] = + flattenLookerInstanceMaintenanceWindowDayOfWeek(original["dayOfWeek"], d, config) + transformed["start_time"] = + flattenLookerInstanceMaintenanceWindowStartTime(original["startTime"], d, config) + return []interface{}{transformed} +} +func flattenLookerInstanceMaintenanceWindowDayOfWeek(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLookerInstanceMaintenanceWindowStartTime(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["hours"] = + flattenLookerInstanceMaintenanceWindowStartTimeHours(original["hours"], d, config) + transformed["minutes"] = + flattenLookerInstanceMaintenanceWindowStartTimeMinutes(original["minutes"], d, config) + transformed["seconds"] = + flattenLookerInstanceMaintenanceWindowStartTimeSeconds(original["seconds"], d, config) + transformed["nanos"] = + flattenLookerInstanceMaintenanceWindowStartTimeNanos(original["nanos"], d, config) + return []interface{}{transformed} +} +func flattenLookerInstanceMaintenanceWindowStartTimeHours(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 flattenLookerInstanceMaintenanceWindowStartTimeMinutes(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 flattenLookerInstanceMaintenanceWindowStartTimeSeconds(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 flattenLookerInstanceMaintenanceWindowStartTimeNanos(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 flattenLookerInstancePlatformEdition(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLookerInstancePrivateIpEnabled(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLookerInstancePublicIpEnabled(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLookerInstanceReservedRange(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLookerInstanceUpdateTime(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} { + return v +} + +func flattenLookerInstanceUserMetadata(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["additional_viewer_user_count"] = + flattenLookerInstanceUserMetadataAdditionalViewerUserCount(original["additionalViewerUserCount"], d, config) + transformed["additional_standard_user_count"] = + flattenLookerInstanceUserMetadataAdditionalStandardUserCount(original["additionalStandardUserCount"], d, config) + transformed["additional_developer_user_count"] = + flattenLookerInstanceUserMetadataAdditionalDeveloperUserCount(original["additionalDeveloperUserCount"], d, config) + return []interface{}{transformed} +} +func flattenLookerInstanceUserMetadataAdditionalViewerUserCount(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 flattenLookerInstanceUserMetadataAdditionalStandardUserCount(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 flattenLookerInstanceUserMetadataAdditionalDeveloperUserCount(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 expandLookerInstanceAdminSettings(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{}) + + transformedAllowedEmailDomains, err := expandLookerInstanceAdminSettingsAllowedEmailDomains(original["allowed_email_domains"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAllowedEmailDomains); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["allowedEmailDomains"] = transformedAllowedEmailDomains + } + + return transformed, nil +} + +func expandLookerInstanceAdminSettingsAllowedEmailDomains(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceConsumerNetwork(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceDenyMaintenancePeriod(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{}) + + transformedStartDate, err := expandLookerInstanceDenyMaintenancePeriodStartDate(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 := expandLookerInstanceDenyMaintenancePeriodEndDate(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 + } + + transformedTime, err := expandLookerInstanceDenyMaintenancePeriodTime(original["time"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedTime); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["time"] = transformedTime + } + + return transformed, nil +} + +func expandLookerInstanceDenyMaintenancePeriodStartDate(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{}) + + transformedYear, err := expandLookerInstanceDenyMaintenancePeriodStartDateYear(original["year"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedYear); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["year"] = transformedYear + } + + transformedMonth, err := expandLookerInstanceDenyMaintenancePeriodStartDateMonth(original["month"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMonth); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["month"] = transformedMonth + } + + transformedDay, err := expandLookerInstanceDenyMaintenancePeriodStartDateDay(original["day"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDay); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["day"] = transformedDay + } + + return transformed, nil +} + +func expandLookerInstanceDenyMaintenancePeriodStartDateYear(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceDenyMaintenancePeriodStartDateMonth(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceDenyMaintenancePeriodStartDateDay(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceDenyMaintenancePeriodEndDate(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{}) + + transformedYear, err := expandLookerInstanceDenyMaintenancePeriodEndDateYear(original["year"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedYear); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["year"] = transformedYear + } + + transformedMonth, err := expandLookerInstanceDenyMaintenancePeriodEndDateMonth(original["month"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMonth); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["month"] = transformedMonth + } + + transformedDay, err := expandLookerInstanceDenyMaintenancePeriodEndDateDay(original["day"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDay); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["day"] = transformedDay + } + + return transformed, nil +} + +func expandLookerInstanceDenyMaintenancePeriodEndDateYear(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceDenyMaintenancePeriodEndDateMonth(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceDenyMaintenancePeriodEndDateDay(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceDenyMaintenancePeriodTime(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{}) + + transformedHours, err := expandLookerInstanceDenyMaintenancePeriodTimeHours(original["hours"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHours); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["hours"] = transformedHours + } + + transformedMinutes, err := expandLookerInstanceDenyMaintenancePeriodTimeMinutes(original["minutes"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMinutes); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["minutes"] = transformedMinutes + } + + transformedSeconds, err := expandLookerInstanceDenyMaintenancePeriodTimeSeconds(original["seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSeconds); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["seconds"] = transformedSeconds + } + + transformedNanos, err := expandLookerInstanceDenyMaintenancePeriodTimeNanos(original["nanos"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNanos); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["nanos"] = transformedNanos + } + + return transformed, nil +} + +func expandLookerInstanceDenyMaintenancePeriodTimeHours(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceDenyMaintenancePeriodTimeMinutes(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceDenyMaintenancePeriodTimeSeconds(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceDenyMaintenancePeriodTimeNanos(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceEncryptionConfig(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{}) + + transformedKmsKeyName, err := expandLookerInstanceEncryptionConfigKmsKeyName(original["kms_key_name"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedKmsKeyName); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["kmsKeyName"] = transformedKmsKeyName + } + + transformedKmsKeyState, err := expandLookerInstanceEncryptionConfigKmsKeyState(original["kms_key_state"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedKmsKeyState); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["kmsKeyState"] = transformedKmsKeyState + } + + transformedKmsKeyNameVersion, err := expandLookerInstanceEncryptionConfigKmsKeyNameVersion(original["kms_key_name_version"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedKmsKeyNameVersion); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["kmsKeyNameVersion"] = transformedKmsKeyNameVersion + } + + return transformed, nil +} + +func expandLookerInstanceEncryptionConfigKmsKeyName(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceEncryptionConfigKmsKeyState(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceEncryptionConfigKmsKeyNameVersion(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceMaintenanceWindow(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{}) + + transformedDayOfWeek, err := expandLookerInstanceMaintenanceWindowDayOfWeek(original["day_of_week"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedDayOfWeek); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["dayOfWeek"] = transformedDayOfWeek + } + + transformedStartTime, err := expandLookerInstanceMaintenanceWindowStartTime(original["start_time"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedStartTime); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["startTime"] = transformedStartTime + } + + return transformed, nil +} + +func expandLookerInstanceMaintenanceWindowDayOfWeek(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceMaintenanceWindowStartTime(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{}) + + transformedHours, err := expandLookerInstanceMaintenanceWindowStartTimeHours(original["hours"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedHours); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["hours"] = transformedHours + } + + transformedMinutes, err := expandLookerInstanceMaintenanceWindowStartTimeMinutes(original["minutes"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedMinutes); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["minutes"] = transformedMinutes + } + + transformedSeconds, err := expandLookerInstanceMaintenanceWindowStartTimeSeconds(original["seconds"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSeconds); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["seconds"] = transformedSeconds + } + + transformedNanos, err := expandLookerInstanceMaintenanceWindowStartTimeNanos(original["nanos"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNanos); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["nanos"] = transformedNanos + } + + return transformed, nil +} + +func expandLookerInstanceMaintenanceWindowStartTimeHours(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceMaintenanceWindowStartTimeMinutes(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceMaintenanceWindowStartTimeSeconds(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceMaintenanceWindowStartTimeNanos(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceOauthConfig(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{}) + + transformedClientId, err := expandLookerInstanceOauthConfigClientId(original["client_id"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedClientId); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["clientId"] = transformedClientId + } + + transformedClientSecret, err := expandLookerInstanceOauthConfigClientSecret(original["client_secret"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedClientSecret); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["clientSecret"] = transformedClientSecret + } + + return transformed, nil +} + +func expandLookerInstanceOauthConfigClientId(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceOauthConfigClientSecret(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstancePlatformEdition(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstancePrivateIpEnabled(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstancePublicIpEnabled(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceReservedRange(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceUserMetadata(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{}) + + transformedAdditionalViewerUserCount, err := expandLookerInstanceUserMetadataAdditionalViewerUserCount(original["additional_viewer_user_count"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAdditionalViewerUserCount); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["additionalViewerUserCount"] = transformedAdditionalViewerUserCount + } + + transformedAdditionalStandardUserCount, err := expandLookerInstanceUserMetadataAdditionalStandardUserCount(original["additional_standard_user_count"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAdditionalStandardUserCount); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["additionalStandardUserCount"] = transformedAdditionalStandardUserCount + } + + transformedAdditionalDeveloperUserCount, err := expandLookerInstanceUserMetadataAdditionalDeveloperUserCount(original["additional_developer_user_count"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAdditionalDeveloperUserCount); val.IsValid() && !tpgresource.IsEmptyValue(val) { + transformed["additionalDeveloperUserCount"] = transformedAdditionalDeveloperUserCount + } + + return transformed, nil +} + +func expandLookerInstanceUserMetadataAdditionalViewerUserCount(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceUserMetadataAdditionalStandardUserCount(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} + +func expandLookerInstanceUserMetadataAdditionalDeveloperUserCount(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) { + return v, nil +} diff --git a/google/services/looker/resource_looker_instance_sweeper.go b/google/services/looker/resource_looker_instance_sweeper.go new file mode 100644 index 00000000000..c6576dfbaa9 --- /dev/null +++ b/google/services/looker/resource_looker_instance_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 looker + +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("LookerInstance", testSweepLookerInstance) +} + +// At the time of writing, the CI only passes us-central1 as the region +func testSweepLookerInstance(region string) error { + resourceName := "LookerInstance" + 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://looker.googleapis.com/v1/projects/{{project}}/locations/{{region}}/instances", "?")[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["instances"] + 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://looker.googleapis.com/v1/projects/{{project}}/locations/{{region}}/instances/{{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/transport/config.go b/google/transport/config.go index 2f95335cdc8..200b132182e 100644 --- a/google/transport/config.go +++ b/google/transport/config.go @@ -244,6 +244,7 @@ type Config struct { IdentityPlatformBasePath string KMSBasePath string LoggingBasePath string + LookerBasePath string MemcacheBasePath string MLEngineBasePath string MonitoringBasePath string @@ -356,6 +357,7 @@ const IapBasePathKey = "Iap" const IdentityPlatformBasePathKey = "IdentityPlatform" const KMSBasePathKey = "KMS" const LoggingBasePathKey = "Logging" +const LookerBasePathKey = "Looker" const MemcacheBasePathKey = "Memcache" const MLEngineBasePathKey = "MLEngine" const MonitoringBasePathKey = "Monitoring" @@ -462,6 +464,7 @@ var DefaultBasePaths = map[string]string{ IdentityPlatformBasePathKey: "https://identitytoolkit.googleapis.com/v2/", KMSBasePathKey: "https://cloudkms.googleapis.com/v1/", LoggingBasePathKey: "https://logging.googleapis.com/v2/", + LookerBasePathKey: "https://looker.googleapis.com/v1/", MemcacheBasePathKey: "https://memcache.googleapis.com/v1/", MLEngineBasePathKey: "https://ml.googleapis.com/v1/", MonitoringBasePathKey: "https://monitoring.googleapis.com/", @@ -879,6 +882,11 @@ func HandleSDKDefaults(d *schema.ResourceData) error { "GOOGLE_LOGGING_CUSTOM_ENDPOINT", }, DefaultBasePaths[LoggingBasePathKey])) } + if d.Get("looker_custom_endpoint") == "" { + d.Set("looker_custom_endpoint", MultiEnvDefault([]string{ + "GOOGLE_LOOKER_CUSTOM_ENDPOINT", + }, DefaultBasePaths[LookerBasePathKey])) + } if d.Get("memcache_custom_endpoint") == "" { d.Set("memcache_custom_endpoint", MultiEnvDefault([]string{ "GOOGLE_MEMCACHE_CUSTOM_ENDPOINT", @@ -1926,6 +1934,7 @@ func ConfigureBasePaths(c *Config) { c.IdentityPlatformBasePath = DefaultBasePaths[IdentityPlatformBasePathKey] c.KMSBasePath = DefaultBasePaths[KMSBasePathKey] c.LoggingBasePath = DefaultBasePaths[LoggingBasePathKey] + c.LookerBasePath = DefaultBasePaths[LookerBasePathKey] c.MemcacheBasePath = DefaultBasePaths[MemcacheBasePathKey] c.MLEngineBasePath = DefaultBasePaths[MLEngineBasePathKey] c.MonitoringBasePath = DefaultBasePaths[MonitoringBasePathKey] diff --git a/website/docs/r/looker_instance.html.markdown b/website/docs/r/looker_instance.html.markdown new file mode 100644 index 00000000000..52f659a28fc --- /dev/null +++ b/website/docs/r/looker_instance.html.markdown @@ -0,0 +1,496 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** 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: "Looker (Google Cloud core)" +description: |- + A Google Cloud Looker instance. +--- + +# google\_looker\_instance + +A Google Cloud Looker instance. + + +To get more information about Instance, see: + +* [API documentation](https://cloud.google.com/looker/docs/reference/rest/v1/projects.locations.instances) +* How-to Guides + * [Create a Looker (Google Cloud core) instance](https://cloud.google.com/looker/docs/looker-core-instance-create) + * [Configure a Looker (Google Cloud core) instance](https://cloud.google.com/looker/docs/looker-core-instance-setup) + + +## Example Usage - Looker Instance Basic + + +```hcl +resource "google_looker_instance" "looker-instance" { + name = "my-instance" + platform_edition = "LOOKER_CORE_STANDARD" + region = "us-central1" + oauth_config { + client_id = "my-client-id" + client_secret = "my-client-secret" + } +} +``` + +## Example Usage - Looker Instance Full + + +```hcl +resource "google_looker_instance" "looker-instance" { + name = "my-instance" + platform_edition = "LOOKER_CORE_STANDARD" + region = "us-central1" + public_ip_enabled = true + admin_settings { + allowed_email_domains = ["google.com"] + } + // User metadata config is only available when platform edition is LOOKER_CORE_STANDARD. + user_metadata { + additional_developer_user_count = 10 + additional_standard_user_count = 10 + additional_viewer_user_count = 10 + } + maintenance_window { + day_of_week = "THURSDAY" + start_time { + hours = 22 + minutes = 0 + seconds = 0 + nanos = 0 + } + } + deny_maintenance_period { + start_date { + year = 2050 + month = 1 + day = 1 + } + end_date { + year = 2050 + month = 2 + day = 1 + } + time { + hours = 10 + minutes = 0 + seconds = 0 + nanos = 0 + } + } + oauth_config { + client_id = "my-client-id" + client_secret = "my-client-secret" + } +} +``` + +## Example Usage - Looker Instance Enterprise Full + + +```hcl +resource "google_looker_instance" "looker-instance" { + name = "my-instance" + platform_edition = "LOOKER_CORE_ENTERPRISE_ANNUAL" + region = "us-central1" + private_ip_enabled = true + public_ip_enabled = false + reserved_range = "${google_compute_global_address.looker_range.name}" + consumer_network = google_compute_network.looker_network.id + admin_settings { + allowed_email_domains = ["google.com"] + } + encryption_config { + kms_key_name = "looker-kms-key" + } + maintenance_window { + day_of_week = "THURSDAY" + start_time { + hours = 22 + minutes = 0 + seconds = 0 + nanos = 0 + } + } + deny_maintenance_period { + start_date { + year = 2050 + month = 1 + day = 1 + } + end_date { + year = 2050 + month = 2 + day = 1 + } + time { + hours = 10 + minutes = 0 + seconds = 0 + nanos = 0 + } + } + oauth_config { + client_id = "my-client-id" + client_secret = "my-client-secret" + } + depends_on = [ + google_service_networking_connection.looker_vpc_connection + ] +} + +resource "google_service_networking_connection" "looker_vpc_connection" { + network = google_compute_network.looker_network.id + service = "servicenetworking.googleapis.com" + reserved_peering_ranges = [google_compute_global_address.looker_range.name] +} + +resource "google_compute_global_address" "looker_range" { + name = "looker-range" + purpose = "VPC_PEERING" + address_type = "INTERNAL" + prefix_length = 20 + network = google_compute_network.looker_network.id +} + +data "google_project" "project" {} + +resource "google_compute_network" "looker_network" { + name = "looker-network" + auto_create_subnetworks = false +} + +resource "google_kms_crypto_key_iam_member" "crypto_key" { + crypto_key_id = "looker-kms-key" + role = "roles/cloudkms.cryptoKeyEncrypterDecrypter" + member = "serviceAccount:service-${data.google_project.project.number}@gcp-sa-looker.iam.gserviceaccount.com" +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `name` - + (Required) + The ID of the instance or a fully qualified identifier for the instance. + + +- - - + + +* `admin_settings` - + (Optional) + Looker instance Admin settings. + Structure is [documented below](#nested_admin_settings). + +* `consumer_network` - + (Optional) + Network name in the consumer project in the format of: projects/{project}/global/networks/{network} + Note that the consumer network may be in a different GCP project than the consumer + project that is hosting the Looker Instance. + +* `deny_maintenance_period` - + (Optional) + Maintenance denial period for this instance. + You must allow at least 14 days of maintenance availability + between any two deny maintenance periods. + Structure is [documented below](#nested_deny_maintenance_period). + +* `encryption_config` - + (Optional) + Looker instance encryption settings. + Structure is [documented below](#nested_encryption_config). + +* `maintenance_window` - + (Optional) + Maintenance window for an instance. + Maintenance of your instance takes place once a month, and will require + your instance to be restarted during updates, which will temporarily + disrupt service. + Structure is [documented below](#nested_maintenance_window). + +* `oauth_config` - + (Optional) + Looker Instance OAuth login settings. + Structure is [documented below](#nested_oauth_config). + +* `platform_edition` - + (Optional) + Platform editions for a Looker instance. Each edition maps to a set of instance features, like its size. Must be one of these values: + - LOOKER_CORE_TRIAL: trial instance + - LOOKER_CORE_STANDARD: pay as you go standard instance + - LOOKER_CORE_STANDARD_ANNUAL: subscription standard instance + - LOOKER_CORE_ENTERPRISE_ANNUAL: subscription enterprise instance + - LOOKER_CORE_EMBED_ANNUAL: subscription embed instance + - LOOKER_MODELER: standalone modeling service + Default value is `LOOKER_CORE_TRIAL`. + Possible values are: `LOOKER_CORE_TRIAL`, `LOOKER_CORE_STANDARD`, `LOOKER_CORE_STANDARD_ANNUAL`, `LOOKER_CORE_ENTERPRISE_ANNUAL`, `LOOKER_CORE_EMBED_ANNUAL`, `LOOKER_MODELER`. + +* `private_ip_enabled` - + (Optional) + Whether private IP is enabled on the Looker instance. + +* `public_ip_enabled` - + (Optional) + Whether public IP is enabled on the Looker instance. + +* `reserved_range` - + (Optional) + Name of a reserved IP address range within the consumer network, to be used for + private service access connection. User may or may not specify this in a request. + +* `user_metadata` - + (Optional) + Metadata about users for a Looker instance. + These settings are only available when platform edition LOOKER_CORE_STANDARD is set. + There are ten Standard and two Developer users included in the cost of the product. + You can allocate additional Standard, Viewer, and Developer users for this instance. + It is an optional step and can be modified later. + With the Standard edition of Looker (Google Cloud core), you can provision up to 50 + total users, distributed across Viewer, Standard, and Developer. + Structure is [documented below](#nested_user_metadata). + +* `region` - + (Optional) + The name of the Looker region of the instance. + +* `project` - (Optional) The ID of the project in which the resource belongs. + If it is not provided, the provider project is used. + + +The `admin_settings` block supports: + +* `allowed_email_domains` - + (Optional) + Email domain allowlist for the instance. + Define the email domains to which your users can deliver Looker (Google Cloud core) content. + Updating this list will restart the instance. Updating the allowed email domains from terraform + means the value provided will be considered as the entire list and not an amendment to the + existing list of allowed email domains. + +The `deny_maintenance_period` block supports: + +* `start_date` - + (Required) + Required. Start date of the deny maintenance period + Structure is [documented below](#nested_start_date). + +* `end_date` - + (Required) + Required. Start date of the deny maintenance period + Structure is [documented below](#nested_end_date). + +* `time` - + (Required) + Required. Start time of the window in UTC time. + Structure is [documented below](#nested_time). + + +The `start_date` block supports: + +* `year` - + (Optional) + Year of the date. Must be from 1 to 9999, or 0 to specify a date without + a year. + +* `month` - + (Optional) + Month of a year. Must be from 1 to 12, or 0 to specify a year without a + month and day. + +* `day` - + (Optional) + Day of a month. Must be from 1 to 31 and valid for the year and month, or 0 + to specify a year by itself or a year and month where the day isn't significant. + +The `end_date` block supports: + +* `year` - + (Optional) + Year of the date. Must be from 1 to 9999, or 0 to specify a date without + a year. + +* `month` - + (Optional) + Month of a year. Must be from 1 to 12, or 0 to specify a year without a + month and day. + +* `day` - + (Optional) + Day of a month. Must be from 1 to 31 and valid for the year and month, or 0 + to specify a year by itself or a year and month where the day isn't significant. + +The `time` block supports: + +* `hours` - + (Optional) + Hours of day in 24 hour format. Should be from 0 to 23. + +* `minutes` - + (Optional) + Minutes of hour of day. Must be from 0 to 59. + +* `seconds` - + (Optional) + Seconds of minutes of the time. Must normally be from 0 to 59. + +* `nanos` - + (Optional) + Fractions of seconds in nanoseconds. Must be from 0 to 999,999,999. + +The `encryption_config` block supports: + +* `kms_key_name` - + (Optional) + Name of the customer managed encryption key (CMEK) in KMS. + +* `kms_key_state` - + (Output) + Status of the customer managed encryption key (CMEK) in KMS. + +* `kms_key_name_version` - + (Output) + Full name and version of the CMEK key currently in use to encrypt Looker data. + +The `maintenance_window` block supports: + +* `day_of_week` - + (Required) + Required. Day of the week for this MaintenanceWindow (in UTC). + - MONDAY: Monday + - TUESDAY: Tuesday + - WEDNESDAY: Wednesday + - THURSDAY: Thursday + - FRIDAY: Friday + - SATURDAY: Saturday + - SUNDAY: Sunday + Possible values are: `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`, `SATURDAY`, `SUNDAY`. + +* `start_time` - + (Required) + Required. Start time of the window in UTC time. + Structure is [documented below](#nested_start_time). + + +The `start_time` block supports: + +* `hours` - + (Optional) + Hours of day in 24 hour format. Should be from 0 to 23. + +* `minutes` - + (Optional) + Minutes of hour of day. Must be from 0 to 59. + +* `seconds` - + (Optional) + Seconds of minutes of the time. Must normally be from 0 to 59. + +* `nanos` - + (Optional) + Fractions of seconds in nanoseconds. Must be from 0 to 999,999,999. + +The `oauth_config` block supports: + +* `client_id` - + (Required) + The client ID for the Oauth config. + +* `client_secret` - + (Required) + The client secret for the Oauth config. + +The `user_metadata` block supports: + +* `additional_viewer_user_count` - + (Optional) + Number of additional Viewer Users to allocate to the Looker Instance. + +* `additional_standard_user_count` - + (Optional) + Number of additional Standard Users to allocate to the Looker Instance. + +* `additional_developer_user_count` - + (Optional) + Number of additional Developer Users to allocate to the Looker Instance. + +## 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/{{region}}/instances/{{name}}` + +* `create_time` - + The time the instance was created in RFC3339 UTC "Zulu" format, + accurate to nanoseconds. + +* `egress_public_ip` - + Public Egress IP (IPv4). + +* `ingress_private_ip` - + Private Ingress IP (IPv4). + +* `ingress_public_ip` - + Public Ingress IP (IPv4). + +* `looker_version` - + The Looker version that the instance is using. + +* `looker_uri` - + Looker instance URI which can be used to access the Looker Instance UI. + +* `update_time` - + The time the instance was updated in RFC3339 UTC "Zulu" format, + accurate to nanoseconds. + + +## Timeouts + +This resource provides the following +[Timeouts](https://developer.hashicorp.com/terraform/plugin/sdkv2/resources/retries-and-customizable-timeouts) configuration options: + +- `create` - Default is 90 minutes. +- `update` - Default is 90 minutes. +- `delete` - Default is 90 minutes. + +## Import + + +Instance can be imported using any of these accepted formats: + +``` +$ terraform import google_looker_instance.default projects/{{project}}/locations/{{region}}/instances/{{name}} +$ terraform import google_looker_instance.default {{project}}/{{region}}/{{name}} +$ terraform import google_looker_instance.default {{region}}/{{name}} +$ terraform import google_looker_instance.default {{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).