@@ -35,6 +35,29 @@ import (
3535 "github.com/hashicorp/terraform-provider-google-beta/google-beta/verify"
3636)
3737
38+ // Suppress diffs when the value read from api
39+ // has the project ID instead of the project number
40+ func ProjectIDDiffSuppress (_ , old , new string , _ * schema.ResourceData ) bool {
41+
42+ const marker = "/locations"
43+
44+ // Find the starting index of "/locations" in both strings.
45+ index1 := strings .Index (old , marker )
46+ index2 := strings .Index (new , marker )
47+
48+ // If "/locations" is not found in either string, they can't be compared as requested.
49+ if index1 == - 1 || index2 == - 1 {
50+ return false
51+ }
52+
53+ // Extract the substrings from the marker to the end.
54+ suffix1 := old [index1 :]
55+ suffix2 := new [index2 :]
56+
57+ // Compare the extracted suffixes.
58+ return suffix1 == suffix2
59+ }
60+
3861func ResourceNetappVolume () * schema.Resource {
3962 return & schema.Resource {
4063 Create : resourceNetappVolumeCreate ,
@@ -47,9 +70,9 @@ func ResourceNetappVolume() *schema.Resource {
4770 },
4871
4972 Timeouts : & schema.ResourceTimeout {
50- Create : schema .DefaultTimeout (20 * time .Minute ),
51- Update : schema .DefaultTimeout (20 * time .Minute ),
52- Delete : schema .DefaultTimeout (20 * time .Minute ),
73+ Create : schema .DefaultTimeout (50 * time .Minute ),
74+ Update : schema .DefaultTimeout (30 * time .Minute ),
75+ Delete : schema .DefaultTimeout (30 * time .Minute ),
5376 },
5477
5578 CustomizeDiff : customdiff .All (
@@ -79,18 +102,12 @@ func ResourceNetappVolume() *schema.Resource {
79102 Type : schema .TypeList ,
80103 Required : true ,
81104 ForceNew : true ,
82- Description : `The protocol of the volume. Allowed combinations are '['NFSV3']', '['NFSV4']', '['SMB']', '['NFSV3', 'NFSV4']', '['SMB', 'NFSV3']' and '['SMB', 'NFSV4']'. Possible values: ["NFSV3", "NFSV4", "SMB"]` ,
105+ Description : `The protocol of the volume. Allowed combinations are '['NFSV3']', '['NFSV4']', '['SMB']', '['NFSV3', 'NFSV4']', '['SMB', 'NFSV3']' and '['SMB', 'NFSV4']'. Possible values: ["NFSV3", "NFSV4", "SMB", "ISCSI" ]` ,
83106 Elem : & schema.Schema {
84107 Type : schema .TypeString ,
85- ValidateFunc : verify .ValidateEnum ([]string {"NFSV3" , "NFSV4" , "SMB" }),
108+ ValidateFunc : verify .ValidateEnum ([]string {"NFSV3" , "NFSV4" , "SMB" , "ISCSI" }),
86109 },
87110 },
88- "share_name" : {
89- Type : schema .TypeString ,
90- Required : true ,
91- ForceNew : true ,
92- Description : `Share name (SMB) or export path (NFS) of the volume. Needs to be unique per location.` ,
93- },
94111 "storage_pool" : {
95112 Type : schema .TypeString ,
96113 Required : true ,
@@ -125,6 +142,63 @@ Format: 'projects/{{projectId}}/locations/{{location}}/backupVaults/{{backupVaul
125142 },
126143 },
127144 },
145+ "block_devices" : {
146+ Type : schema .TypeList ,
147+ Optional : true ,
148+ Description : `Block device represents the device(s) which are stored in the block volume.
149+ Currently, only one block device is permitted per Volume.` ,
150+ Elem : & schema.Resource {
151+ Schema : map [string ]* schema.Schema {
152+ "os_type" : {
153+ Type : schema .TypeString ,
154+ Required : true ,
155+ ForceNew : true ,
156+ ValidateFunc : verify .ValidateEnum ([]string {"LINUX" , "WINDOWS" , "ESXI" }),
157+ Description : `The OS type of the volume.
158+ This field can't be changed after the block device is created. Possible values: ["LINUX", "WINDOWS", "ESXI"]` ,
159+ },
160+ "host_groups" : {
161+ Type : schema .TypeList ,
162+ Computed : true ,
163+ Optional : true ,
164+ DiffSuppressFunc : ProjectIDDiffSuppress ,
165+ Description : `A list of host groups that identify hosts that can mount the block volume.
166+ Format:
167+ 'projects/{project_id}/locations/{location}/hostGroups/{host_group_id}'
168+ This field can be updated after the block device is created.` ,
169+ Elem : & schema.Schema {
170+ Type : schema .TypeString ,
171+ },
172+ },
173+ "name" : {
174+ Type : schema .TypeString ,
175+ Computed : true ,
176+ Optional : true ,
177+ Description : `User-defined name for the block device, unique within the Volume. In case
178+ no user input is provided, name will be autogenerated in the backend.
179+ The name must meet the following requirements:
180+ * Be between 1 and 255 characters long.
181+ * Contain only uppercase or lowercase letters (A-Z, a-z), numbers (0-9),
182+ and the following special characters: "-", "_", "}", "{", ".".
183+ * Spaces are not allowed.` ,
184+ },
185+ "identifier" : {
186+ Type : schema .TypeString ,
187+ Computed : true ,
188+ Description : `Device identifier of the Block volume. This represents lun_serial_number
189+ for ISCSI volumes` ,
190+ },
191+ "size_gib" : {
192+ Type : schema .TypeInt ,
193+ Computed : true ,
194+ Description : `The size of the block device in GiB.
195+ Any value provided in this field during Volume creation is IGNORED.
196+ The block device's size is system-managed and will be set to match
197+ the parent Volume's 'capacity_gib'.` ,
198+ },
199+ },
200+ },
201+ },
128202 "description" : {
129203 Type : schema .TypeString ,
130204 Optional : true ,
@@ -366,6 +440,12 @@ Format: 'projects/{{project}}/locations/{{location}}/volumes/{{volume}}/snapshot
366440 Description : `Security Style of the Volume. Use UNIX to use UNIX or NFSV4 ACLs for file permissions.
367441Use NTFS to use NTFS ACLs for file permissions. Can only be set for volumes which use SMB together with NFS as protocol. Possible values: ["NTFS", "UNIX"]` ,
368442 },
443+ "share_name" : {
444+ Type : schema .TypeString ,
445+ Optional : true ,
446+ ForceNew : true ,
447+ Description : `Share name (SMB) or export path (NFS) of the volume. Needs to be unique per location.` ,
448+ },
369449 "smb_settings" : {
370450 Type : schema .TypeList ,
371451 Computed : true ,
@@ -833,6 +913,12 @@ func resourceNetappVolumeCreate(d *schema.ResourceData, meta interface{}) error
833913 } else if v , ok := d .GetOkExists ("throughput_mibps" ); ! tpgresource .IsEmptyValue (reflect .ValueOf (throughputMibpsProp )) && (ok || ! reflect .DeepEqual (v , throughputMibpsProp )) {
834914 obj ["throughputMibps" ] = throughputMibpsProp
835915 }
916+ blockDevicesProp , err := expandNetappVolumeBlockDevices (d .Get ("block_devices" ), d , config )
917+ if err != nil {
918+ return err
919+ } else if v , ok := d .GetOkExists ("block_devices" ); ! tpgresource .IsEmptyValue (reflect .ValueOf (blockDevicesProp )) && (ok || ! reflect .DeepEqual (v , blockDevicesProp )) {
920+ obj ["blockDevices" ] = blockDevicesProp
921+ }
836922 effectiveLabelsProp , err := expandNetappVolumeEffectiveLabels (d .Get ("effective_labels" ), d , config )
837923 if err != nil {
838924 return err
@@ -1055,6 +1141,9 @@ func resourceNetappVolumeRead(d *schema.ResourceData, meta interface{}) error {
10551141 if err := d .Set ("hot_tier_size_used_gib" , flattenNetappVolumeHotTierSizeUsedGib (res ["hotTierSizeUsedGib" ], d , config )); err != nil {
10561142 return fmt .Errorf ("Error reading Volume: %s" , err )
10571143 }
1144+ if err := d .Set ("block_devices" , flattenNetappVolumeBlockDevices (res ["blockDevices" ], d , config )); err != nil {
1145+ return fmt .Errorf ("Error reading Volume: %s" , err )
1146+ }
10581147 if err := d .Set ("terraform_labels" , flattenNetappVolumeTerraformLabels (res ["labels" ], d , config )); err != nil {
10591148 return fmt .Errorf ("Error reading Volume: %s" , err )
10601149 }
@@ -1165,6 +1254,12 @@ func resourceNetappVolumeUpdate(d *schema.ResourceData, meta interface{}) error
11651254 } else if v , ok := d .GetOkExists ("throughput_mibps" ); ! tpgresource .IsEmptyValue (reflect .ValueOf (v )) && (ok || ! reflect .DeepEqual (v , throughputMibpsProp )) {
11661255 obj ["throughputMibps" ] = throughputMibpsProp
11671256 }
1257+ blockDevicesProp , err := expandNetappVolumeBlockDevices (d .Get ("block_devices" ), d , config )
1258+ if err != nil {
1259+ return err
1260+ } else if v , ok := d .GetOkExists ("block_devices" ); ! tpgresource .IsEmptyValue (reflect .ValueOf (v )) && (ok || ! reflect .DeepEqual (v , blockDevicesProp )) {
1261+ obj ["blockDevices" ] = blockDevicesProp
1262+ }
11681263 effectiveLabelsProp , err := expandNetappVolumeEffectiveLabels (d .Get ("effective_labels" ), d , config )
11691264 if err != nil {
11701265 return err
@@ -1241,6 +1336,10 @@ func resourceNetappVolumeUpdate(d *schema.ResourceData, meta interface{}) error
12411336 updateMask = append (updateMask , "throughputMibps" )
12421337 }
12431338
1339+ if d .HasChange ("block_devices" ) {
1340+ updateMask = append (updateMask , "blockDevices" )
1341+ }
1342+
12441343 if d .HasChange ("effective_labels" ) {
12451344 updateMask = append (updateMask , "labels" )
12461345 }
@@ -1250,6 +1349,38 @@ func resourceNetappVolumeUpdate(d *schema.ResourceData, meta interface{}) error
12501349 if err != nil {
12511350 return err
12521351 }
1352+ // remove sizeGib and identifier from the update request for block_devices
1353+
1354+ if v , ok := d .GetOk ("block_devices" ); ok {
1355+
1356+ l := v .([]interface {})
1357+ newBlockDevices := make ([]interface {}, 0 , len (l ))
1358+
1359+ for _ , item := range v .([]interface {}) {
1360+ if item == nil {
1361+ continue
1362+ }
1363+ blockDevice := item .(map [string ]interface {})
1364+ newblockDevice := make (map [string ]interface {})
1365+
1366+ if val , exists := blockDevice ["name" ]; exists {
1367+ newblockDevice ["name" ] = val
1368+ }
1369+ if val , exists := blockDevice ["host_groups" ]; exists {
1370+ newblockDevice ["host_groups" ] = val
1371+ }
1372+ if val , exists := blockDevice ["os_type" ]; exists {
1373+ newblockDevice ["os_type" ] = val
1374+ }
1375+
1376+ newBlockDevices = append (newBlockDevices , newblockDevice )
1377+ }
1378+
1379+ log .Printf ("newBlockDevices %v" , newBlockDevices )
1380+ if len (newBlockDevices ) > 0 {
1381+ obj ["blockDevices" ] = newBlockDevices
1382+ }
1383+ }
12531384
12541385 // err == nil indicates that the billing_project value was found
12551386 if bp , err := tpgresource .GetBillingProject (d , config ); err == nil {
@@ -2108,6 +2239,61 @@ func flattenNetappVolumeHotTierSizeUsedGib(v interface{}, d *schema.ResourceData
21082239 return v
21092240}
21102241
2242+ func flattenNetappVolumeBlockDevices (v interface {}, d * schema.ResourceData , config * transport_tpg.Config ) interface {} {
2243+ if v == nil {
2244+ return v
2245+ }
2246+ l := v .([]interface {})
2247+ transformed := make ([]interface {}, 0 , len (l ))
2248+ for _ , raw := range l {
2249+ original := raw .(map [string ]interface {})
2250+ if len (original ) < 1 {
2251+ // Do not include empty json objects coming back from the api
2252+ continue
2253+ }
2254+ transformed = append (transformed , map [string ]interface {}{
2255+ "name" : flattenNetappVolumeBlockDevicesName (original ["name" ], d , config ),
2256+ "host_groups" : flattenNetappVolumeBlockDevicesHostGroups (original ["hostGroups" ], d , config ),
2257+ "identifier" : flattenNetappVolumeBlockDevicesIdentifier (original ["identifier" ], d , config ),
2258+ "size_gib" : flattenNetappVolumeBlockDevicesSizeGib (original ["sizeGib" ], d , config ),
2259+ "os_type" : flattenNetappVolumeBlockDevicesOsType (original ["osType" ], d , config ),
2260+ })
2261+ }
2262+ return transformed
2263+ }
2264+ func flattenNetappVolumeBlockDevicesName (v interface {}, d * schema.ResourceData , config * transport_tpg.Config ) interface {} {
2265+ return v
2266+ }
2267+
2268+ func flattenNetappVolumeBlockDevicesHostGroups (v interface {}, d * schema.ResourceData , config * transport_tpg.Config ) interface {} {
2269+ return v
2270+ }
2271+
2272+ func flattenNetappVolumeBlockDevicesIdentifier (v interface {}, d * schema.ResourceData , config * transport_tpg.Config ) interface {} {
2273+ return v
2274+ }
2275+
2276+ func flattenNetappVolumeBlockDevicesSizeGib (v interface {}, d * schema.ResourceData , config * transport_tpg.Config ) interface {} {
2277+ // Handles the string fixed64 format
2278+ if strVal , ok := v .(string ); ok {
2279+ if intVal , err := tpgresource .StringToFixed64 (strVal ); err == nil {
2280+ return intVal
2281+ }
2282+ }
2283+
2284+ // number values are represented as float64
2285+ if floatVal , ok := v .(float64 ); ok {
2286+ intVal := int (floatVal )
2287+ return intVal
2288+ }
2289+
2290+ return v // let terraform core handle it otherwise
2291+ }
2292+
2293+ func flattenNetappVolumeBlockDevicesOsType (v interface {}, d * schema.ResourceData , config * transport_tpg.Config ) interface {} {
2294+ return v
2295+ }
2296+
21112297func flattenNetappVolumeTerraformLabels (v interface {}, d * schema.ResourceData , config * transport_tpg.Config ) interface {} {
21122298 if v == nil {
21132299 return v
@@ -2899,6 +3085,79 @@ func expandNetappVolumeThroughputMibps(v interface{}, d tpgresource.TerraformRes
28993085 return v , nil
29003086}
29013087
3088+ func expandNetappVolumeBlockDevices (v interface {}, d tpgresource.TerraformResourceData , config * transport_tpg.Config ) (interface {}, error ) {
3089+ if v == nil {
3090+ return nil , nil
3091+ }
3092+ l := v .([]interface {})
3093+ req := make ([]interface {}, 0 , len (l ))
3094+ for _ , raw := range l {
3095+ if raw == nil {
3096+ continue
3097+ }
3098+ original := raw .(map [string ]interface {})
3099+ transformed := make (map [string ]interface {})
3100+
3101+ transformedName , err := expandNetappVolumeBlockDevicesName (original ["name" ], d , config )
3102+ if err != nil {
3103+ return nil , err
3104+ } else if val := reflect .ValueOf (transformedName ); val .IsValid () && ! tpgresource .IsEmptyValue (val ) {
3105+ transformed ["name" ] = transformedName
3106+ }
3107+
3108+ transformedHostGroups , err := expandNetappVolumeBlockDevicesHostGroups (original ["host_groups" ], d , config )
3109+ if err != nil {
3110+ return nil , err
3111+ } else if val := reflect .ValueOf (transformedHostGroups ); val .IsValid () && ! tpgresource .IsEmptyValue (val ) {
3112+ transformed ["hostGroups" ] = transformedHostGroups
3113+ }
3114+
3115+ transformedIdentifier , err := expandNetappVolumeBlockDevicesIdentifier (original ["identifier" ], d , config )
3116+ if err != nil {
3117+ return nil , err
3118+ } else if val := reflect .ValueOf (transformedIdentifier ); val .IsValid () && ! tpgresource .IsEmptyValue (val ) {
3119+ transformed ["identifier" ] = transformedIdentifier
3120+ }
3121+
3122+ transformedSizeGib , err := expandNetappVolumeBlockDevicesSizeGib (original ["size_gib" ], d , config )
3123+ if err != nil {
3124+ return nil , err
3125+ } else if val := reflect .ValueOf (transformedSizeGib ); val .IsValid () && ! tpgresource .IsEmptyValue (val ) {
3126+ transformed ["sizeGib" ] = transformedSizeGib
3127+ }
3128+
3129+ transformedOsType , err := expandNetappVolumeBlockDevicesOsType (original ["os_type" ], d , config )
3130+ if err != nil {
3131+ return nil , err
3132+ } else if val := reflect .ValueOf (transformedOsType ); val .IsValid () && ! tpgresource .IsEmptyValue (val ) {
3133+ transformed ["osType" ] = transformedOsType
3134+ }
3135+
3136+ req = append (req , transformed )
3137+ }
3138+ return req , nil
3139+ }
3140+
3141+ func expandNetappVolumeBlockDevicesName (v interface {}, d tpgresource.TerraformResourceData , config * transport_tpg.Config ) (interface {}, error ) {
3142+ return v , nil
3143+ }
3144+
3145+ func expandNetappVolumeBlockDevicesHostGroups (v interface {}, d tpgresource.TerraformResourceData , config * transport_tpg.Config ) (interface {}, error ) {
3146+ return v , nil
3147+ }
3148+
3149+ func expandNetappVolumeBlockDevicesIdentifier (v interface {}, d tpgresource.TerraformResourceData , config * transport_tpg.Config ) (interface {}, error ) {
3150+ return v , nil
3151+ }
3152+
3153+ func expandNetappVolumeBlockDevicesSizeGib (v interface {}, d tpgresource.TerraformResourceData , config * transport_tpg.Config ) (interface {}, error ) {
3154+ return v , nil
3155+ }
3156+
3157+ func expandNetappVolumeBlockDevicesOsType (v interface {}, d tpgresource.TerraformResourceData , config * transport_tpg.Config ) (interface {}, error ) {
3158+ return v , nil
3159+ }
3160+
29023161func expandNetappVolumeEffectiveLabels (v interface {}, d tpgresource.TerraformResourceData , config * transport_tpg.Config ) (map [string ]string , error ) {
29033162 if v == nil {
29043163 return map [string ]string {}, nil
0 commit comments