From 5cd0fba61c1129e56066b98f0ff144369d4e1c28 Mon Sep 17 00:00:00 2001 From: The Magician Date: Wed, 24 Jul 2024 14:39:40 -0700 Subject: [PATCH] move multi-network to GA (#11062) (#18842) [upstream:06b986e64435d596263f0f5bbe0f3121cc51561e] Signed-off-by: Modular Magician --- .changelog/11062.txt | 6 + .../container/resource_container_cluster.go | 11 ++ .../resource_container_cluster_test.go | 81 ++++++++++ .../container/resource_container_node_pool.go | 127 ++++++++++++++- .../resource_container_node_pool_test.go | 145 ++++++++++++++++++ .../docs/r/container_cluster.html.markdown | 2 +- .../docs/r/container_node_pool.html.markdown | 4 +- 7 files changed, 367 insertions(+), 9 deletions(-) create mode 100644 .changelog/11062.txt diff --git a/.changelog/11062.txt b/.changelog/11062.txt new file mode 100644 index 00000000000..a6891de2699 --- /dev/null +++ b/.changelog/11062.txt @@ -0,0 +1,6 @@ +```release-note:enhancement +container: promoted `enable_multi_networking` to GA in the `google_container_cluster` resource +``` +```release-note:enhancement +container: promoted `additional_node_network_configs` and `additional_pod_network_configs` fields to GA in the `google_container_node_pool` resource +``` \ No newline at end of file diff --git a/google/services/container/resource_container_cluster.go b/google/services/container/resource_container_cluster.go index 6e7802c1347..dabc2e6c7b0 100644 --- a/google/services/container/resource_container_cluster.go +++ b/google/services/container/resource_container_cluster.go @@ -1832,6 +1832,13 @@ func ResourceContainerCluster() *schema.Resource { Description: `Whether L4ILB Subsetting is enabled for this cluster.`, Default: false, }, + "enable_multi_networking": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Description: `Whether multi-networking is enabled for this cluster.`, + Default: false, + }, "private_ipv6_google_access": { Type: schema.TypeString, Optional: true, @@ -2119,6 +2126,7 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er EnableL4ilbSubsetting: d.Get("enable_l4_ilb_subsetting").(bool), DnsConfig: expandDnsConfig(d.Get("dns_config")), GatewayApiConfig: expandGatewayApiConfig(d.Get("gateway_api_config")), + EnableMultiNetworking: d.Get("enable_multi_networking").(bool), }, MasterAuth: expandMasterAuth(d.Get("master_auth")), NotificationConfig: expandNotificationConfig(d.Get("notification_config")), @@ -2640,6 +2648,9 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro if err := d.Set("enable_intranode_visibility", cluster.NetworkConfig.EnableIntraNodeVisibility); err != nil { return fmt.Errorf("Error setting enable_intranode_visibility: %s", err) } + if err := d.Set("enable_multi_networking", cluster.NetworkConfig.EnableMultiNetworking); err != nil { + return fmt.Errorf("Error setting enable_multi_networking: %s", err) + } if err := d.Set("private_ipv6_google_access", cluster.NetworkConfig.PrivateIpv6GoogleAccess); err != nil { return fmt.Errorf("Error setting private_ipv6_google_access: %s", err) } diff --git a/google/services/container/resource_container_cluster_test.go b/google/services/container/resource_container_cluster_test.go index 76b56353f04..cc70559c00b 100644 --- a/google/services/container/resource_container_cluster_test.go +++ b/google/services/container/resource_container_cluster_test.go @@ -433,6 +433,29 @@ func TestAccContainerCluster_withILBSubsetting(t *testing.T) { }) } +func TestAccContainerCluster_withMultiNetworking(t *testing.T) { + t.Parallel() + + clusterName := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(t, 10)) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_enableMultiNetworking(clusterName), + }, + { + ResourceName: "google_container_cluster.cluster", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"deletion_protection"}, + }, + }, + }) +} + func TestAccContainerCluster_withMasterAuthConfig_NoCert(t *testing.T) { t.Parallel() @@ -558,6 +581,64 @@ func TestUnitContainerCluster_Rfc3339TimeDiffSuppress(t *testing.T) { } } +func testAccContainerCluster_enableMultiNetworking(clusterName string) string { + return fmt.Sprintf(` +resource "google_compute_network" "container_network" { + name = "%s-nw" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "container_subnetwork" { + name = google_compute_network.container_network.name + network = google_compute_network.container_network.name + ip_cidr_range = "10.0.36.0/24" + region = "us-central1" + private_ip_google_access = true + + secondary_ip_range { + range_name = "pod" + ip_cidr_range = "10.0.0.0/19" + } + + secondary_ip_range { + range_name = "svc" + ip_cidr_range = "10.0.32.0/22" + } + + secondary_ip_range { + range_name = "another-pod" + ip_cidr_range = "10.1.32.0/22" + } + + lifecycle { + ignore_changes = [ + # The auto nodepool creates a secondary range which diffs this resource. + secondary_ip_range, + ] + } +} + +resource "google_container_cluster" "cluster" { + name = "%s" + location = "us-central1" + initial_node_count = 1 + + network = google_compute_network.container_network.name + subnetwork = google_compute_subnetwork.container_subnetwork.name + ip_allocation_policy { + cluster_secondary_range_name = google_compute_subnetwork.container_subnetwork.secondary_ip_range[0].range_name + services_secondary_range_name = google_compute_subnetwork.container_subnetwork.secondary_ip_range[1].range_name + } + release_channel { + channel = "RAPID" + } + enable_multi_networking = true + datapath_provider = "ADVANCED_DATAPATH" + deletion_protection = false +} +`, clusterName, clusterName) +} + func TestAccContainerCluster_withNetworkPolicyEnabled(t *testing.T) { t.Parallel() diff --git a/google/services/container/resource_container_node_pool.go b/google/services/container/resource_container_node_pool.go index bd07346c2bb..cd3a2a4103b 100644 --- a/google/services/container/resource_container_node_pool.go +++ b/google/services/container/resource_container_node_pool.go @@ -391,6 +391,57 @@ var schemaNodePool = map[string]*schema.Schema{ ValidateFunc: verify.ValidateIpCidrRange, Description: `The IP address range for pod IPs in this node pool. Only applicable if create_pod_range is true. Set to blank to have a range chosen with the default size. Set to /netmask (e.g. /14) to have a range chosen with a specific netmask. Set to a CIDR notation (e.g. 10.96.0.0/14) to pick a specific range to use.`, }, + "additional_node_network_configs": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Description: `We specify the additional node networks for this node pool using this list. Each node network corresponds to an additional interface`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "network": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: `Name of the VPC where the additional interface belongs.`, + }, + "subnetwork": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: `Name of the subnetwork where the additional interface belongs.`, + }, + }, + }, + }, + "additional_pod_network_configs": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Description: `We specify the additional pod networks for this node pool using this list. Each pod network corresponds to an additional alias IP range for the node`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "subnetwork": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: `Name of the subnetwork where the additional pod network belongs.`, + }, + "secondary_pod_range": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: `The name of the secondary range on the subnet which provides IP address for this pod range.`, + }, + "max_pods_per_node": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Computed: true, + Description: `The maximum number of pods per node which use this pod network.`, + }, + }, + }, + }, "pod_cidr_overprovision_config": { Type: schema.TypeList, Optional: true, @@ -1164,12 +1215,14 @@ func flattenNodeNetworkConfig(c *container.NodeNetworkConfig, d *schema.Resource result := []map[string]interface{}{} if c != nil { result = append(result, map[string]interface{}{ - "create_pod_range": d.Get(prefix + "network_config.0.create_pod_range"), // API doesn't return this value so we set the old one. Field is ForceNew + Required - "pod_ipv4_cidr_block": c.PodIpv4CidrBlock, - "pod_range": c.PodRange, - "enable_private_nodes": c.EnablePrivateNodes, - "pod_cidr_overprovision_config": flattenPodCidrOverprovisionConfig(c.PodCidrOverprovisionConfig), - "network_performance_config": flattenNodeNetworkPerformanceConfig(c.NetworkPerformanceConfig), + "create_pod_range": d.Get(prefix + "network_config.0.create_pod_range"), // API doesn't return this value so we set the old one. Field is ForceNew + Required + "pod_ipv4_cidr_block": c.PodIpv4CidrBlock, + "pod_range": c.PodRange, + "enable_private_nodes": c.EnablePrivateNodes, + "pod_cidr_overprovision_config": flattenPodCidrOverprovisionConfig(c.PodCidrOverprovisionConfig), + "network_performance_config": flattenNodeNetworkPerformanceConfig(c.NetworkPerformanceConfig), + "additional_node_network_configs": flattenAdditionalNodeNetworkConfig(c.AdditionalNodeNetworkConfigs), + "additional_pod_network_configs": flattenAdditionalPodNetworkConfig(c.AdditionalPodNetworkConfigs), }) } return result @@ -1185,6 +1238,37 @@ func flattenNodeNetworkPerformanceConfig(c *container.NetworkPerformanceConfig) return result } +func flattenAdditionalNodeNetworkConfig(c []*container.AdditionalNodeNetworkConfig) []map[string]interface{} { + if c == nil { + return nil + } + + result := []map[string]interface{}{} + for _, nodeNetworkConfig := range c { + result = append(result, map[string]interface{}{ + "network": nodeNetworkConfig.Network, + "subnetwork": nodeNetworkConfig.Subnetwork, + }) + } + return result +} + +func flattenAdditionalPodNetworkConfig(c []*container.AdditionalPodNetworkConfig) []map[string]interface{} { + if c == nil { + return nil + } + + result := []map[string]interface{}{} + for _, podNetworkConfig := range c { + result = append(result, map[string]interface{}{ + "subnetwork": podNetworkConfig.Subnetwork, + "secondary_pod_range": podNetworkConfig.SecondaryPodRange, + "max_pods_per_node": podNetworkConfig.MaxPodsPerNode.MaxPodsPerNode, + }) + } + return result +} + func expandNodeNetworkConfig(v interface{}) *container.NodeNetworkConfig { networkNodeConfigs := v.([]interface{}) @@ -1213,6 +1297,37 @@ func expandNodeNetworkConfig(v interface{}) *container.NodeNetworkConfig { nnc.ForceSendFields = []string{"EnablePrivateNodes"} } + if v, ok := networkNodeConfig["additional_node_network_configs"]; ok && len(v.([]interface{})) > 0 { + node_network_configs := v.([]interface{}) + nodeNetworkConfigs := make([]*container.AdditionalNodeNetworkConfig, 0, len(node_network_configs)) + for _, raw := range node_network_configs { + data := raw.(map[string]interface{}) + networkConfig := &container.AdditionalNodeNetworkConfig{ + Network: data["network"].(string), + Subnetwork: data["subnetwork"].(string), + } + nodeNetworkConfigs = append(nodeNetworkConfigs, networkConfig) + } + nnc.AdditionalNodeNetworkConfigs = nodeNetworkConfigs + } + + if v, ok := networkNodeConfig["additional_pod_network_configs"]; ok && len(v.([]interface{})) > 0 { + pod_network_configs := v.([]interface{}) + podNetworkConfigs := make([]*container.AdditionalPodNetworkConfig, 0, len(pod_network_configs)) + for _, raw := range pod_network_configs { + data := raw.(map[string]interface{}) + podnetworkConfig := &container.AdditionalPodNetworkConfig{ + Subnetwork: data["subnetwork"].(string), + SecondaryPodRange: data["secondary_pod_range"].(string), + MaxPodsPerNode: &container.MaxPodsConstraint{ + MaxPodsPerNode: int64(data["max_pods_per_node"].(int)), + }, + } + podNetworkConfigs = append(podNetworkConfigs, podnetworkConfig) + } + nnc.AdditionalPodNetworkConfigs = podNetworkConfigs + } + nnc.PodCidrOverprovisionConfig = expandPodCidrOverprovisionConfig(networkNodeConfig["pod_cidr_overprovision_config"]) if v, ok := networkNodeConfig["network_performance_config"]; ok && len(v.([]interface{})) > 0 { diff --git a/google/services/container/resource_container_node_pool_test.go b/google/services/container/resource_container_node_pool_test.go index a79ae3b6f14..95189106d14 100644 --- a/google/services/container/resource_container_node_pool_test.go +++ b/google/services/container/resource_container_node_pool_test.go @@ -671,6 +671,32 @@ func TestAccContainerNodePool_withNetworkConfig(t *testing.T) { }) } +func TestAccContainerNodePool_withMultiNicNetworkConfig(t *testing.T) { + t.Parallel() + + randstr := acctest.RandString(t, 10) + cluster := fmt.Sprintf("tf-test-cluster-%s", randstr) + np := fmt.Sprintf("tf-test-np-%s", randstr) + network := fmt.Sprintf("tf-test-net-%s", randstr) + + acctest.VcrTest(t, resource.TestCase{ + PreCheck: func() { acctest.AccTestPreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + CheckDestroy: testAccCheckContainerClusterDestroyProducer(t), + Steps: []resource.TestStep{ + { + Config: testAccContainerNodePool_withMultiNicNetworkConfig(cluster, np, network), + }, + { + ResourceName: "google_container_cluster.cluster", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"network_config.0.create_pod_range", "deletion_protection"}, + }, + }, + }) +} + func TestAccContainerNodePool_withEnablePrivateNodesToggle(t *testing.T) { t.Parallel() @@ -3206,6 +3232,125 @@ resource "google_container_node_pool" "with_tier1_net" { `, network, cluster, np, np, np, np, netTier) } +func testAccContainerNodePool_withMultiNicNetworkConfig(cluster, np, network string) string { + return fmt.Sprintf(` +resource "google_compute_network" "container_network" { + name = "%s-1" + auto_create_subnetworks = false +} + +resource "google_compute_network" "addn_net_1" { + name = "%s-2" + auto_create_subnetworks = false +} + +resource "google_compute_network" "addn_net_2" { + name = "%s-3" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "container_subnetwork" { + name = "%s-subnet-1" + network = google_compute_network.container_network.name + ip_cidr_range = "10.0.36.0/24" + region = "us-central1" + private_ip_google_access = true + + secondary_ip_range { + range_name = "pod" + ip_cidr_range = "10.0.0.0/19" + } + + secondary_ip_range { + range_name = "svc" + ip_cidr_range = "10.0.32.0/22" + } + + lifecycle { + ignore_changes = [ + # The auto nodepool creates a secondary range which diffs this resource. + secondary_ip_range, + ] + } +} + +resource "google_compute_subnetwork" "subnet1" { + name = "%s-subnet-2" + network = google_compute_network.addn_net_1.name + ip_cidr_range = "10.0.37.0/24" + region = "us-central1" +} + +resource "google_compute_subnetwork" "subnet2" { + name = "%s-subnet-3" + network = google_compute_network.addn_net_2.name + ip_cidr_range = "10.0.38.0/24" + region = "us-central1" + + secondary_ip_range { + range_name = "pod" + ip_cidr_range = "10.0.64.0/19" + } +} + +resource "google_container_cluster" "cluster" { + name = "%s" + location = "us-central1" + initial_node_count = 1 + + network = google_compute_network.container_network.name + subnetwork = google_compute_subnetwork.container_subnetwork.name + ip_allocation_policy { + cluster_secondary_range_name = google_compute_subnetwork.container_subnetwork.secondary_ip_range[0].range_name + services_secondary_range_name = google_compute_subnetwork.container_subnetwork.secondary_ip_range[1].range_name + } + private_cluster_config { + enable_private_nodes = true + master_ipv4_cidr_block = "10.42.0.0/28" + } + release_channel { + channel = "RAPID" + } + enable_multi_networking = true + datapath_provider = "ADVANCED_DATAPATH" + deletion_protection = false +} + +resource "google_container_node_pool" "with_multi_nic" { + name = "%s-mutli-nic" + location = "us-central1" + cluster = google_container_cluster.cluster.name + node_count = 1 + network_config { + create_pod_range = false + enable_private_nodes = true + pod_range = google_compute_subnetwork.container_subnetwork.secondary_ip_range[0].range_name + additional_node_network_configs { + network = google_compute_network.addn_net_1.name + subnetwork = google_compute_subnetwork.subnet1.name + } + additional_node_network_configs { + network = google_compute_network.addn_net_2.name + subnetwork = google_compute_subnetwork.subnet2.name + } + additional_pod_network_configs { + subnetwork = google_compute_subnetwork.subnet2.name + secondary_pod_range = "pod" + max_pods_per_node = 32 + } + } + node_config { + machine_type = "n2-standard-8" + oauth_scopes = [ + "https://www.googleapis.com/auth/cloud-platform", + ] + image_type = "COS_CONTAINERD" + } +} + +`, network, network, network, network, network, network, cluster, np) +} + func makeUpgradeSettings(maxSurge int, maxUnavailable int, strategy string, nodePoolSoakDuration string, batchNodeCount int, batchPercentage float64, batchSoakDuration string) string { if strategy == "BLUE_GREEN" { return fmt.Sprintf(` diff --git a/website/docs/r/container_cluster.html.markdown b/website/docs/r/container_cluster.html.markdown index c53020d209f..9241b1ae744 100644 --- a/website/docs/r/container_cluster.html.markdown +++ b/website/docs/r/container_cluster.html.markdown @@ -356,7 +356,7 @@ subnetwork in which the cluster's instances are launched. * `enable_l4_ilb_subsetting` - (Optional) Whether L4ILB Subsetting is enabled for this cluster. -* `enable_multi_networking` - (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) +* `enable_multi_networking` - (Optional) Whether multi-networking is enabled for this cluster. * `enable_fqdn_network_policy` - (Optional, [Beta](https://terraform.io/docs/providers/google/guides/provider_versions.html)) diff --git a/website/docs/r/container_node_pool.html.markdown b/website/docs/r/container_node_pool.html.markdown index 310b0fbbb15..eaedb6f0f31 100644 --- a/website/docs/r/container_node_pool.html.markdown +++ b/website/docs/r/container_node_pool.html.markdown @@ -221,10 +221,10 @@ cluster. * `pod_range` - (Optional) The ID of the secondary range for pod IPs. If `create_pod_range` is true, this ID is used for the new range. If `create_pod_range` is false, uses an existing secondary range with this ID. -* `additional_node_network_configs` - (Optional, Beta) We specify the additional node networks for this node pool using this list. Each node network corresponds to an additional interface. +* `additional_node_network_configs` - (Optional) We specify the additional node networks for this node pool using this list. Each node network corresponds to an additional interface. Structure is [documented below](#nested_additional_node_network_configs) -* `additional_pod_network_configs` - (Optional, Beta) We specify the additional pod networks for this node pool using this list. Each pod network corresponds to an additional alias IP range for the node. +* `additional_pod_network_configs` - (Optional) We specify the additional pod networks for this node pool using this list. Each pod network corresponds to an additional alias IP range for the node. Structure is [documented below](#nested_additional_pod_network_configs) * `pod_cidr_overprovision_config` - (Optional) Configuration for node-pool level pod cidr overprovision. If not set, the cluster level setting will be inherited. Structure is [documented below](#pod_cidr_overprovision_config).