Skip to content

Commit

Permalink
Add cmek to spanner database (#4699) (#3181)
Browse files Browse the repository at this point in the history
* Add cmek to spanner database

* Update timeout

* Bump default timeout

* Mark test as beta

* Move to handwritten test

Signed-off-by: Modular Magician <[email protected]>
  • Loading branch information
modular-magician authored Apr 21, 2021
1 parent fc8333a commit 19dd648
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 2 deletions.
6 changes: 6 additions & 0 deletions .changelog/4699.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
```release-note:enhancement
spanner: added support for setting a CMEK on `google_spanner_database`
```
```release-note:note
all: changed default HTTP request timeout from 30 seconds to 120 seconds
```
2 changes: 1 addition & 1 deletion google-beta/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ func expandProviderBatchingConfig(v interface{}) (*batchingConfig, error) {

func (c *Config) synchronousTimeout() time.Duration {
if c.RequestTimeout == 0 {
return 30 * time.Second
return 120 * time.Second
}
return c.RequestTimeout
}
Expand Down
2 changes: 1 addition & 1 deletion google-beta/resource_dataflow_flex_template_job_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
compute "google.golang.org/api/compute/v1"
"google.golang.org/api/compute/v1"
)

func TestAccDataflowFlexTemplateJob_basic(t *testing.T) {
Expand Down
67 changes: 67 additions & 0 deletions google-beta/resource_spanner_database.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,24 @@ error in any statement, the database is not created.`,
Type: schema.TypeString,
},
},
"encryption_config": {
Type: schema.TypeList,
Optional: true,
ForceNew: true,
Description: `Encryption configuration for the database`,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"kms_key_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: `Fully qualified name of the KMS key to use to encrypt this database. This key must exist
in the same location as the Spanner Database.`,
},
},
},
},
"state": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -143,6 +161,12 @@ func resourceSpannerDatabaseCreate(d *schema.ResourceData, meta interface{}) err
} else if v, ok := d.GetOkExists("ddl"); !isEmptyValue(reflect.ValueOf(extraStatementsProp)) && (ok || !reflect.DeepEqual(v, extraStatementsProp)) {
obj["extraStatements"] = extraStatementsProp
}
encryptionConfigProp, err := expandSpannerDatabaseEncryptionConfig(d.Get("encryption_config"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("encryption_config"); !isEmptyValue(reflect.ValueOf(encryptionConfigProp)) && (ok || !reflect.DeepEqual(v, encryptionConfigProp)) {
obj["encryptionConfig"] = encryptionConfigProp
}
instanceProp, err := expandSpannerDatabaseInstance(d.Get("instance"), d, config)
if err != nil {
return err
Expand Down Expand Up @@ -280,6 +304,9 @@ func resourceSpannerDatabaseRead(d *schema.ResourceData, meta interface{}) error
if err := d.Set("state", flattenSpannerDatabaseState(res["state"], d, config)); err != nil {
return fmt.Errorf("Error reading Database: %s", err)
}
if err := d.Set("encryption_config", flattenSpannerDatabaseEncryptionConfig(res["encryptionConfig"], d, config)); err != nil {
return fmt.Errorf("Error reading Database: %s", err)
}
if err := d.Set("instance", flattenSpannerDatabaseInstance(res["instance"], d, config)); err != nil {
return fmt.Errorf("Error reading Database: %s", err)
}
Expand Down Expand Up @@ -434,6 +461,23 @@ func flattenSpannerDatabaseState(v interface{}, d *schema.ResourceData, config *
return v
}

func flattenSpannerDatabaseEncryptionConfig(v interface{}, d *schema.ResourceData, config *Config) interface{} {
if v == nil {
return nil
}
original := v.(map[string]interface{})
if len(original) == 0 {
return nil
}
transformed := make(map[string]interface{})
transformed["kms_key_name"] =
flattenSpannerDatabaseEncryptionConfigKmsKeyName(original["kmsKeyName"], d, config)
return []interface{}{transformed}
}
func flattenSpannerDatabaseEncryptionConfigKmsKeyName(v interface{}, d *schema.ResourceData, config *Config) interface{} {
return v
}

func flattenSpannerDatabaseInstance(v interface{}, d *schema.ResourceData, config *Config) interface{} {
if v == nil {
return v
Expand All @@ -449,6 +493,29 @@ func expandSpannerDatabaseDdl(v interface{}, d TerraformResourceData, config *Co
return v, nil
}

func expandSpannerDatabaseEncryptionConfig(v interface{}, d TerraformResourceData, config *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 := expandSpannerDatabaseEncryptionConfigKmsKeyName(original["kms_key_name"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedKmsKeyName); val.IsValid() && !isEmptyValue(val) {
transformed["kmsKeyName"] = transformedKmsKeyName
}

return transformed, nil
}

func expandSpannerDatabaseEncryptionConfigKmsKeyName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
return v, nil
}

func expandSpannerDatabaseInstance(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
f, err := parseGlobalFieldValue("instances", v.(string), "project", d, config, true)
if err != nil {
Expand Down
88 changes: 88 additions & 0 deletions google-beta/resource_spanner_database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,91 @@ resource "google_spanner_database" "database" {
}
`, context)
}

