From b3df08c294efcb0bda1598551b76b5d2ed4d3c0c Mon Sep 17 00:00:00 2001 From: The Magician Date: Wed, 13 Apr 2022 12:22:03 -0500 Subject: [PATCH] =?UTF-8?q?Move=20filestore=20enterprise=20features=20out?= =?UTF-8?q?=20of=20beta=20and=20add=20support=20for=20cu=E2=80=A6=20(#5875?= =?UTF-8?q?)=20(#11493)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Modular Magician --- .changelog/5875.txt | 4 + google/resource_filestore_instance.go | 255 +++++++++++++++++- ...ource_filestore_instance_generated_test.go | 60 +++++ .../docs/r/filestore_instance.html.markdown | 42 ++- 4 files changed, 354 insertions(+), 7 deletions(-) create mode 100644 .changelog/5875.txt diff --git a/.changelog/5875.txt b/.changelog/5875.txt new file mode 100644 index 00000000000..aa9ad801bd3 --- /dev/null +++ b/.changelog/5875.txt @@ -0,0 +1,4 @@ +```release-note:enhancement +filestore: promoted enterprise features to GA +filestore: added `kms_key_name` field to `google_filestore_instance` resource to support CMEK +``` diff --git a/google/resource_filestore_instance.go b/google/resource_filestore_instance.go index 3d59530776f..23cbaf5bcb2 100644 --- a/google/resource_filestore_instance.go +++ b/google/resource_filestore_instance.go @@ -73,6 +73,56 @@ for the standard tier, or 2560 GiB for the premium tier.`, ForceNew: true, Description: `The name of the fileshare (16 characters or less)`, }, + "nfs_export_options": { + Type: schema.TypeList, + Optional: true, + Description: `Nfs Export Options. There is a limit of 10 export options per file share.`, + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "access_mode": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateEnum([]string{"READ_ONLY", "READ_WRITE", ""}), + Description: `Either READ_ONLY, for allowing only read requests on the exported directory, +or READ_WRITE, for allowing both read and write requests. The default is READ_WRITE. Default value: "READ_WRITE" Possible values: ["READ_ONLY", "READ_WRITE"]`, + Default: "READ_WRITE", + }, + "anon_gid": { + Type: schema.TypeInt, + Optional: true, + Description: `An integer representing the anonymous group id with a default value of 65534. +Anon_gid may only be set with squashMode of ROOT_SQUASH. An error will be returned +if this field is specified for other squashMode settings.`, + }, + "anon_uid": { + Type: schema.TypeInt, + Optional: true, + Description: `An integer representing the anonymous user id with a default value of 65534. +Anon_uid may only be set with squashMode of ROOT_SQUASH. An error will be returned +if this field is specified for other squashMode settings.`, + }, + "ip_ranges": { + Type: schema.TypeList, + Optional: true, + Description: `List of either IPv4 addresses, or ranges in CIDR notation which may mount the file share. +Overlapping IP ranges are not allowed, both within and across NfsExportOptions. An error will be returned. +The limit is 64 IP ranges/addresses for each FileShareConfig among all NfsExportOptions.`, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "squash_mode": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateEnum([]string{"NO_ROOT_SQUASH", "ROOT_SQUASH", ""}), + Description: `Either NO_ROOT_SQUASH, for allowing root access on the exported directory, or ROOT_SQUASH, +for not allowing root access. The default is NO_ROOT_SQUASH. Default value: "NO_ROOT_SQUASH" Possible values: ["NO_ROOT_SQUASH", "ROOT_SQUASH"]`, + Default: "NO_ROOT_SQUASH", + }, + }, + }, + }, }, }, }, @@ -108,6 +158,16 @@ IP addresses assigned. Possible values: ["ADDRESS_MODE_UNSPECIFIED", "MODE_IPV4" Description: `The name of the GCE VPC network to which the instance is connected.`, }, + "connect_mode": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateEnum([]string{"DIRECT_PEERING", "PRIVATE_SERVICE_ACCESS", ""}), + Description: `The network connect mode of the Filestore instance. +If not provided, the connect mode defaults to +DIRECT_PEERING. Default value: "DIRECT_PEERING" Possible values: ["DIRECT_PEERING", "PRIVATE_SERVICE_ACCESS"]`, + Default: "DIRECT_PEERING", + }, "reserved_ip_range": { Type: schema.TypeString, Computed: true, @@ -132,13 +192,19 @@ addresses reserved for this instance.`, Required: true, ForceNew: true, Description: `The service tier of the instance. -Possible values include: STANDARD, PREMIUM, BASIC_HDD, BASIC_SSD, HIGH_SCALE_SSD and ENTERPRISE (beta only)`, +Possible values include: STANDARD, PREMIUM, BASIC_HDD, BASIC_SSD, HIGH_SCALE_SSD and ENTERPRISE`, }, "description": { Type: schema.TypeString, Optional: true, Description: `A description of the instance.`, }, + "kms_key_name": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: `KMS key name used for data encryption.`, + }, "labels": { Type: schema.TypeMap, Optional: true, @@ -222,6 +288,12 @@ func resourceFilestoreInstanceCreate(d *schema.ResourceData, meta interface{}) e } else if v, ok := d.GetOkExists("networks"); !isEmptyValue(reflect.ValueOf(networksProp)) && (ok || !reflect.DeepEqual(v, networksProp)) { obj["networks"] = networksProp } + kmsKeyNameProp, err := expandFilestoreInstanceKmsKeyName(d.Get("kms_key_name"), d, config) + if err != nil { + return err + } else if v, ok := d.GetOkExists("kms_key_name"); !isEmptyValue(reflect.ValueOf(kmsKeyNameProp)) && (ok || !reflect.DeepEqual(v, kmsKeyNameProp)) { + obj["kmsKeyName"] = kmsKeyNameProp + } lockName, err := replaceVars(d, config, "filestore/{{project}}") if err != nil { @@ -357,6 +429,9 @@ func resourceFilestoreInstanceRead(d *schema.ResourceData, meta interface{}) err if err := d.Set("etag", flattenFilestoreInstanceEtag(res["etag"], d, config)); err != nil { return fmt.Errorf("Error reading Instance: %s", err) } + if err := d.Set("kms_key_name", flattenFilestoreInstanceKmsKeyName(res["kmsKeyName"], d, config)); err != nil { + return fmt.Errorf("Error reading Instance: %s", err) + } return nil } @@ -554,8 +629,9 @@ func flattenFilestoreInstanceFileShares(v interface{}, d *schema.ResourceData, c continue } transformed = append(transformed, map[string]interface{}{ - "name": flattenFilestoreInstanceFileSharesName(original["name"], d, config), - "capacity_gb": flattenFilestoreInstanceFileSharesCapacityGb(original["capacityGb"], d, config), + "name": flattenFilestoreInstanceFileSharesName(original["name"], d, config), + "capacity_gb": flattenFilestoreInstanceFileSharesCapacityGb(original["capacityGb"], d, config), + "nfs_export_options": flattenFilestoreInstanceFileSharesNfsExportOptions(original["nfsExportOptions"], d, config), }) } return transformed @@ -581,6 +657,74 @@ func flattenFilestoreInstanceFileSharesCapacityGb(v interface{}, d *schema.Resou return v // let terraform core handle it otherwise } +func flattenFilestoreInstanceFileSharesNfsExportOptions(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil { + return v + } + l := v.([]interface{}) + transformed := make([]interface{}, 0, len(l)) + for _, raw := range l { + original := raw.(map[string]interface{}) + if len(original) < 1 { + // Do not include empty json objects coming back from the api + continue + } + transformed = append(transformed, map[string]interface{}{ + "ip_ranges": flattenFilestoreInstanceFileSharesNfsExportOptionsIpRanges(original["ipRanges"], d, config), + "access_mode": flattenFilestoreInstanceFileSharesNfsExportOptionsAccessMode(original["accessMode"], d, config), + "squash_mode": flattenFilestoreInstanceFileSharesNfsExportOptionsSquashMode(original["squashMode"], d, config), + "anon_uid": flattenFilestoreInstanceFileSharesNfsExportOptionsAnonUid(original["anonUid"], d, config), + "anon_gid": flattenFilestoreInstanceFileSharesNfsExportOptionsAnonGid(original["anonGid"], d, config), + }) + } + return transformed +} +func flattenFilestoreInstanceFileSharesNfsExportOptionsIpRanges(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenFilestoreInstanceFileSharesNfsExportOptionsAccessMode(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenFilestoreInstanceFileSharesNfsExportOptionsSquashMode(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + +func flattenFilestoreInstanceFileSharesNfsExportOptionsAnonUid(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := 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 flattenFilestoreInstanceFileSharesNfsExportOptionsAnonGid(v interface{}, d *schema.ResourceData, config *Config) interface{} { + // Handles the string fixed64 format + if strVal, ok := v.(string); ok { + if intVal, err := 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 flattenFilestoreInstanceNetworks(v interface{}, d *schema.ResourceData, config *Config) interface{} { if v == nil { return v @@ -598,6 +742,7 @@ func flattenFilestoreInstanceNetworks(v interface{}, d *schema.ResourceData, con "modes": flattenFilestoreInstanceNetworksModes(original["modes"], d, config), "reserved_ip_range": flattenFilestoreInstanceNetworksReservedIpRange(original["reservedIpRange"], d, config), "ip_addresses": flattenFilestoreInstanceNetworksIpAddresses(original["ipAddresses"], d, config), + "connect_mode": flattenFilestoreInstanceNetworksConnectMode(original["connectMode"], d, config), }) } return transformed @@ -618,10 +763,22 @@ func flattenFilestoreInstanceNetworksIpAddresses(v interface{}, d *schema.Resour return v } +func flattenFilestoreInstanceNetworksConnectMode(v interface{}, d *schema.ResourceData, config *Config) interface{} { + if v == nil || isEmptyValue(reflect.ValueOf(v)) { + return "DIRECT_PEERING" + } + + return v +} + func flattenFilestoreInstanceEtag(v interface{}, d *schema.ResourceData, config *Config) interface{} { return v } +func flattenFilestoreInstanceKmsKeyName(v interface{}, d *schema.ResourceData, config *Config) interface{} { + return v +} + func expandFilestoreInstanceDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { return v, nil } @@ -665,6 +822,13 @@ func expandFilestoreInstanceFileShares(v interface{}, d TerraformResourceData, c transformed["capacityGb"] = transformedCapacityGb } + transformedNfsExportOptions, err := expandFilestoreInstanceFileSharesNfsExportOptions(original["nfs_export_options"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedNfsExportOptions); val.IsValid() && !isEmptyValue(val) { + transformed["nfsExportOptions"] = transformedNfsExportOptions + } + req = append(req, transformed) } return req, nil @@ -678,6 +842,76 @@ func expandFilestoreInstanceFileSharesCapacityGb(v interface{}, d TerraformResou return v, nil } +func expandFilestoreInstanceFileSharesNfsExportOptions(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + l := v.([]interface{}) + req := make([]interface{}, 0, len(l)) + for _, raw := range l { + if raw == nil { + continue + } + original := raw.(map[string]interface{}) + transformed := make(map[string]interface{}) + + transformedIpRanges, err := expandFilestoreInstanceFileSharesNfsExportOptionsIpRanges(original["ip_ranges"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedIpRanges); val.IsValid() && !isEmptyValue(val) { + transformed["ipRanges"] = transformedIpRanges + } + + transformedAccessMode, err := expandFilestoreInstanceFileSharesNfsExportOptionsAccessMode(original["access_mode"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAccessMode); val.IsValid() && !isEmptyValue(val) { + transformed["accessMode"] = transformedAccessMode + } + + transformedSquashMode, err := expandFilestoreInstanceFileSharesNfsExportOptionsSquashMode(original["squash_mode"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedSquashMode); val.IsValid() && !isEmptyValue(val) { + transformed["squashMode"] = transformedSquashMode + } + + transformedAnonUid, err := expandFilestoreInstanceFileSharesNfsExportOptionsAnonUid(original["anon_uid"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAnonUid); val.IsValid() && !isEmptyValue(val) { + transformed["anonUid"] = transformedAnonUid + } + + transformedAnonGid, err := expandFilestoreInstanceFileSharesNfsExportOptionsAnonGid(original["anon_gid"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedAnonGid); val.IsValid() && !isEmptyValue(val) { + transformed["anonGid"] = transformedAnonGid + } + + req = append(req, transformed) + } + return req, nil +} + +func expandFilestoreInstanceFileSharesNfsExportOptionsIpRanges(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandFilestoreInstanceFileSharesNfsExportOptionsAccessMode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandFilestoreInstanceFileSharesNfsExportOptionsSquashMode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandFilestoreInstanceFileSharesNfsExportOptionsAnonUid(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandFilestoreInstanceFileSharesNfsExportOptionsAnonGid(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func expandFilestoreInstanceNetworks(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { l := v.([]interface{}) req := make([]interface{}, 0, len(l)) @@ -716,6 +950,13 @@ func expandFilestoreInstanceNetworks(v interface{}, d TerraformResourceData, con transformed["ipAddresses"] = transformedIpAddresses } + transformedConnectMode, err := expandFilestoreInstanceNetworksConnectMode(original["connect_mode"], d, config) + if err != nil { + return nil, err + } else if val := reflect.ValueOf(transformedConnectMode); val.IsValid() && !isEmptyValue(val) { + transformed["connectMode"] = transformedConnectMode + } + req = append(req, transformed) } return req, nil @@ -737,6 +978,14 @@ func expandFilestoreInstanceNetworksIpAddresses(v interface{}, d TerraformResour return v, nil } +func expandFilestoreInstanceNetworksConnectMode(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + +func expandFilestoreInstanceKmsKeyName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) { + return v, nil +} + func resourceFilestoreInstanceResourceV0() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ diff --git a/google/resource_filestore_instance_generated_test.go b/google/resource_filestore_instance_generated_test.go index f5f25698bc3..be3e8510139 100644 --- a/google/resource_filestore_instance_generated_test.go +++ b/google/resource_filestore_instance_generated_test.go @@ -68,6 +68,66 @@ resource "google_filestore_instance" "instance" { `, context) } +func TestAccFilestoreInstance_filestoreInstanceFullExample(t *testing.T) { + t.Parallel() + + context := map[string]interface{}{ + "random_suffix": randString(t, 10), + } + + vcrTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckFilestoreInstanceDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccFilestoreInstance_filestoreInstanceFullExample(context), + }, + { + ResourceName: "google_filestore_instance.instance", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"name", "zone", "location"}, + }, + }, + }) +} + +func testAccFilestoreInstance_filestoreInstanceFullExample(context map[string]interface{}) string { + return Nprintf(` +resource "google_filestore_instance" "instance" { + name = "tf-test-test-instance%{random_suffix}" + location = "us-central1-b" + tier = "BASIC_SSD" + + file_shares { + capacity_gb = 2660 + name = "share1" + + nfs_export_options { + ip_ranges = ["10.0.0.0/24"] + access_mode = "READ_WRITE" + squash_mode = "NO_ROOT_SQUASH" + } + + nfs_export_options { + ip_ranges = ["10.10.0.0/24"] + access_mode = "READ_ONLY" + squash_mode = "ROOT_SQUASH" + anon_uid = 123 + anon_gid = 456 + } + } + + networks { + network = "default" + modes = ["MODE_IPV4"] + connect_mode = "DIRECT_PEERING" + } +} +`, context) +} + func testAccCheckFilestoreInstanceDestroyProducer(t *testing.T) func(s *terraform.State) error { return func(s *terraform.State) error { for name, rs := range s.RootModule().Resources { diff --git a/website/docs/r/filestore_instance.html.markdown b/website/docs/r/filestore_instance.html.markdown index 475185e62a6..a7187f17e92 100644 --- a/website/docs/r/filestore_instance.html.markdown +++ b/website/docs/r/filestore_instance.html.markdown @@ -68,7 +68,6 @@ resource "google_filestore_instance" "instance" { ```hcl resource "google_filestore_instance" "instance" { - provider = google-beta name = "test-instance" location = "us-central1-b" tier = "BASIC_SSD" @@ -99,6 +98,37 @@ resource "google_filestore_instance" "instance" { } } ``` +## Example Usage - Filestore Instance Enterprise + + +```hcl +resource "google_filestore_instance" "instance" { + name = "test-instance" + location = "us-central1" + tier = "ENTERPRISE" + + file_shares { + capacity_gb = 2560 + name = "share1" + } + + networks { + network = "default" + modes = ["MODE_IPV4"] + } + kms_key_name = google_kms_crypto_key.filestore_key.id +} + +resource "google_kms_key_ring" "filestore_keyring" { + name = "filestore-keyring" + location = "us-central1" +} + +resource "google_kms_crypto_key" "filestore_key" { + name = "filestore-key" + key_ring = google_kms_key_ring.filestore_keyring.id +} +``` ## Argument Reference @@ -112,7 +142,7 @@ The following arguments are supported: * `tier` - (Required) The service tier of the instance. - Possible values include: STANDARD, PREMIUM, BASIC_HDD, BASIC_SSD, HIGH_SCALE_SSD and ENTERPRISE (beta only) + Possible values include: STANDARD, PREMIUM, BASIC_HDD, BASIC_SSD, HIGH_SCALE_SSD and ENTERPRISE * `file_shares` - (Required) @@ -139,7 +169,7 @@ The following arguments are supported: for the standard tier, or 2560 GiB for the premium tier. * `nfs_export_options` - - (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) + (Optional) Nfs Export Options. There is a limit of 10 export options per file share. Structure is [documented below](#nested_nfs_export_options). @@ -200,7 +230,7 @@ The following arguments are supported: A list of IPv4 or IPv6 addresses. * `connect_mode` - - (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) + (Optional) The network connect mode of the Filestore instance. If not provided, the connect mode defaults to DIRECT_PEERING. @@ -218,6 +248,10 @@ The following arguments are supported: (Optional) Resource labels to represent user-provided metadata. +* `kms_key_name` - + (Optional) + KMS key name used for data encryption. + * `zone` - (Optional, Deprecated) The name of the Filestore zone of the instance.