Skip to content

Commit 65f0547

Browse files
Feature gap: Add missed enable_flow_logs & state to subnetwork (#13093)
Signed-off-by: Cezary Sobczak <[email protected]> [upstream:7fe6b89e3bcd4c180198da7f5fd7405d968b91e8] Signed-off-by: Modular Magician <[email protected]>
1 parent b8d0f5a commit 65f0547

5 files changed

+288
-5
lines changed

.changelog/13093.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
compute: added `enable_flow_logs` and `state` fields to `google_compute_subnetwork` resource
3+
```

google-beta/services/compute/resource_compute_subnetwork.go

+66-5
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ func sendSecondaryIpRangeIfEmptyDiff(_ context.Context, diff *schema.ResourceDif
8888
return nil
8989
}
9090

91+
// DiffSuppressFunc for fields inside `log_config`.
92+
func subnetworkLogConfigDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
93+
// If the enable_flow_logs is not enabled, we don't need to check for differences.
94+
if enable_flow_logs := d.Get("enable_flow_logs"); !enable_flow_logs.(bool) {
95+
return true
96+
}
97+
98+
return false
99+
}
100+
91101
func ResourceComputeSubnetwork() *schema.Resource {
92102
return &schema.Resource{
93103
Create: resourceComputeSubnetworkCreate,
@@ -150,6 +160,15 @@ via BGP even if their destinations match existing subnet ranges.`,
150160
Description: `An optional description of this resource. Provide this property when
151161
you create the resource. This field can be set only at resource
152162
creation time.`,
163+
},
164+
"enable_flow_logs": {
165+
Type: schema.TypeBool,
166+
Computed: true,
167+
Optional: true,
168+
Description: `Whether to enable flow logging for this subnetwork. If this field is not explicitly set,
169+
it will not appear in get listings. If not set the default behavior is determined by the
170+
org policy, if there is no org policy specified, then it will default to disabled.
171+
This field isn't supported if the subnet purpose field is set to REGIONAL_MANAGED_PROXY.`,
153172
},
154173
"external_ipv6_prefix": {
155174
Type: schema.TypeString,
@@ -192,8 +211,10 @@ or the first time the subnet is updated into IPV4_IPV6 dual stack. If the ipv6_t
192211
cannot enable direct path. Possible values: ["EXTERNAL", "INTERNAL"]`,
193212
},
194213
"log_config": {
195-
Type: schema.TypeList,
196-
Optional: true,
214+
Type: schema.TypeList,
215+
Computed: true,
216+
Optional: true,
217+
DiffSuppressFunc: subnetworkLogConfigDiffSuppress,
197218
Description: `This field denotes the VPC flow logging options for this subnetwork. If
198219
logging is enabled, logs are exported to Cloud Logging. Flow logging
199220
isn't supported if the subnet 'purpose' field is set to subnetwork is
@@ -388,6 +409,14 @@ outside this subnetwork.`,
388409
gets external IPv6 ranges from a public delegated prefix and cannot be used to create NetLb.
389410
* VM_AND_FR: The subnetwork can be used for creating both VM instances and Forwarding Rules. It can also be used to reserve
390411
IPv6 addresses with both VM and FR endpoint types. Such a subnetwork gets its IPv6 range from Google IP Pool directly.`,
412+
},
413+
"state": {
414+
Type: schema.TypeString,
415+
Computed: true,
416+
Description: `'The state of the subnetwork, which can be one of the following values:
417+
READY: Subnetwork is created and ready to use DRAINING: only applicable to subnetworks that have the purpose
418+
set to INTERNAL_HTTPS_LOAD_BALANCER and indicates that connections to the load balancer are being drained.
419+
A subnetwork that is draining cannot be used or modified until it reaches a status of READY'`,
391420
},
392421
"subnetwork_id": {
393422
Type: schema.TypeInt,
@@ -577,6 +606,12 @@ func resourceComputeSubnetworkCreate(d *schema.ResourceData, meta interface{}) e
577606
} else if v, ok := d.GetOkExists("allow_subnet_cidr_routes_overlap"); ok || !reflect.DeepEqual(v, allowSubnetCidrRoutesOverlapProp) {
578607
obj["allowSubnetCidrRoutesOverlap"] = allowSubnetCidrRoutesOverlapProp
579608
}
609+
enableFlowLogsProp, err := expandComputeSubnetworkEnableFlowLogs(d.Get("enable_flow_logs"), d, config)
610+
if err != nil {
611+
return err
612+
} else if v, ok := d.GetOkExists("enable_flow_logs"); ok || !reflect.DeepEqual(v, enableFlowLogsProp) {
613+
obj["enableFlowLogs"] = enableFlowLogsProp
614+
}
580615