func TestAccSpannerDatabase_cmek(t *testing.T) {
skipIfVcr(t)
t.Parallel()

context := map[string]interface{}{
"random_suffix": randString(t, 10),
}

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProvidersOiCS,
CheckDestroy: testAccCheckSpannerDatabaseDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccSpannerDatabase_cmek(context),
},
{
ResourceName: "google_spanner_database.database",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"ddl", "deletion_protection"},
},
},
})
}

func testAccSpannerDatabase_cmek(context map[string]interface{}) string {
return Nprintf(`
resource "google_spanner_instance" "main" {
provider = google-beta
config = "regional-europe-west1"
display_name = "main-instance1"
}
resource "google_spanner_database" "database" {
provider = google-beta
instance = google_spanner_instance.main.name
name = "tf-test-cmek-db%{random_suffix}"
ddl = [
"CREATE TABLE t1 (t1 INT64 NOT NULL,) PRIMARY KEY(t1)",
"CREATE TABLE t2 (t2 INT64 NOT NULL,) PRIMARY KEY(t2)",
]
encryption_config {
kms_key_name = google_kms_crypto_key.example-key.id
}
deletion_protection = false
depends_on = [google_kms_crypto_key_iam_binding.crypto-key-binding]
}
resource "google_kms_key_ring" "keyring" {
provider = google-beta
name = "tf-test-ring%{random_suffix}"
location = "europe-west1"
}
resource "google_kms_crypto_key" "example-key" {
provider = google-beta
name = "tf-test-key%{random_suffix}"
key_ring = google_kms_key_ring.keyring.id
rotation_period = "100000s"
}
resource "google_kms_crypto_key_iam_binding" "crypto-key-binding" {
provider = google-beta
crypto_key_id = google_kms_crypto_key.example-key.id
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
members = [
"serviceAccount:${google_project_service_identity.ck_sa.email}",
]
}
data "google_project" "project" {
provider = google-beta
}
resource "google_project_service_identity" "ck_sa" {
provider = google-beta
project = data.google_project.project.project_id
service = "spanner.googleapis.com"
}
`, context)
}
12 changes: 12 additions & 0 deletions website/docs/r/spanner_database.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,25 @@ The following arguments are supported:
execute atomically with the creation of the database: if there is an
error in any statement, the database is not created.

* `encryption_config` -
(Optional)
Encryption configuration for the database
Structure is documented below.

* `project` - (Optional) The ID of the project in which the resource belongs.
If it is not provided, the provider project is used.

* `deletion_protection` - (Optional) Whether or not to allow Terraform to destroy the instance. Unless this field is set to false
in Terraform state, a `terraform destroy` or `terraform apply` that would delete the instance will fail.


The `encryption_config` block supports:

* `kms_key_name` -
(Required)
Fully qualified name of the KMS key to use to encrypt this database. This key must exist
in the same location as the Spanner Database.

## Attributes Reference

In addition to the arguments listed above, the following computed attributes are exported:
Expand Down

0 comments on commit 19dd648

Please sign in to comment.