@@ -23,6 +23,7 @@ import (
23
23
"time"
24
24
25
25
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
26
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
26
27
)
27
28
28
29
func resourceNameSetFromSelfLinkSet (v interface {}) * schema.Set {
@@ -111,6 +112,48 @@ func computeRouterNatIPsHash(v interface{}) int {
111
112
return schema .HashString (GetResourceNameFromSelfLink (val ))
112
113
}
113
114
115
+ func computeRouterNatRulesHash (v interface {}) int {
116
+ obj := v .(map [string ]interface {})
117
+ ruleNumber := obj ["rule_number" ].(int )
118
+
119
+ description := obj ["description" ]
120
+ descriptionHash := 0
121
+ if description != nil {
122
+ descriptionHash = schema .HashString (description .(string ))
123
+ }
124
+
125
+ match := obj ["match" ].(string )
126
+
127
+ sourceNatActiveIpHash := 0
128
+ sourceNatDrainIpHash := 0
129
+ if obj ["action" ] != nil {
130
+ actions := obj ["action" ].([]interface {})
131
+ if len (actions ) != 0 && actions [0 ] != nil {
132
+ action := actions [0 ].(map [string ]interface {})
133
+
134
+ sourceNatActiveIps := action ["source_nat_active_ips" ]
135
+ if sourceNatActiveIps != nil {
136
+ sourceNatActiveIpSet := sourceNatActiveIps .(* schema.Set )
137
+ for _ , sourceNatActiveIp := range sourceNatActiveIpSet .List () {
138
+ sourceNatActiveIpStr := fmt .Sprintf ("source_nat_active_ips-%d" , computeRouterNatIPsHash (sourceNatActiveIp .(string )))
139
+ sourceNatActiveIpHash += schema .HashString (sourceNatActiveIpStr )
140
+ }
141
+ }
142
+
143
+ soureNatDrainIps := action ["source_nat_drain_ips" ]
144
+ if soureNatDrainIps != nil {
145
+ soureNatDrainIpSet := soureNatDrainIps .(* schema.Set )
146
+ for _ , soureNatDrainIp := range soureNatDrainIpSet .List () {
147
+ sourceNatDrainIpStr := fmt .Sprintf ("source_nat_drain_ips-%d" , computeRouterNatIPsHash (soureNatDrainIp .(string )))
148
+ sourceNatDrainIpHash += schema .HashString (sourceNatDrainIpStr )
149
+ }
150
+ }
151
+ }
152
+ }
153
+
154
+ return ruleNumber + descriptionHash + schema .HashString (match ) + sourceNatActiveIpHash + sourceNatDrainIpHash
155
+ }
156
+
114
157
func resourceComputeRouterNat () * schema.Resource {
115
158
return & schema.Resource {
116
159
Create : resourceComputeRouterNatCreate ,
@@ -256,6 +299,13 @@ is set to MANUAL_ONLY.`,
256
299
DiffSuppressFunc : compareSelfLinkOrResourceName ,
257
300
Description : `Region where the router and NAT reside.` ,
258
301
},
302
+ "rules" : {
303
+ Type : schema .TypeSet ,
304
+ Optional : true ,
305
+ Description : `A list of rules associated with this NAT.` ,
306
+ Elem : computeRouterNatRulesSchema (),
307
+ Set : computeRouterNatRulesHash ,
308
+ },
259
309
"subnetwork" : {
260
310
Type : schema .TypeSet ,
261
311
Optional : true ,
@@ -333,6 +383,77 @@ sourceIpRangesToNat`,
333
383
}
334
384
}
335
385
386
+ func computeRouterNatRulesSchema () * schema.Resource {
387
+ return & schema.Resource {
388
+ Schema : map [string ]* schema.Schema {
389
+ "match" : {
390
+ Type : schema .TypeString ,
391
+ Required : true ,
392
+ Description : `CEL expression that specifies the match condition that egress traffic from a VM is evaluated against.
393
+ If it evaluates to true, the corresponding action is enforced.
394
+
395
+ The following examples are valid match expressions for public NAT:
396
+
397
+ "inIpRange(destination.ip, '1.1.0.0/16') || inIpRange(destination.ip, '2.2.0.0/16')"
398
+
399
+ "destination.ip == '1.1.0.1' || destination.ip == '8.8.8.8'"
400
+
401
+ The following example is a valid match expression for private NAT:
402
+
403
+ "nexthop.hub == 'https://networkconnectivity.googleapis.com/v1alpha1/projects/my-project/global/hub/hub-1'"` ,
404
+ },
405
+ "rule_number" : {
406
+ Type : schema .TypeInt ,
407
+ Required : true ,
408
+ ValidateFunc : validation .IntBetween (0 , 65000 ),
409
+ Description : `An integer uniquely identifying a rule in the list.
410
+ The rule number must be a positive value between 0 and 65000, and must be unique among rules within a NAT.` ,
411
+ },
412
+ "action" : {
413
+ Type : schema .TypeList ,
414
+ Computed : true ,
415
+ Optional : true ,
416
+ Description : `The action to be enforced for traffic that matches this rule.` ,
417
+ MaxItems : 1 ,
418
+ Elem : & schema.Resource {
419
+ Schema : map [string ]* schema.Schema {
420
+ "source_nat_active_ips" : {
421
+ Type : schema .TypeSet ,
422
+ Optional : true ,
423
+ Description : `A list of URLs of the IP resources used for this NAT rule.
424
+ These IP addresses must be valid static external IP addresses assigned to the project.
425
+ This field is used for public NAT.` ,
426
+ Elem : & schema.Schema {
427
+ Type : schema .TypeString ,
428
+ DiffSuppressFunc : compareSelfLinkOrResourceName ,
429
+ },
430
+ Set : computeRouterNatIPsHash ,
431
+ },
432
+ "source_nat_drain_ips" : {
433
+ Type : schema .TypeSet ,
434
+ Optional : true ,
435
+ Description : `A list of URLs of the IP resources to be drained.
436
+ These IPs must be valid static external IPs that have been assigned to the NAT.
437
+ These IPs should be used for updating/patching a NAT rule only.
438
+ This field is used for public NAT.` ,
439
+ Elem : & schema.Schema {
440
+ Type : schema .TypeString ,
441
+ DiffSuppressFunc : compareSelfLinkOrResourceName ,
442
+ },
443
+ Set : computeRouterNatIPsHash ,
444
+ },
445
+ },
446
+ },
447
+ },
448
+ "description" : {
449
+ Type : schema .TypeString ,
450
+ Optional : true ,
451
+ Description : `An optional description of this rule.` ,
452
+ },
453
+ },
454
+ }
455
+ }
456
+
336
457
func resourceComputeRouterNatCreate (d * schema.ResourceData , meta interface {}) error {
337
458
config := meta .(* Config )
338
459
userAgent , err := generateUserAgentString (d , config .userAgent )
@@ -425,6 +546,12 @@ func resourceComputeRouterNatCreate(d *schema.ResourceData, meta interface{}) er
425
546
} else if v , ok := d .GetOkExists ("log_config" ); ok || ! reflect .DeepEqual (v , logConfigProp ) {
426
547
obj ["logConfig" ] = logConfigProp
427
548
}
549
+ rulesProp , err := expandNestedComputeRouterNatRules (d .Get ("rules" ), d , config )
550
+ if err != nil {
551
+ return err
552
+ } else if v , ok := d .GetOkExists ("rules" ); ok || ! reflect .DeepEqual (v , rulesProp ) {
553
+ obj ["rules" ] = rulesProp
554
+ }
428
555
enableEndpointIndependentMappingProp , err := expandNestedComputeRouterNatEnableEndpointIndependentMapping (d .Get ("enable_endpoint_independent_mapping" ), d , config )
429
556
if err != nil {
430
557
return err
@@ -578,6 +705,9 @@ func resourceComputeRouterNatRead(d *schema.ResourceData, meta interface{}) erro
578
705
if err := d .Set ("log_config" , flattenNestedComputeRouterNatLogConfig (res ["logConfig" ], d , config )); err != nil {
579
706
return fmt .Errorf ("Error reading RouterNat: %s" , err )
580
707
}
708
+ if err := d .Set ("rules" , flattenNestedComputeRouterNatRules (res ["rules" ], d , config )); err != nil {
709
+ return fmt .Errorf ("Error reading RouterNat: %s" , err )
710
+ }
581
711
if err := d .Set ("enable_endpoint_independent_mapping" , flattenNestedComputeRouterNatEnableEndpointIndependentMapping (res ["enableEndpointIndependentMapping" ], d , config )); err != nil {
582
712
return fmt .Errorf ("Error reading RouterNat: %s" , err )
583
713
}
@@ -679,6 +809,12 @@ func resourceComputeRouterNatUpdate(d *schema.ResourceData, meta interface{}) er
679
809
} else if v , ok := d .GetOkExists ("log_config" ); ok || ! reflect .DeepEqual (v , logConfigProp ) {
680
810
obj ["logConfig" ] = logConfigProp
681
811
}
812
+ rulesProp , err := expandNestedComputeRouterNatRules (d .Get ("rules" ), d , config )
813
+ if err != nil {
814
+ return err
815
+ } else if v , ok := d .GetOkExists ("rules" ); ok || ! reflect .DeepEqual (v , rulesProp ) {
816
+ obj ["rules" ] = rulesProp
817
+ }
682
818
enableEndpointIndependentMappingProp , err := expandNestedComputeRouterNatEnableEndpointIndependentMapping (d .Get ("enable_endpoint_independent_mapping" ), d , config )
683
819
if err != nil {
684
820
return err
@@ -991,6 +1127,81 @@ func flattenNestedComputeRouterNatLogConfigFilter(v interface{}, d *schema.Resou
991
1127
return v
992
1128
}
993
1129
1130
+ func flattenNestedComputeRouterNatRules (v interface {}, d * schema.ResourceData , config * Config ) interface {} {
1131
+ if v == nil {
1132
+ return v
1133
+ }
1134
+ l := v .([]interface {})
1135
+ transformed := schema .NewSet (computeRouterNatRulesHash , []interface {}{})
1136
+ for _ , raw := range l {
1137
+ original := raw .(map [string ]interface {})
1138
+ if len (original ) < 1 {
1139
+ // Do not include empty json objects coming back from the api
1140
+ continue
1141
+ }
1142
+ transformed .Add (map [string ]interface {}{
1143
+ "rule_number" : flattenNestedComputeRouterNatRulesRuleNumber (original ["ruleNumber" ], d , config ),
1144
+ "description" : flattenNestedComputeRouterNatRulesDescription (original ["description" ], d , config ),
1145
+ "match" : flattenNestedComputeRouterNatRulesMatch (original ["match" ], d , config ),
1146
+ "action" : flattenNestedComputeRouterNatRulesAction (original ["action" ], d , config ),
1147
+ })
1148
+ }
1149
+ return transformed
1150
+ }
1151
+ func flattenNestedComputeRouterNatRulesRuleNumber (v interface {}, d * schema.ResourceData , config * Config ) interface {} {
1152
+ // Handles the string fixed64 format
1153
+ if strVal , ok := v .(string ); ok {
1154
+ if intVal , err := stringToFixed64 (strVal ); err == nil {
1155
+ return intVal
1156
+ }
1157
+ }
1158
+
1159
+ // number values are represented as float64
1160
+ if floatVal , ok := v .(float64 ); ok {
1161
+ intVal := int (floatVal )
1162
+ return intVal
1163
+ }
1164
+
1165
+ return v // let terraform core handle it otherwise
1166
+ }
1167
+
1168
+ func flattenNestedComputeRouterNatRulesDescription (v interface {}, d * schema.ResourceData , config * Config ) interface {} {
1169
+ return v
1170
+ }
1171
+
1172
+ func flattenNestedComputeRouterNatRulesMatch (v interface {}, d * schema.ResourceData , config * Config ) interface {} {
1173
+ return v
1174
+ }
1175
+
1176
+ func flattenNestedComputeRouterNatRulesAction (v interface {}, d * schema.ResourceData , config * Config ) interface {} {
1177
+ if v == nil {
1178
+ return nil
1179
+ }
1180
+ original := v .(map [string ]interface {})
1181
+ if len (original ) == 0 {
1182
+ return nil
1183
+ }
1184
+ transformed := make (map [string ]interface {})
1185
+ transformed ["source_nat_active_ips" ] =
1186
+ flattenNestedComputeRouterNatRulesActionSourceNatActiveIps (original ["sourceNatActiveIps" ], d , config )
1187
+ transformed ["source_nat_drain_ips" ] =
1188
+ flattenNestedComputeRouterNatRulesActionSourceNatDrainIps (original ["sourceNatDrainIps" ], d , config )
1189
+ return []interface {}{transformed }
1190
+ }
1191
+ func flattenNestedComputeRouterNatRulesActionSourceNatActiveIps (v interface {}, d * schema.ResourceData , config * Config ) interface {} {
1192
+ if v == nil {
1193
+ return v
1194
+ }
1195
+ return schema .NewSet (computeRouterNatIPsHash , convertStringArrToInterface (convertAndMapStringArr (v .([]interface {}), ConvertSelfLinkToV1 )))
1196
+ }
1197
+
1198
+ func flattenNestedComputeRouterNatRulesActionSourceNatDrainIps (v interface {}, d * schema.ResourceData , config * Config ) interface {} {
1199
+ if v == nil {
1200
+ return v
1201
+ }
1202
+ return schema .NewSet (computeRouterNatIPsHash , convertStringArrToInterface (convertAndMapStringArr (v .([]interface {}), ConvertSelfLinkToV1 )))
1203
+ }
1204
+
994
1205
func flattenNestedComputeRouterNatEnableEndpointIndependentMapping (v interface {}, d * schema.ResourceData , config * Config ) interface {} {
995
1206
return v
996
1207
}
@@ -1158,6 +1369,122 @@ func expandNestedComputeRouterNatLogConfigFilter(v interface{}, d TerraformResou
1158
1369
return v , nil
1159
1370
}
1160
1371
1372
+ func expandNestedComputeRouterNatRules (v interface {}, d TerraformResourceData , config * Config ) (interface {}, error ) {
1373
+ v = v .(* schema.Set ).List ()
1374
+ l := v .([]interface {})
1375
+ req := make ([]interface {}, 0 , len (l ))
1376
+ for _ , raw := range l {
1377
+ if raw == nil {
1378
+ continue
1379
+ }
1380
+ original := raw .(map [string ]interface {})
1381
+ transformed := make (map [string ]interface {})
1382
+
1383
+ transformedRuleNumber , err := expandNestedComputeRouterNatRulesRuleNumber (original ["rule_number" ], d , config )
1384
+ if err != nil {
1385
+ return nil , err
1386
+ } else {
1387
+ transformed ["ruleNumber" ] = transformedRuleNumber
1388
+ }
1389
+
1390
+ transformedDescription , err := expandNestedComputeRouterNatRulesDescription (original ["description" ], d , config )
1391
+ if err != nil {
1392
+ return nil , err
1393
+ } else if val := reflect .ValueOf (transformedDescription ); val .IsValid () && ! isEmptyValue (val ) {
1394
+ transformed ["description" ] = transformedDescription
1395
+ }
1396
+
1397
+ transformedMatch , err := expandNestedComputeRouterNatRulesMatch (original ["match" ], d , config )
1398
+ if err != nil {
1399
+ return nil , err
1400
+ } else if val := reflect .ValueOf (transformedMatch ); val .IsValid () && ! isEmptyValue (val ) {
1401
+ transformed ["match" ] = transformedMatch
1402
+ }
1403
+
1404
+ transformedAction , err := expandNestedComputeRouterNatRulesAction (original ["action" ], d , config )
1405
+ if err != nil {
1406
+ return nil , err
1407
+ } else if val := reflect .ValueOf (transformedAction ); val .IsValid () && ! isEmptyValue (val ) {
1408
+ transformed ["action" ] = transformedAction
1409
+ }
1410
+
1411
+ req = append (req , transformed )
1412
+ }
1413
+ return req , nil
1414
+ }
1415
+
1416
+ func expandNestedComputeRouterNatRulesRuleNumber (v interface {}, d TerraformResourceData , config * Config ) (interface {}, error ) {
1417
+ return v , nil
1418
+ }
1419
+
1420
+ func expandNestedComputeRouterNatRulesDescription (v interface {}, d TerraformResourceData , config * Config ) (interface {}, error ) {
1421
+ return v , nil
1422
+ }
1423
+
1424
+ func expandNestedComputeRouterNatRulesMatch (v interface {}, d TerraformResourceData , config * Config ) (interface {}, error ) {
1425
+ return v , nil
1426
+ }
1427
+
1428
+ func expandNestedComputeRouterNatRulesAction (v interface {}, d TerraformResourceData , config * Config ) (interface {}, error ) {
1429
+ l := v .([]interface {})
1430
+ if len (l ) == 0 || l [0 ] == nil {
1431
+ return nil , nil
1432
+ }
1433
+ raw := l [0 ]
1434
+ original := raw .(map [string ]interface {})
1435
+ transformed := make (map [string ]interface {})
1436
+
1437
+ transformedSourceNatActiveIps , err := expandNestedComputeRouterNatRulesActionSourceNatActiveIps (original ["source_nat_active_ips" ], d , config )
1438
+ if err != nil {
1439
+ return nil , err
1440
+ } else if val := reflect .ValueOf (transformedSourceNatActiveIps ); val .IsValid () && ! isEmptyValue (val ) {
1441
+ transformed ["sourceNatActiveIps" ] = transformedSourceNatActiveIps
1442
+ }
1443
+
1444
+ transformedSourceNatDrainIps , err := expandNestedComputeRouterNatRulesActionSourceNatDrainIps (original ["source_nat_drain_ips" ], d , config )
1445
+ if err != nil {
1446
+ return nil , err
1447
+ } else if val := reflect .ValueOf (transformedSourceNatDrainIps ); val .IsValid () && ! isEmptyValue (val ) {
1448
+ transformed ["sourceNatDrainIps" ] = transformedSourceNatDrainIps
1449
+ }
1450
+
1451
+ return transformed , nil
1452
+ }
1453
+
1454
+ func expandNestedComputeRouterNatRulesActionSourceNatActiveIps (v interface {}, d TerraformResourceData , config * Config ) (interface {}, error ) {
1455
+ v = v .(* schema.Set ).List ()
1456
+ l := v .([]interface {})
1457
+ req := make ([]interface {}, 0 , len (l ))
1458
+ for _ , raw := range l {
1459
+ if raw == nil {
1460
+ return nil , fmt .Errorf ("Invalid value for source_nat_active_ips: nil" )
1461
+ }
1462
+ f , err := parseRegionalFieldValue ("addresses" , raw .(string ), "project" , "region" , "zone" , d , config , true )
1463
+ if err != nil {
1464
+ return nil , fmt .Errorf ("Invalid value for source_nat_active_ips: %s" , err )
1465
+ }
1466
+ req = append (req , f .RelativeLink ())
1467
+ }
1468
+ return req , nil
1469
+ }
1470
+
1471
+ func expandNestedComputeRouterNatRulesActionSourceNatDrainIps (v interface {}, d TerraformResourceData , config * Config ) (interface {}, error ) {
1472
+ v = v .(* schema.Set ).List ()
1473
+ l := v .([]interface {})
1474
+ req := make ([]interface {}, 0 , len (l ))
1475
+ for _ , raw := range l {
1476
+ if raw == nil {
1477
+ return nil , fmt .Errorf ("Invalid value for source_nat_drain_ips: nil" )
1478
+ }
1479
+ f , err := parseRegionalFieldValue ("addresses" , raw .(string ), "project" , "region" , "zone" , d , config , true )
1480
+ if err != nil {
1481
+ return nil , fmt .Errorf ("Invalid value for source_nat_drain_ips: %s" , err )
1482
+ }
1483
+ req = append (req , f .RelativeLink ())
1484
+ }
1485
+ return req , nil
1486
+ }
1487
+
1161
1488
func expandNestedComputeRouterNatEnableEndpointIndependentMapping (v interface {}, d TerraformResourceData , config * Config ) (interface {}, error ) {
1162
1489
return v , nil
1163
1490
}
0 commit comments