581616
url, err := tpgresource.ReplaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/subnetworks")
582617
if err != nil {
@@ -743,6 +778,12 @@ func resourceComputeSubnetworkRead(d *schema.ResourceData, meta interface{}) err
743778
if err := d.Set("allow_subnet_cidr_routes_overlap", flattenComputeSubnetworkAllowSubnetCidrRoutesOverlap(res["allowSubnetCidrRoutesOverlap"], d, config)); err != nil {
744779
return fmt.Errorf("Error reading Subnetwork: %s", err)
745780
}
781+
if err := d.Set("enable_flow_logs", flattenComputeSubnetworkEnableFlowLogs(res["enableFlowLogs"], d, config)); err != nil {
782+
return fmt.Errorf("Error reading Subnetwork: %s", err)
783+
}
784+
if err := d.Set("state", flattenComputeSubnetworkState(res["state"], d, config)); err != nil {
785+
return fmt.Errorf("Error reading Subnetwork: %s", err)
786+
}
746787
if err := d.Set("self_link", tpgresource.ConvertSelfLinkToV1(res["selfLink"].(string))); err != nil {
747788
return fmt.Errorf("Error reading Subnetwork: %s", err)
748789
}
@@ -857,7 +898,7 @@ func resourceComputeSubnetworkUpdate(d *schema.ResourceData, meta interface{}) e
857898
return err
858899
}
859900
}
860-
if d.HasChange("private_ipv6_google_access") || d.HasChange("stack_type") || d.HasChange("ipv6_access_type") || d.HasChange("allow_subnet_cidr_routes_overlap") {
901+
if d.HasChange("private_ipv6_google_access") || d.HasChange("stack_type") || d.HasChange("ipv6_access_type") || d.HasChange("allow_subnet_cidr_routes_overlap") || d.HasChange("enable_flow_logs") {
861902
obj := make(map[string]interface{})
862903

863904
getUrl, err := tpgresource.ReplaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/subnetworks/{{name}}")
@@ -907,6 +948,12 @@ func resourceComputeSubnetworkUpdate(d *schema.ResourceData, meta interface{}) e
907948
} else if v, ok := d.GetOkExists("allow_subnet_cidr_routes_overlap"); ok || !reflect.DeepEqual(v, allowSubnetCidrRoutesOverlapProp) {
908949
obj["allowSubnetCidrRoutesOverlap"] = allowSubnetCidrRoutesOverlapProp
909950
}
951+
enableFlowLogsProp, err := expandComputeSubnetworkEnableFlowLogs(d.Get("enable_flow_logs"), d, config)
952+
if err != nil {
953+
return err
954+
} else if v, ok := d.GetOkExists("enable_flow_logs"); ok || !reflect.DeepEqual(v, enableFlowLogsProp) {
955+
obj["enableFlowLogs"] = enableFlowLogsProp
956+
}
910957

911958
url, err := tpgresource.ReplaceVars(d, config, "{{ComputeBasePath}}projects/{{project}}/regions/{{region}}/subnetworks/{{name}}")
912959
if err != nil {
@@ -1542,6 +1589,14 @@ func flattenComputeSubnetworkAllowSubnetCidrRoutesOverlap(v interface{}, d *sche
15421589
return v
15431590
}
15441591

1592+
func flattenComputeSubnetworkEnableFlowLogs(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
1593+
return v
1594+
}
1595+
1596+
func flattenComputeSubnetworkState(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
1597+
return v
1598+
}
1599+
15451600
func expandComputeSubnetworkDescription(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
15461601
return v, nil
15471602
}
@@ -1648,8 +1703,10 @@ func expandComputeSubnetworkLogConfig(v interface{}, d tpgresource.TerraformReso
16481703
// Subnetworks for regional L7 ILB/XLB or cross-regional L7 ILB do not accept any values for logConfig
16491704
return nil, nil
16501705
}
1651-
// send enable = false to ensure logging is disabled if there is no config
1652-
transformed["enable"] = false
1706+
1707+
// set enable field basing on the enable_flow_logs field. It's needed for case when enable_flow_logs
1708+
// is set to true to avoid conflict with the API. In that case API will return default values for log_config
1709+
transformed["enable"] = d.Get("enable_flow_logs")
16531710
return transformed, nil
16541711
}
16551712

@@ -1688,3 +1745,7 @@ func expandComputeSubnetworkIpCollection(v interface{}, d tpgresource.TerraformR
16881745
func expandComputeSubnetworkAllowSubnetCidrRoutesOverlap(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
16891746
return v, nil
16901747
}
1748+
1749+
func expandComputeSubnetworkEnableFlowLogs(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
1750+
return v, nil
1751+
}

google-beta/services/compute/resource_compute_subnetwork_generated_meta.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ fields:
88
- field: 'allow_subnet_cidr_routes_overlap'
99
- field: 'creation_timestamp'
1010
- field: 'description'
11+
- field: 'enable_flow_logs'
1112
- field: 'external_ipv6_prefix'
1213
- field: 'gateway_address'
1314
- field: 'internal_ipv6_prefix'
@@ -38,5 +39,6 @@ fields:
3839
- field: 'send_secondary_ip_range_if_empty'
3940
provider_only: true
4041
- field: 'stack_type'
42+
- field: 'state'
4143
- field: 'subnetwork_id'
4244
api_field: 'id'

google-beta/services/compute/resource_compute_subnetwork_test.go

+204
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,66 @@ func TestAccComputeSubnetwork_internal_ipv6(t *testing.T) {
493493
})
494494
}
495495

