From 61c9686e44c6a31bc7921752deddd67b0254f233 Mon Sep 17 00:00:00 2001 From: Chris Stephens Date: Mon, 6 Jan 2020 23:08:45 +0000 Subject: [PATCH 01/10] Allow domain mapping to succeed if DNS is pending Signed-off-by: Modular Magician --- google-beta/cloudrun_polling.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/google-beta/cloudrun_polling.go b/google-beta/cloudrun_polling.go index 4750183d41..04fb129df5 100644 --- a/google-beta/cloudrun_polling.go +++ b/google-beta/cloudrun_polling.go @@ -59,6 +59,11 @@ func (s KnativeStatus) LatestMessage() string { func (s KnativeStatus) State(res interface{}) string { for _, condition := range s.Status.Conditions { if condition.Type == "Ready" { + // DomainMapping can enter a 'terminal' state of waiting for external verification + // of DNS records. + if condition.Reason == "CertificatePending" { + return "Ready:CertificatePending" + } return fmt.Sprintf("%s:%s", condition.Type, condition.Status) } } @@ -76,7 +81,7 @@ func (p *CloudRunPolling) PendingStates() []string { return []string{"Ready:Unknown", "Empty"} } func (p *CloudRunPolling) TargetStates() []string { - return []string{"Ready:True"} + return []string{"Ready:True", "Ready:CertificatePending"} } func (p *CloudRunPolling) ErrorStates() []string { return []string{"Ready:False"} From a1755dfa3929b3050b5b4b26613cb571555a1f4e Mon Sep 17 00:00:00 2001 From: The Magician Date: Mon, 6 Jan 2020 15:54:30 -0800 Subject: [PATCH 02/10] Update google_folder import description (#1592) Signed-off-by: Modular Magician Co-authored-by: Dana Hoffman --- website/docs/r/google_folder.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/google_folder.html.markdown b/website/docs/r/google_folder.html.markdown index 6f90117df5..bde78e4d04 100644 --- a/website/docs/r/google_folder.html.markdown +++ b/website/docs/r/google_folder.html.markdown @@ -61,7 +61,7 @@ exported: ## Import -Folders can be imported using the folder autogenerated `name`, e.g. +Folders can be imported using the folder's id, e.g. ``` # Both syntaxes are valid From 7b7994e1497f4d4080c18282bdefb25dbcb5b5d9 Mon Sep 17 00:00:00 2001 From: The Magician Date: Mon, 6 Jan 2020 16:05:47 -0800 Subject: [PATCH 03/10] add google_kms_secret_ciphertext resource, deprecate datasource (#1586) Signed-off-by: Modular Magician Co-authored-by: Dana Hoffman --- ...ata_source_google_kms_secret_ciphertext.go | 3 +- ...ource_google_kms_secret_ciphertext_test.go | 82 +-------- google-beta/provider.go | 5 +- google-beta/resource_kms_secret_ciphertext.go | 165 ++++++++++++++++++ .../resource_kms_secret_ciphertext_test.go | 82 +++++++++ ...google_kms_secret_ciphertext.html.markdown | 2 + ...ackend_bucket_signed_url_key.html.markdown | 13 -- ...ckend_service_signed_url_key.html.markdown | 13 -- .../r/kms_secret_ciphertext.html.markdown | 126 +++++++++++++ website/google.erb | 3 + 10 files changed, 388 insertions(+), 106 deletions(-) create mode 100644 google-beta/resource_kms_secret_ciphertext.go create mode 100644 google-beta/resource_kms_secret_ciphertext_test.go create mode 100644 website/docs/r/kms_secret_ciphertext.html.markdown diff --git a/google-beta/data_source_google_kms_secret_ciphertext.go b/google-beta/data_source_google_kms_secret_ciphertext.go index 1a78e450bf..2be6daca16 100644 --- a/google-beta/data_source_google_kms_secret_ciphertext.go +++ b/google-beta/data_source_google_kms_secret_ciphertext.go @@ -13,7 +13,8 @@ import ( func dataSourceGoogleKmsSecretCiphertext() *schema.Resource { return &schema.Resource{ - Read: dataSourceGoogleKmsSecretCiphertextRead, + DeprecationMessage: "Use the google_kms_secret_ciphertext resource instead.", + Read: dataSourceGoogleKmsSecretCiphertextRead, Schema: map[string]*schema.Schema{ "crypto_key": { Type: schema.TypeString, diff --git a/google-beta/data_source_google_kms_secret_ciphertext_test.go b/google-beta/data_source_google_kms_secret_ciphertext_test.go index 16675d0906..f13409161f 100644 --- a/google-beta/data_source_google_kms_secret_ciphertext_test.go +++ b/google-beta/data_source_google_kms_secret_ciphertext_test.go @@ -1,113 +1,41 @@ package google import ( - "encoding/base64" "fmt" - "log" "testing" "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/terraform" - "google.golang.org/api/cloudkms/v1" ) -func TestAccKmsSecretCiphertext_basic(t *testing.T) { +func TestAccDataKmsSecretCiphertext_basic(t *testing.T) { t.Parallel() - projectOrg := getTestOrgFromEnv(t) - projectBillingAccount := getTestBillingAccountFromEnv(t) - - projectId := "terraform-" + acctest.RandString(10) - keyRingName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - cryptoKeyName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + kms := BootstrapKMSKey(t) plaintext := fmt.Sprintf("secret-%s", acctest.RandString(10)) - // The first test creates resources needed to encrypt plaintext and produce ciphertext resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, Steps: []resource.TestStep{ { - Config: testGoogleKmsCryptoKey_basic(projectId, projectOrg, projectBillingAccount, keyRingName, cryptoKeyName), + Config: testGoogleKmsSecretCiphertext_datasource(kms.CryptoKey.Name, plaintext), Check: func(s *terraform.State) error { - cryptoKeyId, err := getCryptoKeyId(s, "google_kms_crypto_key.crypto_key") + plaintext, err := testAccDecryptSecretDataWithCryptoKey(s, kms.CryptoKey.Name, "data.google_kms_secret_ciphertext.acceptance") if err != nil { return err } - // The second test asserts that the data source created a ciphertext that can be decrypted to the correct plaintext - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: testGoogleKmsSecretCiphertext_datasource(cryptoKeyId.terraformId(), plaintext), - Check: func(s *terraform.State) error { - plaintext, err := testAccDecryptSecretDataWithCryptoKey(s, cryptoKeyId, "data.google_kms_secret_ciphertext.acceptance") - - if err != nil { - return err - } - - return resource.TestCheckResourceAttr("data.google_kms_secret_ciphertext.acceptance", "plaintext", plaintext)(s) - }, - }, - }, - }) - - return nil + return resource.TestCheckResourceAttr("data.google_kms_secret_ciphertext.acceptance", "plaintext", plaintext)(s) }, }, }, }) } -func getCryptoKeyId(s *terraform.State, cryptoKeyResourceName string) (*kmsCryptoKeyId, error) { - config := testAccProvider.Meta().(*Config) - rs, ok := s.RootModule().Resources[cryptoKeyResourceName] - if !ok { - return nil, fmt.Errorf("Resource not found: %s", cryptoKeyResourceName) - } - - return parseKmsCryptoKeyId(rs.Primary.Attributes["id"], config) -} - -func testAccDecryptSecretDataWithCryptoKey(s *terraform.State, cryptoKeyId *kmsCryptoKeyId, secretCiphertextResourceName string) (string, error) { - config := testAccProvider.Meta().(*Config) - rs, ok := s.RootModule().Resources[secretCiphertextResourceName] - if !ok { - return "", fmt.Errorf("Resource not found: %s", secretCiphertextResourceName) - } - ciphertext, ok := rs.Primary.Attributes["ciphertext"] - if !ok { - return "", fmt.Errorf("Attribute 'ciphertext' not found in resource '%s'", secretCiphertextResourceName) - } - - kmsDecryptRequest := &cloudkms.DecryptRequest{ - Ciphertext: ciphertext, - } - - decryptResponse, err := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.Decrypt(cryptoKeyId.cryptoKeyId(), kmsDecryptRequest).Do() - - if err != nil { - return "", fmt.Errorf("Error decrypting ciphertext: %s", err) - } - - plaintextBytes, err := base64.StdEncoding.DecodeString(decryptResponse.Plaintext) - - if err != nil { - return "", err - } - - plaintext := string(plaintextBytes) - log.Printf("[INFO] Successfully decrypted ciphertext and got plaintext: %s", plaintext) - - return plaintext, nil -} - func testGoogleKmsSecretCiphertext_datasource(cryptoKeyTerraformId, plaintext string) string { return fmt.Sprintf(` data "google_kms_secret_ciphertext" "acceptance" { diff --git a/google-beta/provider.go b/google-beta/provider.go index 1416449a0f..c52831d9e3 100644 --- a/google-beta/provider.go +++ b/google-beta/provider.go @@ -521,9 +521,9 @@ func Provider() terraform.ResourceProvider { return provider } -// Generated resources: 111 +// Generated resources: 112 // Generated IAM resources: 45 -// Total generated resources: 156 +// Total generated resources: 157 func ResourceMap() map[string]*schema.Resource { resourceMap, _ := ResourceMapWithErrors() return resourceMap @@ -655,6 +655,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) { "google_identity_platform_tenant": resourceIdentityPlatformTenant(), "google_kms_key_ring": resourceKMSKeyRing(), "google_kms_crypto_key": resourceKMSCryptoKey(), + "google_kms_secret_ciphertext": resourceKMSSecretCiphertext(), "google_logging_metric": resourceLoggingMetric(), "google_ml_engine_model": resourceMLEngineModel(), "google_monitoring_alert_policy": resourceMonitoringAlertPolicy(), diff --git a/google-beta/resource_kms_secret_ciphertext.go b/google-beta/resource_kms_secret_ciphertext.go new file mode 100644 index 0000000000..712c216fb7 --- /dev/null +++ b/google-beta/resource_kms_secret_ciphertext.go @@ -0,0 +1,165 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Magic Modules and manual +// changes will be clobbered when the file is regenerated. +// +// Please read more about how to change this file in +// .github/CONTRIBUTING.md. +// +// ---------------------------------------------------------------------------- + +package google + +import ( + "encoding/base64" + "fmt" + "log" + "reflect" + "regexp" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func resourceKMSSecretCiphertext() *schema.Resource { + return &schema.Resource{ + Create: resourceKMSSecretCiphertextCreate, + Read: resourceKMSSecretCiphertextRead, + Delete: resourceKMSSecretCiphertextDelete, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(4 * time.Minute), + Delete: schema.DefaultTimeout(4 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "crypto_key": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The full name of the CryptoKey that will be used to encrypt the provided plaintext. +Format: ''projects/{{project}}/locations/{{location}}/keyRings/{{keyRing}}/cryptoKeys/{{cryptoKey}}''`, + }, + "plaintext": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `The plaintext to be encrypted.`, + Sensitive: true, + }, + "ciphertext": { + Type: schema.TypeString, + Computed: true, + Description: `Contains the result of encrypting the provided plaintext, encoded in base64.`, + }, + }, + } +} + +func resourceKMSSecretCiphertextCreate(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + obj := make(map[string]interface{}) + plaintextProp, err := expandKMSSecretCiphertextPlaintext(d.Get("plaintext"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("plaintext"); !isEmptyValue(reflect.ValueOf(plaintextProp)) && (ok || !reflect.DeepEqual(v, plaintextProp)) { + obj["plaintext"] = plaintextProp + } + + url, err := replaceVars(d, config, "{{KMSBasePath}}{{crypto_key}}:encrypt") + if err != nil { + return err + } + + log.Printf("[DEBUG] Creating new SecretCiphertext: %#v", obj) + var project string + if parts := regexp.MustCompile(`projects\/([^\/]+)\/`).FindStringSubmatch(url); parts != nil { + project = parts[1] + } + res, err := sendRequestWithTimeout(config, "POST", project, url, obj, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return fmt.Errorf("Error creating SecretCiphertext: %s", err) + } + + // Store the ID now + id, err := replaceVars(d, config, "{{crypto_key}}/{{ciphertext}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + log.Printf("[DEBUG] Finished creating SecretCiphertext %q: %#v", d.Id(), res) + + // we don't set anything on read and instead do it all in create + ciphertext, ok := res["ciphertext"] + if !ok { + return fmt.Errorf("Create response didn't contain critical fields. Create may not have succeeded.") + } + d.Set("ciphertext", ciphertext.(string)) + + id, err = replaceVars(d, config, "{{crypto_key}}/{{ciphertext}}") + if err != nil { + return fmt.Errorf("Error constructing id: %s", err) + } + d.SetId(id) + + return resourceKMSSecretCiphertextRead(d, meta) +} + +func resourceKMSSecretCiphertextRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + url, err := replaceVars(d, config, "{{KMSBasePath}}{{crypto_key}}") + if err != nil { + return err + } + + var project string + if parts := regexp.MustCompile(`projects\/([^\/]+)\/`).FindStringSubmatch(url); parts != nil { + project = parts[1] + } + res, err := sendRequest(config, "GET", project, url, nil) + if err != nil { + return handleNotFoundError(err, d, fmt.Sprintf("KMSSecretCiphertext %q", d.Id())) + } + + res, err = resourceKMSSecretCiphertextDecoder(d, meta, res) + if err != nil { + return err + } + + if res == nil { + // Decoding the object has resulted in it being gone. It may be marked deleted + log.Printf("[DEBUG] Removing KMSSecretCiphertext because it no longer exists.") + d.SetId("") + return nil + } + + return nil +} + +func resourceKMSSecretCiphertextDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[WARNING] KMS SecretCiphertext resources"+ + " cannot be deleted from GCP. The resource %s will be removed from Terraform"+ + " state, but will still be present on the server.", d.Id()) + d.SetId("") + + return nil +} + +func expandKMSSecretCiphertextPlaintext(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + if v == nil { + return nil, nil + } + + return base64.StdEncoding.EncodeToString([]byte(v.(string))), nil +} + +func resourceKMSSecretCiphertextDecoder(d *schema.ResourceData, meta interface{}, res map[string]interface{}) (map[string]interface{}, error) { + return res, nil +} diff --git a/google-beta/resource_kms_secret_ciphertext_test.go b/google-beta/resource_kms_secret_ciphertext_test.go new file mode 100644 index 0000000000..03400ca461 --- /dev/null +++ b/google-beta/resource_kms_secret_ciphertext_test.go @@ -0,0 +1,82 @@ +package google + +import ( + "encoding/base64" + "fmt" + "log" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "google.golang.org/api/cloudkms/v1" +) + +func TestAccKmsSecretCiphertext_basic(t *testing.T) { + t.Parallel() + + kms := BootstrapKMSKey(t) + + plaintext := fmt.Sprintf("secret-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testGoogleKmsSecretCiphertext(kms.CryptoKey.Name, plaintext), + Check: func(s *terraform.State) error { + plaintext, err := testAccDecryptSecretDataWithCryptoKey(s, kms.CryptoKey.Name, "google_kms_secret_ciphertext.acceptance") + + if err != nil { + return err + } + + return resource.TestCheckResourceAttr("google_kms_secret_ciphertext.acceptance", "plaintext", plaintext)(s) + }, + }, + }, + }) +} + +func testAccDecryptSecretDataWithCryptoKey(s *terraform.State, cryptoKeyId string, secretCiphertextResourceName string) (string, error) { + config := testAccProvider.Meta().(*Config) + rs, ok := s.RootModule().Resources[secretCiphertextResourceName] + if !ok { + return "", fmt.Errorf("Resource not found: %s", secretCiphertextResourceName) + } + ciphertext, ok := rs.Primary.Attributes["ciphertext"] + if !ok { + return "", fmt.Errorf("Attribute 'ciphertext' not found in resource '%s'", secretCiphertextResourceName) + } + + kmsDecryptRequest := &cloudkms.DecryptRequest{ + Ciphertext: ciphertext, + } + + decryptResponse, err := config.clientKms.Projects.Locations.KeyRings.CryptoKeys.Decrypt(cryptoKeyId, kmsDecryptRequest).Do() + + if err != nil { + return "", fmt.Errorf("Error decrypting ciphertext: %s", err) + } + + plaintextBytes, err := base64.StdEncoding.DecodeString(decryptResponse.Plaintext) + + if err != nil { + return "", err + } + + plaintext := string(plaintextBytes) + log.Printf("[INFO] Successfully decrypted ciphertext and got plaintext: %s", plaintext) + + return plaintext, nil +} + +func testGoogleKmsSecretCiphertext(cryptoKeyTerraformId, plaintext string) string { + return fmt.Sprintf(` +resource "google_kms_secret_ciphertext" "acceptance" { + crypto_key = "%s" + plaintext = "%s" +} +`, cryptoKeyTerraformId, plaintext) +} diff --git a/website/docs/d/google_kms_secret_ciphertext.html.markdown b/website/docs/d/google_kms_secret_ciphertext.html.markdown index cbfc407d53..9e06d5c105 100644 --- a/website/docs/d/google_kms_secret_ciphertext.html.markdown +++ b/website/docs/d/google_kms_secret_ciphertext.html.markdown @@ -9,6 +9,8 @@ description: |- # google\_kms\_secret\_ciphertext +!> **Warning:** This data source is deprecated. Use the [`google_kms_secret_ciphertext`](../../r/kms_secret_ciphertext.html) **resource** instead. + This data source allows you to encrypt data with Google Cloud KMS and use the ciphertext within your resource definitions. diff --git a/website/docs/r/compute_backend_bucket_signed_url_key.html.markdown b/website/docs/r/compute_backend_bucket_signed_url_key.html.markdown index 54a5ae20dd..0ce1c4957e 100644 --- a/website/docs/r/compute_backend_bucket_signed_url_key.html.markdown +++ b/website/docs/r/compute_backend_bucket_signed_url_key.html.markdown @@ -94,19 +94,6 @@ This resource provides the following - `create` - Default is 4 minutes. - `delete` - Default is 4 minutes. -## Import - -BackendBucketSignedUrlKey can be imported using any of these accepted formats: - -``` -$ terraform import google_compute_backend_bucket_signed_url_key.default projects/{{project}}/global/backendBuckets/{{backend_bucket}}/{{name}} -$ terraform import google_compute_backend_bucket_signed_url_key.default {{project}}/{{backend_bucket}}/{{name}} -$ terraform import google_compute_backend_bucket_signed_url_key.default {{backend_bucket}}/{{name}} -``` - --> If you're importing a resource with beta features, make sure to include `-provider=google-beta` -as an argument so that Terraform uses the correct provider to import your resource. - ## User Project Overrides This resource supports [User Project Overrides](https://www.terraform.io/docs/providers/google/guides/provider_reference.html#user_project_override). diff --git a/website/docs/r/compute_backend_service_signed_url_key.html.markdown b/website/docs/r/compute_backend_service_signed_url_key.html.markdown index d4946a1c70..afd2809c04 100644 --- a/website/docs/r/compute_backend_service_signed_url_key.html.markdown +++ b/website/docs/r/compute_backend_service_signed_url_key.html.markdown @@ -132,19 +132,6 @@ This resource provides the following - `create` - Default is 4 minutes. - `delete` - Default is 4 minutes. -## Import - -BackendServiceSignedUrlKey can be imported using any of these accepted formats: - -``` -$ terraform import google_compute_backend_service_signed_url_key.default projects/{{project}}/global/backendServices/{{backend_service}}/{{name}} -$ terraform import google_compute_backend_service_signed_url_key.default {{project}}/{{backend_service}}/{{name}} -$ terraform import google_compute_backend_service_signed_url_key.default {{backend_service}}/{{name}} -``` - --> If you're importing a resource with beta features, make sure to include `-provider=google-beta` -as an argument so that Terraform uses the correct provider to import your resource. - ## User Project Overrides This resource supports [User Project Overrides](https://www.terraform.io/docs/providers/google/guides/provider_reference.html#user_project_override). diff --git a/website/docs/r/kms_secret_ciphertext.html.markdown b/website/docs/r/kms_secret_ciphertext.html.markdown new file mode 100644 index 0000000000..4bb0c4365d --- /dev/null +++ b/website/docs/r/kms_secret_ciphertext.html.markdown @@ -0,0 +1,126 @@ +--- +# ---------------------------------------------------------------------------- +# +# *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +# +# ---------------------------------------------------------------------------- +# +# This file is automatically generated by Magic Modules and manual +# changes will be clobbered when the file is regenerated. +# +# Please read more about how to change this file in +# .github/CONTRIBUTING.md. +# +# ---------------------------------------------------------------------------- +subcategory: "Cloud KMS" +layout: "google" +page_title: "Google: google_kms_secret_ciphertext" +sidebar_current: "docs-google-kms-secret-ciphertext" +description: |- + Encrypts secret data with Google Cloud KMS and provides access to the ciphertext. +--- + +# google\_kms\_secret\_ciphertext + +Encrypts secret data with Google Cloud KMS and provides access to the ciphertext. + + +~> **NOTE**: Using this resource will allow you to conceal secret data within your +resource definitions, but it does not take care of protecting that data in the +logging output, plan output, or state output. Please take care to secure your secret +data outside of resource definitions. + + +To get more information about SecretCiphertext, see: + +* [API documentation](https://cloud.google.com/kms/docs/reference/rest/v1/projects.locations.keyRings.cryptoKeys/encrypt) +* How-to Guides + * [Encrypting and decrypting data with a symmetric key](https://cloud.google.com/kms/docs/encrypt-decrypt) + +## Example Usage - Kms Secret Ciphertext Basic + + +```hcl +resource "google_kms_key_ring" "keyring" { + name = "keyring-example" + location = "global" +} + +resource "google_kms_crypto_key" "cryptokey" { + name = "crypto-key-example" + key_ring = google_kms_key_ring.keyring.id + rotation_period = "100000s" + + lifecycle { + prevent_destroy = true + } +} + +resource "google_kms_secret_ciphertext" "my_password" { + crypto_key = google_kms_crypto_key.cryptokey.id + plaintext = "my-secret-password" +} + +resource "google_compute_instance" "instance" { + name = "my-instance" + machine_type = "n1-standard-1" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "debian-cloud/debian-9" + } + } + + network_interface { + network = "default" + + access_config { + } + } + + metadata = { + password = google_kms_secret_ciphertext.my_password.ciphertext + } +} +``` + +## Argument Reference + +The following arguments are supported: + + +* `plaintext` - + (Required) + The plaintext to be encrypted. + +* `crypto_key` - + (Required) + The full name of the CryptoKey that will be used to encrypt the provided plaintext. + Format: `'projects/{{project}}/locations/{{location}}/keyRings/{{keyRing}}/cryptoKeys/{{cryptoKey}}'` + + +- - - + + + +## Attributes Reference + +In addition to the arguments listed above, the following computed attributes are exported: + + +* `ciphertext` - + Contains the result of encrypting the provided plaintext, encoded in base64. + + +## Timeouts + +This resource provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - Default is 4 minutes. +- `delete` - Default is 4 minutes. + +## User Project Overrides + +This resource supports [User Project Overrides](https://www.terraform.io/docs/providers/google/guides/provider_reference.html#user_project_override). diff --git a/website/google.erb b/website/google.erb index 6a9fdaefc1..38ff6f5447 100644 --- a/website/google.erb +++ b/website/google.erb @@ -1003,6 +1003,9 @@ > google_kms_key_ring_iam_policy + > + google_kms_secret_ciphertext + From 4ea508bbb244b20d97c3d8d51da1606feca044ef Mon Sep 17 00:00:00 2001 From: The Magician Date: Tue, 7 Jan 2020 09:24:35 -0800 Subject: [PATCH 04/10] Allow add/removing Bigtable clusters (#1589) Signed-off-by: Modular Magician Co-authored-by: Riley Karson --- google-beta/resource_bigtable_instance.go | 146 +++++++++++++----- .../resource_bigtable_instance_test.go | 46 +++++- .../docs/r/bigtable_instance.html.markdown | 16 +- 3 files changed, 164 insertions(+), 44 deletions(-) diff --git a/google-beta/resource_bigtable_instance.go b/google-beta/resource_bigtable_instance.go index 5b22f30c83..3340a7099b 100644 --- a/google-beta/resource_bigtable_instance.go +++ b/google-beta/resource_bigtable_instance.go @@ -44,12 +44,10 @@ func resourceBigtableInstance() *schema.Resource { "cluster_id": { Type: schema.TypeString, Required: true, - ForceNew: true, }, "zone": { Type: schema.TypeString, Required: true, - ForceNew: true, }, "num_nodes": { Type: schema.TypeInt, @@ -60,7 +58,6 @@ func resourceBigtableInstance() *schema.Resource { Type: schema.TypeString, Optional: true, Default: "SSD", - ForceNew: true, ValidateFunc: validation.StringInSlice([]string{"SSD", "HDD"}, false), }, }, @@ -212,27 +209,28 @@ func resourceBigtableInstanceUpdate(d *schema.ResourceData, meta interface{}) er } defer c.Close() - clusters, err := c.Clusters(ctx, d.Get("name").(string)) - if err != nil { - return fmt.Errorf("Error retrieving clusters for instance %s", err.Error()) + conf := &bigtable.InstanceWithClustersConfig{ + InstanceID: d.Get("name").(string), } - clusterMap := make(map[string]*bigtable.ClusterInfo, len(clusters)) - for _, cluster := range clusters { - clusterMap[cluster.Name] = cluster - } - - for _, cluster := range d.Get("cluster").([]interface{}) { - config := cluster.(map[string]interface{}) - cluster_id := config["cluster_id"].(string) - if cluster, ok := clusterMap[cluster_id]; ok { - if cluster.ServeNodes != config["num_nodes"].(int) { - err = c.UpdateCluster(ctx, d.Get("name").(string), cluster.Name, int32(config["num_nodes"].(int))) - if err != nil { - return fmt.Errorf("Error updating cluster %s for instance %s", cluster.Name, d.Get("name").(string)) - } - } - } + displayName, ok := d.GetOk("display_name") + if !ok { + displayName = conf.InstanceID + } + conf.DisplayName = displayName.(string) + + switch d.Get("instance_type").(string) { + case "DEVELOPMENT": + conf.InstanceType = bigtable.DEVELOPMENT + case "PRODUCTION": + conf.InstanceType = bigtable.PRODUCTION + } + + conf.Clusters = expandBigtableClusters(d.Get("cluster").([]interface{}), conf.InstanceID) + + _, err = bigtable.UpdateInstanceAndSyncClusters(ctx, c, conf) + if err != nil { + return fmt.Errorf("Error updating instance. %s", err) } return resourceBigtableInstanceRead(d, meta) @@ -305,6 +303,7 @@ func expandBigtableClusters(clusters []interface{}, instanceID string) []bigtabl return results } +// resourceBigtableInstanceValidateDevelopment validates restrictions specific to DEVELOPMENT clusters func resourceBigtableInstanceValidateDevelopment(diff *schema.ResourceDiff, meta interface{}) error { if diff.Get("instance_type").(string) != "DEVELOPMENT" { return nil @@ -318,46 +317,115 @@ func resourceBigtableInstanceValidateDevelopment(diff *schema.ResourceDiff, meta return nil } +// resourceBigtableInstanceClusterReorderTypeList causes the cluster block to +// act like a TypeSet while it's a TypeList underneath. It preserves state +// ordering on updates, and causes the resource to get recreated if it would +// attempt to perform an impossible change. +// This doesn't use the standard unordered list utility (https://github.com/GoogleCloudPlatform/magic-modules/blob/master/templates/terraform/unordered_list_customize_diff.erb) +// because some fields can't be modified using the API and we recreate the instance +// when they're changed. func resourceBigtableInstanceClusterReorderTypeList(diff *schema.ResourceDiff, meta interface{}) error { - old_count, new_count := diff.GetChange("cluster.#") + oldCount, newCount := diff.GetChange("cluster.#") // simulate Required:true, MinItems:1, MaxItems:4 for "cluster" - if new_count.(int) < 1 { + if newCount.(int) < 1 { return fmt.Errorf("config is invalid: Too few cluster blocks: Should have at least 1 \"cluster\" block") } - if new_count.(int) > 4 { + if newCount.(int) > 4 { return fmt.Errorf("config is invalid: Too many cluster blocks: No more than 4 \"cluster\" blocks are allowed") } - if old_count.(int) != new_count.(int) { + // exit early if we're in create (name's old value is nil) + n, _ := diff.GetChange("name") + if n == nil || n == "" { return nil } - var old_ids []string - clusters := make(map[string]interface{}, new_count.(int)) + oldIds := []string{} + clusters := make(map[string]interface{}, newCount.(int)) - for i := 0; i < new_count.(int); i++ { - old_id, new_id := diff.GetChange(fmt.Sprintf("cluster.%d.cluster_id", i)) - if old_id != nil && old_id != "" { - old_ids = append(old_ids, old_id.(string)) + for i := 0; i < oldCount.(int); i++ { + oldId, _ := diff.GetChange(fmt.Sprintf("cluster.%d.cluster_id", i)) + if oldId != nil && oldId != "" { + oldIds = append(oldIds, oldId.(string)) } + } + log.Printf("[DEBUG] Saw old ids: %#v", oldIds) + + for i := 0; i < newCount.(int); i++ { + _, newId := diff.GetChange(fmt.Sprintf("cluster.%d.cluster_id", i)) _, c := diff.GetChange(fmt.Sprintf("cluster.%d", i)) - clusters[new_id.(string)] = c + clusters[newId.(string)] = c + } + + // create a list of clusters using the old order when possible to minimise + // diffs + // initially, add matching clusters to their index by id (nil otherwise) + // then, fill in nils with new clusters. + // [a, b, c, e] -> [c, a, d] becomes [a, nil, c] followed by [a, d, c] + var orderedClusters []interface{} + for i := 0; i < newCount.(int); i++ { + // when i is out of range of old, all values are nil + if i >= len(oldIds) { + orderedClusters = append(orderedClusters, nil) + continue + } + + oldId := oldIds[i] + if c, ok := clusters[oldId]; ok { + log.Printf("[DEBUG] Matched: %#v", oldId) + orderedClusters = append(orderedClusters, c) + delete(clusters, oldId) + } else { + orderedClusters = append(orderedClusters, nil) + } } - // reorder clusters according to the old cluster order - var old_cluster_order []interface{} - for _, id := range old_ids { - if c, ok := clusters[id]; ok { - old_cluster_order = append(old_cluster_order, c) + log.Printf("[DEBUG] Remaining clusters: %#v", clusters) + for _, elem := range clusters { + for i, e := range orderedClusters { + if e == nil { + orderedClusters[i] = elem + } } } - err := diff.SetNew("cluster", old_cluster_order) + err := diff.SetNew("cluster", orderedClusters) if err != nil { return fmt.Errorf("Error setting cluster diff: %s", err) } + // Clusters can't have their zone / storage_type updated, ForceNew if it's + // changed. This will show a diff with the old state on the left side and + // the unmodified new state on the right and the ForceNew attributed to the + // _old state index_ even if the diff appears to have moved. + // This depends on the clusters having been reordered already by the prior + // SetNew call. + // We've implemented it here because it doesn't return an error in the + // client and silently fails. + for i := 0; i < newCount.(int); i++ { + oldId, newId := diff.GetChange(fmt.Sprintf("cluster.%d.cluster_id", i)) + if oldId != newId { + continue + } + + oZone, nZone := diff.GetChange(fmt.Sprintf("cluster.%d.zone", i)) + if oZone != nZone { + err := diff.ForceNew(fmt.Sprintf("cluster.%d.zone", i)) + if err != nil { + return fmt.Errorf("Error setting cluster diff: %s", err) + } + } + + oST, nST := diff.GetChange(fmt.Sprintf("cluster.%d.storage_type", i)) + if oST != nST { + err := diff.ForceNew(fmt.Sprintf("cluster.%d.storage_type", i)) + if err != nil { + return fmt.Errorf("Error setting cluster diff: %s", err) + } + } + } + return nil } diff --git a/google-beta/resource_bigtable_instance_test.go b/google-beta/resource_bigtable_instance_test.go index e03f962c27..efef6cfbfd 100644 --- a/google-beta/resource_bigtable_instance_test.go +++ b/google-beta/resource_bigtable_instance_test.go @@ -68,7 +68,23 @@ func TestAccBigtableInstance_cluster(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccBigtableInstance_cluster_reordered(instanceName, 5), + Config: testAccBigtableInstance_clusterReordered(instanceName, 5), + }, + { + ResourceName: "google_bigtable_instance.instance", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccBigtableInstance_clusterModified(instanceName, 5), + }, + { + ResourceName: "google_bigtable_instance.instance", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccBigtableInstance_clusterReordered(instanceName, 5), }, { ResourceName: "google_bigtable_instance.instance", @@ -225,7 +241,7 @@ resource "google_bigtable_instance" "instance" { `, instanceName, instanceName, instanceName, instanceName, instanceName, instanceName) } -func testAccBigtableInstance_cluster_reordered(instanceName string, numNodes int) string { +func testAccBigtableInstance_clusterReordered(instanceName string, numNodes int) string { return fmt.Sprintf(` resource "google_bigtable_instance" "instance" { name = "%s" @@ -257,6 +273,32 @@ resource "google_bigtable_instance" "instance" { `, instanceName, instanceName, numNodes, instanceName, numNodes, instanceName, numNodes, instanceName, numNodes) } +func testAccBigtableInstance_clusterModified(instanceName string, numNodes int) string { + return fmt.Sprintf(` +resource "google_bigtable_instance" "instance" { + name = "%s" + cluster { + cluster_id = "%s-c" + zone = "us-central1-c" + num_nodes = %d + storage_type = "HDD" + } + cluster { + cluster_id = "%s-a" + zone = "us-central1-a" + num_nodes = %d + storage_type = "HDD" + } + cluster { + cluster_id = "%s-b" + zone = "us-central1-b" + num_nodes = %d + storage_type = "HDD" + } +} +`, instanceName, instanceName, numNodes, instanceName, numNodes, instanceName, numNodes) +} + func testAccBigtableInstance_development(instanceName string) string { return fmt.Sprintf(` resource "google_bigtable_instance" "instance" { diff --git a/website/docs/r/bigtable_instance.html.markdown b/website/docs/r/bigtable_instance.html.markdown index 083db724b3..47205456ba 100644 --- a/website/docs/r/bigtable_instance.html.markdown +++ b/website/docs/r/bigtable_instance.html.markdown @@ -68,11 +68,21 @@ The `cluster` block supports the following arguments: * `cluster_id` - (Required) The ID of the Cloud Bigtable cluster. -* `zone` - (Required) The zone to create the Cloud Bigtable cluster in. Each cluster must have a different zone in the same region. Zones that support Bigtable instances are noted on the [Cloud Bigtable locations page](https://cloud.google.com/bigtable/docs/locations). +* `zone` - (Required) The zone to create the Cloud Bigtable cluster in. Each +cluster must have a different zone in the same region. Zones that support +Bigtable instances are noted on the [Cloud Bigtable locations page](https://cloud.google.com/bigtable/docs/locations). -* `num_nodes` - (Optional) The number of nodes in your Cloud Bigtable cluster. Required, with a minimum of `3` for a `PRODUCTION` instance. Must be left unset for a `DEVELOPMENT` instance. +* `num_nodes` - (Optional) The number of nodes in your Cloud Bigtable cluster. +Required, with a minimum of `3` for a `PRODUCTION` instance. Must be left unset +for a `DEVELOPMENT` instance. -* `storage_type` - (Optional) The storage type to use. One of `"SSD"` or `"HDD"`. Defaults to `"SSD"`. +* `storage_type` - (Optional) The storage type to use. One of `"SSD"` or +`"HDD"`. Defaults to `"SSD"`. + +!> **Warning:** Modifying the `storage_type` or `zone` of an existing cluster (by +`cluster_id`) will cause Terraform to delete/recreate the entire +`google_bigtable_instance` resource. If these values are changing, use a new +`cluster_id`. ## Attributes Reference From 812048aa8a13da9f199efc53ca76d7ac39e0f1b7 Mon Sep 17 00:00:00 2001 From: The Magician Date: Tue, 7 Jan 2020 10:19:17 -0800 Subject: [PATCH 05/10] Add bootstrapped test networks for service networking tests (#1588) Signed-off-by: Modular Magician Co-authored-by: emily --- google-beta/bootstrap_utils_test.go | 61 ++++++++++++ ...urce_service_networking_connection_test.go | 95 +++++-------------- .../resource_sql_database_instance_test.go | 11 +-- 3 files changed, 90 insertions(+), 77 deletions(-) diff --git a/google-beta/bootstrap_utils_test.go b/google-beta/bootstrap_utils_test.go index fcab7f2e1f..f654f55e4d 100644 --- a/google-beta/bootstrap_utils_test.go +++ b/google-beta/bootstrap_utils_test.go @@ -6,6 +6,7 @@ import ( "log" "os" "testing" + "time" "google.golang.org/api/cloudkms/v1" "google.golang.org/api/iam/v1" @@ -230,3 +231,63 @@ func BootstrapServiceAccount(t *testing.T, project, testRunner string) string { return sa.Email } + +const SharedTestNetworkPrefix = "tf-bootstrap-net-" + +// BootstrapSharedServiceNetworkingConsumerNetwork will return a shared compute network +// for service networking test to prevent hitting limits on tenancy projects. +// +// This will either return an existing network or create one if it hasn't been created +// in the project yet. One consumer network/tenant project we don't own is created +// per producer network (i.e. network created by test), with a hard limit set. +func BootstrapSharedServiceNetworkingConsumerNetwork(t *testing.T, testId string) string { + if v := os.Getenv("TF_ACC"); v == "" { + log.Println("Acceptance tests and bootstrapping skipped unless env 'TF_ACC' set") + // If not running acceptance tests, return an empty string + return "" + } + + project := getTestProjectFromEnv() + networkName := SharedTestNetworkPrefix + testId + config := &Config{ + Credentials: getTestCredsFromEnv(), + Project: project, + Region: getTestRegionFromEnv(), + Zone: getTestZoneFromEnv(), + } + ConfigureBasePaths(config) + if err := config.LoadAndValidate(context.Background()); err != nil { + t.Errorf("Unable to bootstrap network: %s", err) + } + + log.Printf("[DEBUG] Getting shared test network %q", networkName) + _, err := config.clientCompute.Networks.Get(project, networkName).Do() + if err != nil && isGoogleApiErrorWithCode(err, 404) { + log.Printf("[DEBUG] Network %q not found, bootstrapping", networkName) + url := fmt.Sprintf("%sprojects/%s/global/networks", config.ComputeBasePath, project) + netObj := map[string]interface{}{ + "name": networkName, + "autoCreateSubnetworks": false, + } + + res, err := sendRequestWithTimeout(config, "POST", project, url, netObj, 4*time.Minute) + if err != nil { + t.Fatalf("Error bootstrapping shared test network %q: %s", networkName, err) + } + + log.Printf("[DEBUG] Waiting for network creation to finish") + err = computeOperationWaitTime(config, res, project, "Error bootstrapping shared test network", 4) + if err != nil { + t.Fatalf("Error bootstrapping shared test network %q: %s", networkName, err) + } + } + + network, err := config.clientCompute.Networks.Get(project, networkName).Do() + if err != nil { + t.Errorf("Error getting shared test network %q: %s", networkName, err) + } + if network == nil { + t.Fatalf("Error getting shared test network %q: is nil", networkName) + } + return network.Name +} diff --git a/google-beta/resource_service_networking_connection_test.go b/google-beta/resource_service_networking_connection_test.go index 65263055da..4932b7f7ef 100644 --- a/google-beta/resource_service_networking_connection_test.go +++ b/google-beta/resource_service_networking_connection_test.go @@ -9,19 +9,20 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/terraform" ) -func TestAccServiceNetworkingConnectionCreate(t *testing.T) { +func TestAccServiceNetworkingConnection_create(t *testing.T) { t.Parallel() + network := BootstrapSharedServiceNetworkingConsumerNetwork(t, "service-networking-connection-create") + addr := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + service := "servicenetworking.googleapis.com" + resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testServiceNetworkingConnectionDestroy(service, network), Steps: []resource.TestStep{ { - Config: testAccServiceNetworkingConnection( - fmt.Sprintf("tf-test-%s", acctest.RandString(10)), - fmt.Sprintf("tf-test-%s", acctest.RandString(10)), - "servicenetworking.googleapis.com", - ), + Config: testAccServiceNetworkingConnection(network, addr, "servicenetworking.googleapis.com"), }, { ResourceName: "google_service_networking_connection.foobar", @@ -32,49 +33,21 @@ func TestAccServiceNetworkingConnectionCreate(t *testing.T) { }) } -// Standard checkDestroy cannot be used here because destroying the network will delete -// all the networking connections so this would return false positives. -func TestAccServiceNetworkingConnectionDestroy(t *testing.T) { +func TestAccServiceNetworkingConnection_update(t *testing.T) { t.Parallel() - network := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - addressRange := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: testAccServiceNetworkingConnection( - network, - addressRange, - "servicenetworking.googleapis.com", - ), - }, - { - Config: testAccServiceNetworkingConnectionDestroy(network, addressRange), - Check: resource.ComposeTestCheckFunc( - testServiceNetworkingConnectionDestroy("servicenetworking.googleapis.com", network, getTestProjectFromEnv()), - ), - }, - }, - }) -} - -func TestAccServiceNetworkingConnectionUpdate(t *testing.T) { - t.Parallel() + network := BootstrapSharedServiceNetworkingConsumerNetwork(t, "service-networking-connection-update") + addr1 := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + addr2 := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + service := "servicenetworking.googleapis.com" - network := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testServiceNetworkingConnectionDestroy(service, network), Steps: []resource.TestStep{ { - Config: testAccServiceNetworkingConnection( - network, - fmt.Sprintf("tf-test-%s", acctest.RandString(10)), - "servicenetworking.googleapis.com", - ), + Config: testAccServiceNetworkingConnection(network, addr1, "servicenetworking.googleapis.com"), }, { ResourceName: "google_service_networking_connection.foobar", @@ -82,11 +55,7 @@ func TestAccServiceNetworkingConnectionUpdate(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccServiceNetworkingConnection( - network, - fmt.Sprintf("tf-test-%s", acctest.RandString(10)), - "servicenetworking.googleapis.com", - ), + Config: testAccServiceNetworkingConnection(network, addr2, "servicenetworking.googleapis.com"), }, { ResourceName: "google_service_networking_connection.foobar", @@ -98,11 +67,11 @@ func TestAccServiceNetworkingConnectionUpdate(t *testing.T) { } -func testServiceNetworkingConnectionDestroy(parent, network, project string) resource.TestCheckFunc { +func testServiceNetworkingConnectionDestroy(parent, network string) resource.TestCheckFunc { return func(s *terraform.State) error { config := testAccProvider.Meta().(*Config) parentService := "services/" + parent - networkName := fmt.Sprintf("projects/%s/global/networks/%s", project, network) + networkName := fmt.Sprintf("projects/%s/global/networks/%s", getTestProjectFromEnv(), network) response, err := config.clientServiceNetworking.Services.Connections.List(parentService). Network(networkName).Do() @@ -122,7 +91,7 @@ func testServiceNetworkingConnectionDestroy(parent, network, project string) res func testAccServiceNetworkingConnection(networkName, addressRangeName, serviceName string) string { return fmt.Sprintf(` -resource "google_compute_network" "foobar" { +data "google_compute_network" "servicenet" { name = "%s" } @@ -131,29 +100,13 @@ resource "google_compute_global_address" "foobar" { purpose = "VPC_PEERING" address_type = "INTERNAL" prefix_length = 16 - network = google_compute_network.foobar.self_link + network = data.google_compute_network.servicenet.self_link } resource "google_service_networking_connection" "foobar" { - network = google_compute_network.foobar.self_link + network = data.google_compute_network.servicenet.self_link service = "%s" reserved_peering_ranges = [google_compute_global_address.foobar.name] } `, networkName, addressRangeName, serviceName) } - -func testAccServiceNetworkingConnectionDestroy(networkName, addressRangeName string) string { - return fmt.Sprintf(` -resource "google_compute_network" "foobar" { - name = "%s" -} - -resource "google_compute_global_address" "foobar" { - name = "%s" - purpose = "VPC_PEERING" - address_type = "INTERNAL" - prefix_length = 16 - network = google_compute_network.foobar.self_link -} -`, networkName, addressRangeName) -} diff --git a/google-beta/resource_sql_database_instance_test.go b/google-beta/resource_sql_database_instance_test.go index 5ac15dea3d..21a0acd20b 100644 --- a/google-beta/resource_sql_database_instance_test.go +++ b/google-beta/resource_sql_database_instance_test.go @@ -623,8 +623,8 @@ func TestAccSqlDatabaseInstance_withPrivateNetwork(t *testing.T) { t.Parallel() databaseName := "tf-test-" + acctest.RandString(10) - networkName := "tf-test-" + acctest.RandString(10) addressName := "tf-test-" + acctest.RandString(10) + networkName := BootstrapSharedServiceNetworkingConsumerNetwork(t, "sql-instance-private") resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -779,9 +779,8 @@ resource "google_sql_database_instance" "instance-failover" { func testAccSqlDatabaseInstance_withPrivateNetwork(databaseName, networkName, addressRangeName string) string { return fmt.Sprintf(` -resource "google_compute_network" "foobar" { +data "google_compute_network" "servicenet" { name = "%s" - auto_create_subnetworks = false } resource "google_compute_global_address" "foobar" { @@ -789,11 +788,11 @@ resource "google_compute_global_address" "foobar" { purpose = "VPC_PEERING" address_type = "INTERNAL" prefix_length = 16 - network = google_compute_network.foobar.self_link + network = data.google_compute_network.servicenet.self_link } resource "google_service_networking_connection" "foobar" { - network = google_compute_network.foobar.self_link + network = data.google_compute_network.servicenet.self_link service = "servicenetworking.googleapis.com" reserved_peering_ranges = [google_compute_global_address.foobar.name] } @@ -806,7 +805,7 @@ resource "google_sql_database_instance" "instance" { tier = "db-f1-micro" ip_configuration { ipv4_enabled = "false" - private_network = google_compute_network.foobar.self_link + private_network = data.google_compute_network.servicenet.self_link } } } From 996128aa36d2e0a416dd4fdade4025e5388b7fce Mon Sep 17 00:00:00 2001 From: Paddy Date: Tue, 7 Jan 2020 13:39:05 -0800 Subject: [PATCH 06/10] Update CHANGELOG.md --- CHANGELOG.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd0403e42a..a1cc35fec2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,56 @@ -## 3.3.1 (Unreleased) +## 3.4.1 (Unreleased) +## 3.4.0 (January 07, 2020) + +DEPRECATIONS: +* kms: deprecated `data.google_kms_secret_ciphertext` as there was no way to make it idempotent. Instead, use the `google_kms_secret_ciphertext` resource. ([#1586](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1586)) + +BREAKING CHANGES: +* `google_iap_web_iam_*`, `google_iap_web_type_compute_iam_*`, `google_iap_web_type_app_engine_*`, and `google_iap_app_engine_service_iam_*` resources now support IAM Conditions (beta provider only). If any conditions had been created out of band before this release, take extra care to ensure they are present in your Terraform config so the provider doesn't try to create new bindings with no conditions. Terraform will show a diff that it is adding the condition to the resource, which is safe to apply. ([#1527](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1527)) +* `google_kms_key_ring_iam_*` and `google_kms_crypto_key_iam_*` resources now support IAM Conditions (beta provider only). If any conditions had been created out of band before this release, take extra care to ensure they are present in your Terraform config so the provider doesn't try to create new bindings with no conditions. Terraform will show a diff that it is adding the condition to the resource, which is safe to apply. ([#1524](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1524)) +* cloudrun: Changed `google_cloud_run_domain_mapping` to correctly match Cloud Run API expected format for `spec.route_name`, {serviceName}, instead of invalid projects/{project}/global/services/{serviceName} ([#1563](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1563)) +* compute: Added back ConflictsWith restrictions for ExactlyOneOf restrictions that were removed in v3.3.0 for `google_compute_firewall`, `google_compute_health_check`, and `google_compute_region_health_check`. This effectively changes an API-side failure that was only accessible in v3.3.0 to a plan-time one. ([#1534](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1534)) +* logging: Changed `google_logging_metric.metric_descriptors.labels` from a list to a set ([#1559](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1559)) +* resourcemanager: Added back ConflictsWith restrictions for ExactlyOneOf restrictions that were removed in v3.3.0 for `google_organization_policy`, `google_folder_organization_policy`, and `google_project_organization_policy`. This effectively changes an API-side failure that was only accessible in v3.3.0 to a plan-time one. ([#1534](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1534)) + +FEATURES: +* **New Data Source:** `google_sql_ca_certs` ([#1580](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1580)) +* **New Resource:** `google_identity_platform_default_supported_idp_config` ([#1523](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1523)) +* **New Resource:** `google_identity_platform_inbound_saml_config` ([#1523](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1523)) +* **New Resource:** `google_identity_platform_oauth_idp_config` ([#1523](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1523)) +* **New Resource:** `google_identity_platform_tenant_default_supported_idp_config` ([#1523](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1523)) +* **New Resource:** `google_identity_platform_tenant_inbound_saml_config` ([#1523](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1523)) +* **New Resource:** `google_identity_platform_tenant_oauth_idp_config` ([#1523](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1523)) +* **New Resource:** `google_identity_platform_tenant` ([#1523](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1523)) +* **New Resource:** `google_kms_crypto_key_iam_policy` ([#1554](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1554)) +* **New Resource:** `google_kms_secret_ciphertext` ([#1586](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1586)) + +IMPROVEMENTS: +* composer: Increased default timeouts for `google_composer_environment` ([#1539](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1539)) +* compute: Added graceful termination to `container_cluster` create calls so that partially created clusters will resume the original operation if the Terraform process is killed mid create. ([#1533](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1533)) +* compute: Fixed `google_compute_disk_resource_policy_attachment` parsing of region from zone to allow for provider-level zone and make error message more accurate` ([#1557](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1557)) +* datafusion: Increased default timeouts for `google_data_fusion_instance` ([#1545](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1545)) +* datafusion: Increased update timeout for updating `google_data_fusion_instance` ([#1538](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1538)) +* healthcare: Enabled request batching for (beta-only) Healthcare API IAM resources `google_healthcare_*_iam_*` to reduce likelihood of errors from very low default write quota. ([#1558](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1558)) +* iap: added support for IAM Conditions to the `google_iap_web_iam_*`, `google_iap_web_type_compute_iam_*`, `google_iap_web_type_app_engine_*`, and `google_iap_app_engine_service_iam_*` resources (beta provider only) ([#1527](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1527)) +* kms: added support for IAM Conditions to the `google_kms_key_ring_iam_*` and `google_kms_crypto_key_iam_*` resources (beta provider only) ([#1524](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1524)) +* provider: Reduced default `send_after` controlling the time interval after which a batched request sends. ([#1565](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1565)) + +BUG FIXES: +* all: fixed issue where many fields that were removed in 3.0.0 would show a diff when they were removed from config ([#1585](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1585)) +* bigquery: fixed `bigquery_table.encryption_configuration` to correctly recreate the table when modified ([#1591](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1591)) +* cloudrun: Changed `google_cloud_run_domain_mapping` to correctly match Cloud Run API expected format for `spec.route_name`, {serviceName}, instead of invalid projects/{project}/global/services/{serviceName} ([#1563](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1563)) +* cloudrun: Changed `cloud_run_domain_mapping` to poll for success or failure and throw an appropriate error when ready status returns as false. ([#1564](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1564)) +* cloudrun: Fixed `google_cloudrun_service` to allow update instead of force-recreation for changes in `spec` `env` and `command` fields ([#1566](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1566)) +* cloudrun: Removed unsupported update for `google_cloud_run_domain_mapping` to allow force-recreation. ([#1556](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1556)) +* cloudrun: Stopped returning an error when a `cloud_run_domain_mapping` was waiting on DNS verification. ([#1587](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1587)) +* compute: Fixed `google_compute_backend_service` to allow updating `cdn_policy.cache_key_policy.*` fields to false or empty. ([#1569](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1569)) +* compute: Fixed behaviour where `google_compute_subnetwork` did not record a value for `name` when `self_link` was specified. ([#1579](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1579)) +* container: fixed issue where an empty variable in `tags` would cause a crash ([#1543](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1543)) +* endpoints: Added operation wait for `google_endpoints_service` to fix 403 "Service not found" errors during initial creation ([#1560](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1560)) +* logging: Made `google_logging_metric.metric_descriptors.labels` a set to prevent diff from ordering ([#1559](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1559)) +* resourcemanager: added retries for `data.google_organization` ([#1553](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1553)) +* vpcaccess: marked `network` field as required in order to fail invalid configs at plan-time instead of at apply-time ([#1577](https://github.com/terraform-providers/terraform-provider-google-beta/pull/1577)) + ## 3.3.0 (December 17, 2019) BREAKING CHANGES: From d924c9e154dcdc10d90dcc5b7a5eba0d9a44ef7b Mon Sep 17 00:00:00 2001 From: The Magician Date: Tue, 7 Jan 2020 13:46:56 -0800 Subject: [PATCH 07/10] fix docs for google_bigquery_default_service_account (#1596) Signed-off-by: Modular Magician Co-authored-by: Martin Nowak --- ...tml => google_bigquery_default_service_account.html.markdown} | 1 + 1 file changed, 1 insertion(+) rename website/docs/d/{google_bigquery_default_service_account.html => google_bigquery_default_service_account.html.markdown} (97%) diff --git a/website/docs/d/google_bigquery_default_service_account.html b/website/docs/d/google_bigquery_default_service_account.html.markdown similarity index 97% rename from website/docs/d/google_bigquery_default_service_account.html rename to website/docs/d/google_bigquery_default_service_account.html.markdown index 4fb7ea5175..c9717c7ef3 100644 --- a/website/docs/d/google_bigquery_default_service_account.html +++ b/website/docs/d/google_bigquery_default_service_account.html.markdown @@ -1,4 +1,5 @@ --- +subcategory: "BigQuery" layout: "google" page_title: "Google: google_bigquery_default_service_account" sidebar_current: "docs-google-datasource-bigquery-default-service-account" From af0f94effcf1a34d30bae9000e6ee53029709760 Mon Sep 17 00:00:00 2001 From: The Magician Date: Tue, 7 Jan 2020 14:42:25 -0800 Subject: [PATCH 08/10] Nil return for absent Bigtable resources (#1597) Signed-off-by: Modular Magician Co-authored-by: Brian Hildebrandt --- google-beta/resource_bigtable_gc_policy.go | 2 +- google-beta/resource_bigtable_instance.go | 2 +- google-beta/resource_bigtable_table.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/google-beta/resource_bigtable_gc_policy.go b/google-beta/resource_bigtable_gc_policy.go index d77f9ea1ca..aa40d98ccf 100644 --- a/google-beta/resource_bigtable_gc_policy.go +++ b/google-beta/resource_bigtable_gc_policy.go @@ -151,7 +151,7 @@ func resourceBigtableGCPolicyRead(d *schema.ResourceData, meta interface{}) erro if err != nil { log.Printf("[WARN] Removing %s because it's gone", name) d.SetId("") - return fmt.Errorf("Error retrieving table. Could not find %s in %s. %s", name, instanceName, err) + return nil } for _, fi := range ti.FamilyInfos { diff --git a/google-beta/resource_bigtable_instance.go b/google-beta/resource_bigtable_instance.go index 3340a7099b..e6212ffbbf 100644 --- a/google-beta/resource_bigtable_instance.go +++ b/google-beta/resource_bigtable_instance.go @@ -159,7 +159,7 @@ func resourceBigtableInstanceRead(d *schema.ResourceData, meta interface{}) erro if err != nil { log.Printf("[WARN] Removing %s because it's gone", instanceName) d.SetId("") - return fmt.Errorf("Error retrieving instance. Could not find %s. %s", instanceName, err) + return nil } d.Set("project", project) diff --git a/google-beta/resource_bigtable_table.go b/google-beta/resource_bigtable_table.go index 5a24e52240..4c63a6dbf1 100644 --- a/google-beta/resource_bigtable_table.go +++ b/google-beta/resource_bigtable_table.go @@ -142,7 +142,7 @@ func resourceBigtableTableRead(d *schema.ResourceData, meta interface{}) error { if err != nil { log.Printf("[WARN] Removing %s because it's gone", name) d.SetId("") - return fmt.Errorf("Error retrieving table. Could not find %s in %s. %s", name, instanceName, err) + return nil } d.Set("project", project) From ad16517b5f3c3099d7fd94ac72bbebf55bba7bd7 Mon Sep 17 00:00:00 2001 From: megan07 Date: Wed, 8 Jan 2020 19:06:08 +0000 Subject: [PATCH 09/10] add lifecycle_config to dataproc_cluster.cluster_config Signed-off-by: Modular Magician --- google-beta/resource_dataproc_cluster.go | 87 ++++++++++++++++++- google-beta/resource_dataproc_cluster_test.go | 86 ++++++++++++++++++ .../resource_sql_database_instance_test.go | 1 + 3 files changed, 173 insertions(+), 1 deletion(-) diff --git a/google-beta/resource_dataproc_cluster.go b/google-beta/resource_dataproc_cluster.go index ffb24ea2a6..c64b284099 100644 --- a/google-beta/resource_dataproc_cluster.go +++ b/google-beta/resource_dataproc_cluster.go @@ -52,6 +52,7 @@ var ( "cluster_config.0.initialization_action", "cluster_config.0.encryption_config", "cluster_config.0.autoscaling_config", + "cluster_config.0.lifecycle_config", } ) @@ -507,6 +508,40 @@ by Dataproc`, }, }, }, + "lifecycle_config": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + AtLeastOneOf: clusterConfigKeys, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "idle_delete_ttl": { + Type: schema.TypeString, + Optional: true, + AtLeastOneOf: []string{ + "cluster_config.0.lifecycle_config.0.idle_delete_ttl", + "cluster_config.0.lifecycle_config.0.auto_delete_time", + }, + }, + "idle_start_time": { + Type: schema.TypeString, + Computed: true, + }, + // the API also has the auto_delete_ttl option in its request, however, + // the value is not returned in the response, rather the auto_delete_time + // after calculating ttl with the update time is returned, thus, for now + // we will only allow auto_delete_time to updated. + "auto_delete_time": { + Type: schema.TypeString, + Optional: true, + AtLeastOneOf: []string{ + "cluster_config.0.lifecycle_config.0.idle_delete_ttl", + "cluster_config.0.lifecycle_config.0.auto_delete_time", + }, + }, + }, + }, + }, }, }, }, @@ -701,7 +736,6 @@ func resourceDataprocClusterCreate(d *schema.ResourceData, meta interface{}) err log.Printf("[INFO] Dataproc cluster %s has been created", cluster.ClusterName) return resourceDataprocClusterRead(d, meta) - } func expandClusterConfig(d *schema.ResourceData, config *Config) (*dataproc.ClusterConfig, error) { @@ -748,6 +782,10 @@ func expandClusterConfig(d *schema.ResourceData, config *Config) (*dataproc.Clus conf.AutoscalingConfig = expandAutoscalingConfig(cfg) } + if cfg, ok := configOptions(d, "cluster_config.0.lifecycle_config"); ok { + conf.LifecycleConfig = expandLifecycleConfig(cfg) + } + if cfg, ok := configOptions(d, "cluster_config.0.master_config"); ok { log.Println("[INFO] got master_config") conf.MasterConfig = expandInstanceGroupConfig(cfg) @@ -917,6 +955,17 @@ func expandAutoscalingConfig(cfg map[string]interface{}) *dataproc.AutoscalingCo return conf } +func expandLifecycleConfig(cfg map[string]interface{}) *dataproc.LifecycleConfig { + conf := &dataproc.LifecycleConfig{} + if v, ok := cfg["idle_delete_ttl"]; ok { + conf.IdleDeleteTtl = v.(string) + } + if v, ok := cfg["auto_delete_time"]; ok { + conf.AutoDeleteTime = v.(string) + } + return conf +} + func expandInitializationActions(v interface{}) []*dataproc.NodeInitializationAction { actionList := v.([]interface{}) @@ -1063,6 +1112,28 @@ func resourceDataprocClusterUpdate(d *schema.ResourceData, meta interface{}) err updMask = append(updMask, "config.secondary_worker_config.num_instances") } + if d.HasChange("cluster_config.0.lifecycle_config.0.idle_delete_ttl") { + idleDeleteTtl := d.Get("cluster_config.0.lifecycle_config.0.idle_delete_ttl").(string) + cluster.Config.LifecycleConfig = &dataproc.LifecycleConfig{ + IdleDeleteTtl: idleDeleteTtl, + } + + updMask = append(updMask, "config.lifecycle_config.idle_delete_ttl") + } + + if d.HasChange("cluster_config.0.lifecycle_config.0.auto_delete_time") { + desiredDeleteTime := d.Get("cluster_config.0.lifecycle_config.0.auto_delete_time").(string) + if cluster.Config.LifecycleConfig != nil { + cluster.Config.LifecycleConfig.AutoDeleteTime = desiredDeleteTime + } else { + cluster.Config.LifecycleConfig = &dataproc.LifecycleConfig{ + AutoDeleteTime: desiredDeleteTime, + } + } + + updMask = append(updMask, "config.lifecycle_config.auto_delete_time") + } + if len(updMask) > 0 { patch := config.clientDataprocBeta.Projects.Regions.Clusters.Patch( project, region, clusterName, cluster) @@ -1131,6 +1202,7 @@ func flattenClusterConfig(d *schema.ResourceData, cfg *dataproc.ClusterConfig) ( "preemptible_worker_config": flattenPreemptibleInstanceGroupConfig(d, cfg.SecondaryWorkerConfig), "encryption_config": flattenEncryptionConfig(d, cfg.EncryptionConfig), "autoscaling_config": flattenAutoscalingConfig(d, cfg.AutoscalingConfig), + "lifecycle_config": flattenLifecycleConfig(d, cfg.LifecycleConfig), } if len(cfg.InitializationActions) > 0 { @@ -1211,6 +1283,19 @@ func flattenAutoscalingConfig(d *schema.ResourceData, ec *dataproc.AutoscalingCo return []map[string]interface{}{data} } +func flattenLifecycleConfig(d *schema.ResourceData, lc *dataproc.LifecycleConfig) []map[string]interface{} { + if lc == nil { + return nil + } + + data := map[string]interface{}{ + "idle_delete_ttl": lc.IdleDeleteTtl, + "auto_delete_time": lc.AutoDeleteTime, + } + + return []map[string]interface{}{data} +} + func flattenAccelerators(accelerators []*dataproc.AcceleratorConfig) interface{} { acceleratorsTypeSet := schema.NewSet(schema.HashResource(acceleratorsSchema()), []interface{}{}) for _, accelerator := range accelerators { diff --git a/google-beta/resource_dataproc_cluster_test.go b/google-beta/resource_dataproc_cluster_test.go index e86edecc2f..e536cebfcf 100644 --- a/google-beta/resource_dataproc_cluster_test.go +++ b/google-beta/resource_dataproc_cluster_test.go @@ -8,6 +8,7 @@ import ( "strconv" "strings" "testing" + "time" "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" @@ -529,6 +530,61 @@ func TestAccDataprocCluster_withOptionalComponents(t *testing.T) { }) } +func TestAccDataprocCluster_withLifecycleConfigIdleDeleteTtl(t *testing.T) { + t.Parallel() + + rnd := acctest.RandString(10) + var cluster dataproc.Cluster + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDataprocClusterDestroy(), + Steps: []resource.TestStep{ + { + Config: testAccDataprocCluster_withLifecycleConfigIdleDeleteTtl(rnd, "600s"), + Check: resource.ComposeTestCheckFunc( + testAccCheckDataprocClusterExists("google_dataproc_cluster.with_lifecycle_config", &cluster), + ), + }, + { + Config: testAccDataprocCluster_withLifecycleConfigIdleDeleteTtl(rnd, "610s"), + Check: resource.ComposeTestCheckFunc( + testAccCheckDataprocClusterExists("google_dataproc_cluster.with_lifecycle_config", &cluster), + ), + }, + }, + }) +} + +func TestAccDataprocCluster_withLifecycleConfigAutoDeletion(t *testing.T) { + t.Parallel() + + rnd := acctest.RandString(10) + now := time.Now() + fmtString := "2006-01-02T15:04:05.072Z" + + var cluster dataproc.Cluster + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckDataprocClusterDestroy(), + Steps: []resource.TestStep{ + { + Config: testAccDataprocCluster_withLifecycleConfigAutoDeletionTime(rnd, now.Add(time.Hour*10).Format(fmtString)), + Check: resource.ComposeTestCheckFunc( + testAccCheckDataprocClusterExists("google_dataproc_cluster.with_lifecycle_config", &cluster), + ), + }, + { + Config: testAccDataprocCluster_withLifecycleConfigAutoDeletionTime(rnd, now.Add(time.Hour*20).Format(fmtString)), + Check: resource.ComposeTestCheckFunc( + testAccCheckDataprocClusterExists("google_dataproc_cluster.with_lifecycle_config", &cluster), + ), + }, + }, + }) +} + func TestAccDataprocCluster_withLabels(t *testing.T) { t.Parallel() @@ -1202,6 +1258,36 @@ resource "google_dataproc_cluster" "with_opt_components" { `, rnd) } +func testAccDataprocCluster_withLifecycleConfigIdleDeleteTtl(rnd, tm string) string { + return fmt.Sprintf(` +resource "google_dataproc_cluster" "with_lifecycle_config" { + name = "dproc-cluster-test-%s" + region = "us-central1" + + cluster_config { + lifecycle_config { + idle_delete_ttl = "%s" + } + } +} +`, rnd, tm) +} + +func testAccDataprocCluster_withLifecycleConfigAutoDeletionTime(rnd, tm string) string { + return fmt.Sprintf(` +resource "google_dataproc_cluster" "with_lifecycle_config" { + name = "dproc-cluster-test-%s" + region = "us-central1" + + cluster_config { + lifecycle_config { + auto_delete_time = "%s" + } + } +} +`, rnd, tm) +} + func testAccDataprocCluster_withServiceAcc(sa string, rnd string) string { return fmt.Sprintf(` resource "google_service_account" "service_account" { diff --git a/google-beta/resource_sql_database_instance_test.go b/google-beta/resource_sql_database_instance_test.go index 21a0acd20b..07d20ca915 100644 --- a/google-beta/resource_sql_database_instance_test.go +++ b/google-beta/resource_sql_database_instance_test.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/terraform" + sqladmin "google.golang.org/api/sqladmin/v1beta4" ) From bb206122467ee4036fcacb3f67fbac0c7d8402ca Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Wed, 8 Jan 2020 19:56:06 +0000 Subject: [PATCH 10/10] set master_authorized_networks_config.enabled=true when there is a master_authorized_networks_config block (#2939) Merged PR #2939. --- .changelog/2939.txt | 3 +++ google-beta/resource_sql_database_instance_test.go | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 .changelog/2939.txt diff --git a/.changelog/2939.txt b/.changelog/2939.txt new file mode 100644 index 0000000000..f3edc4698e --- /dev/null +++ b/.changelog/2939.txt @@ -0,0 +1,3 @@ +```release-note:REPLACEME +terraform-google-conversion only: set master_authorized_networks_config.enabled to true when there is a master_authorized_networks_config block in Terraform configuration. +``` diff --git a/google-beta/resource_sql_database_instance_test.go b/google-beta/resource_sql_database_instance_test.go index 07d20ca915..21a0acd20b 100644 --- a/google-beta/resource_sql_database_instance_test.go +++ b/google-beta/resource_sql_database_instance_test.go @@ -10,7 +10,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/terraform" - sqladmin "google.golang.org/api/sqladmin/v1beta4" )