496+
func TestAccComputeSubnetwork_enableFlowLogs(t *testing.T) {
497+
t.Parallel()
498+
var subnetwork compute.Subnetwork
499+
500+
cnName := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
501+
subnetworkName := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10))
502+
503+
acctest.VcrTest(t, resource.TestCase{
504+
PreCheck: func() { acctest.AccTestPreCheck(t) },
505+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
506+
CheckDestroy: testAccCheckComputeSubnetworkDestroyProducer(t),
507+
Steps: []resource.TestStep{
508+
{
509+
Config: testAccComputeSubnetwork_enableFlowLogs(cnName, subnetworkName, true),
510+
Check: resource.ComposeTestCheckFunc(
511+
testAccCheckComputeSubnetworkExists(t, "google_compute_subnetwork.subnetwork", &subnetwork),
512+
testAccCheckComputeSubnetworkIfLogConfigExists(&subnetwork, "google_compute_subnetwork.subnetwork"),
513+
testAccCheckComputeSubnetworkLogConfig(&subnetwork, "log_config.0.enable", "true"),
514+
resource.TestCheckResourceAttr("google_compute_subnetwork.subnetwork", "enable_flow_logs", "true"),
515+
),
516+
},
517+
{
518+
ResourceName: "google_compute_subnetwork.subnetwork",
519+
ImportState: true,
520+
ImportStateVerify: true,
521+
},
522+
{
523+
Config: testAccComputeSubnetwork_enableFlowLogs_with_logConfig(cnName, subnetworkName, true),
524+
Check: resource.ComposeTestCheckFunc(
525+
testAccCheckComputeSubnetworkExists(t, "google_compute_subnetwork.subnetwork", &subnetwork),
526+
resource.TestCheckResourceAttr("google_compute_subnetwork.subnetwork", "enable_flow_logs", "true"),
527+
testAccCheckComputeSubnetworkLogConfig(&subnetwork, "log_config.0.enable", "true"),
528+
testAccCheckComputeSubnetworkLogConfig(&subnetwork, "log_config.0.aggregation_interval", "INTERVAL_1_MIN"),
529+
testAccCheckComputeSubnetworkLogConfig(&subnetwork, "log_config.0.metadata", "INCLUDE_ALL_METADATA"),
530+
),
531+
},
532+
{
533+
ResourceName: "google_compute_subnetwork.subnetwork",
534+
ImportState: true,
535+
ImportStateVerify: true,
536+
},
537+
{
538+
Config: testAccComputeSubnetwork_enableFlowLogs(cnName, subnetworkName, false),
539+
Check: resource.ComposeTestCheckFunc(
540+
testAccCheckComputeSubnetworkExists(t, "google_compute_subnetwork.subnetwork", &subnetwork),
541+
resource.TestCheckResourceAttr("google_compute_subnetwork.subnetwork", "enable_flow_logs", "false"),
542+
testAccCheckComputeSubnetworkLogConfig(&subnetwork, "log_config.0.enable", "false"),
543+
testAccCheckComputeSubnetworkLogConfig(&subnetwork, "log_config.0.aggregation_interval", "INTERVAL_1_MIN"),
544+
testAccCheckComputeSubnetworkLogConfig(&subnetwork, "log_config.0.metadata", "INCLUDE_ALL_METADATA"),
545+
),
546+
},
547+
{
548+
ResourceName: "google_compute_subnetwork.subnetwork",
549+
ImportState: true,
550+
ImportStateVerify: true,
551+
},
552+
},
553+
})
554+
}
555+
496556
func testAccCheckComputeSubnetworkExists(t *testing.T, n string, subnetwork *compute.Subnetwork) resource.TestCheckFunc {
497557
return func(s *terraform.State) error {
498558
rs, ok := s.RootModule().Resources[n]
@@ -553,6 +613,111 @@ func testAccCheckComputeSubnetworkHasNotSecondaryIpRange(subnetwork *compute.Sub
553613
}
554614
}
555615

616+
func testAccCheckComputeSubnetworkIfLogConfigExists(subnetwork *compute.Subnetwork, resourceName string) resource.TestCheckFunc {
617+
return func(s *terraform.State) error {
618+
// Retrieve the resource state using a fixed resource name
619+
rs, ok := s.RootModule().Resources[resourceName]
620+
if !ok {
621+
return fmt.Errorf("resource google_compute_subnetwork.subnetwork not found in state")
622+
}
623+
624+
if rs.Primary.ID == "" {
625+
return fmt.Errorf("resource ID is not set")
626+
}
627+
628+
// Ensure that the log_config exists in the API response.
629+
if subnetwork.LogConfig == nil {
630+
return fmt.Errorf("no log_config exists in subnetwork")
631+
}
632+
633+
stateAttrs := rs.Primary.Attributes
634+
635+
// Check aggregation_interval.
636+
aggInterval, ok := stateAttrs["log_config.0.aggregation_interval"]
637+
if !ok {
638+
return fmt.Errorf("aggregation_interval not found in state")
639+
}
640+
if subnetwork.LogConfig.AggregationInterval != aggInterval {
641+
return fmt.Errorf("aggregation_interval mismatch: expected %s, got %s", aggInterval, subnetwork.LogConfig.AggregationInterval)
642+
}
643+
644+
// Check flow_sampling.
645+
fsState, ok := stateAttrs["log_config.0.flow_sampling"]
646+
if !ok {
647+
return fmt.Errorf("flow_sampling not found in state")
648+
}
649+
actualFS := fmt.Sprintf("%g", subnetwork.LogConfig.FlowSampling)
650+
if actualFS != fsState {
651+
return fmt.Errorf("flow_sampling mismatch: expected %s, got %s", fsState, actualFS)
652+
}
653+
654+
// Check metadata.
655+
metadata, ok := stateAttrs["log_config.0.metadata"]
656+
if !ok {
657+
return fmt.Errorf("metadata not found in state")
658+
}
659+
if subnetwork.LogConfig.Metadata != metadata {
660+
return fmt.Errorf("metadata mismatch: expected %s, got %s", metadata, subnetwork.LogConfig.Metadata)
661+
}
662+
663+
// Optionally, check filter_expr if it exists.
664+
if subnetwork.LogConfig.FilterExpr != "" {
665+
filterExpr, ok := stateAttrs["log_config.0.filter_expr"]
666+
if !ok {
667+
return fmt.Errorf("filter_expr is set in API but not found in state")
668+
}
669+
if subnetwork.LogConfig.FilterExpr != filterExpr {
670+
return fmt.Errorf("filter_expr mismatch: expected %s, got %s", filterExpr, subnetwork.LogConfig.FilterExpr)
671+
}
672+
}
673+
674+
// Optionally, check metadata_fields if present.
675+
if len(subnetwork.LogConfig.MetadataFields) > 0 {
676+
if _, ok := stateAttrs["log_config.0.metadata_fields"]; !ok {
677+
return fmt.Errorf("metadata_fields are set in API but not found in state")
678+
}
679+
}
680+
681+
return nil
682+
}
683+
}
684+
685+
func testAccCheckComputeSubnetworkLogConfig(subnetwork *compute.Subnetwork, key, value string) resource.TestCheckFunc {
686+
return func(s *terraform.State) error {
687+
if subnetwork.LogConfig == nil {
688+
return fmt.Errorf("no log_config found")
689+
}
690+
691+
switch key {
692+
case "log_config.0.enable":
693+
if subnetwork.LogConfig.Enable != (value == "true") {
694+
return fmt.Errorf("expected %s to be '%s', got '%t'", key, value, subnetwork.LogConfig.Enable)
695+
}
696+
case "log_config.0.aggregation_interval":
697+
if subnetwork.LogConfig.AggregationInterval != value {
698+
return fmt.Errorf("expected %s to be '%s', got '%s'", key, value, subnetwork.LogConfig.AggregationInterval)
699+
}
700+
case "log_config.0.metadata":
701+
if subnetwork.LogConfig.Metadata != value {
702+
return fmt.Errorf("expected %s to be '%s', got '%s'", key, value, subnetwork.LogConfig.Metadata)
703+
}
704+
case "log_config.0.flow_sampling":
705+
flowSamplingStr := fmt.Sprintf("%g", subnetwork.LogConfig.FlowSampling)
706+
if flowSamplingStr != value {
707+
return fmt.Errorf("expected %s to be '%s', got '%s'", key, value, flowSamplingStr)
708+
}
709+
case "log_config.0.filterExpr":
710+
if subnetwork.LogConfig.FilterExpr != value {
711+
return fmt.Errorf("expected %s to be '%s', got '%s'", key, value, subnetwork.LogConfig.FilterExpr)
712+
}
713+
default:
714+
return fmt.Errorf("unknown log_config key: %s", key)
715+
}
716+
717+
return nil
718+
}
719+
}
720+
556721
func testAccComputeSubnetwork_basic(cnName, subnetwork1Name, subnetwork2Name, subnetwork3Name string) string {
557722
return fmt.Sprintf(`
558723
resource "google_compute_network" "custom-test" {
@@ -1030,3 +1195,42 @@ resource "google_compute_subnetwork" "subnetwork" {
10301195
}
10311196
`, cnName, subnetworkName)
10321197
}
1198+
1199+
func testAccComputeSubnetwork_enableFlowLogs(cnName, subnetworkName string, enableFlowLogs bool) string {
1200+
return fmt.Sprintf(`
1201+
resource "google_compute_network" "custom-test" {
1202+
name = "%s"
1203+
auto_create_subnetworks = false
1204+
}
1205+
1206+
resource "google_compute_subnetwork" "subnetwork" {
1207+
name = "%s"
1208+
ip_cidr_range = "10.0.0.0/16"
1209+
region = "us-central1"
1210+
network = google_compute_network.custom-test.self_link
1211+
enable_flow_logs = %t
1212+
}
1213+
`, cnName, subnetworkName, enableFlowLogs)
1214+
}
1215+
1216+
func testAccComputeSubnetwork_enableFlowLogs_with_logConfig(cnName, subnetworkName string, enableFlowLogs bool) string {
1217+
return fmt.Sprintf(`
1218+
resource "google_compute_network" "custom-test" {
1219+
name = "%s"
1220+
auto_create_subnetworks = false
1221+
}
1222+
1223+
resource "google_compute_subnetwork" "subnetwork" {
1224+
name = "%s"
1225+
ip_cidr_range = "10.0.0.0/16"
1226+
region = "us-central1"
1227+
network = google_compute_network.custom-test.self_link
1228+
enable_flow_logs = %t
1229+
1230+
log_config {
1231+
aggregation_interval = "INTERVAL_1_MIN"
1232+
metadata = "INCLUDE_ALL_METADATA"
1233+
}
1234+
}
1235+
`, cnName, subnetworkName, enableFlowLogs)
1236+
}

0 commit comments

Comments
 (0)