diff --git a/.changelog/8622.txt b/.changelog/8622.txt
new file mode 100644
index 00000000000..31328a282e7
--- /dev/null
+++ b/.changelog/8622.txt
@@ -0,0 +1,3 @@
+```release-note:enhancement
+container: added `additional_pod_ranges_config` field to `google_container_cluster` resource
+```
diff --git a/google/services/container/resource_container_cluster.go b/google/services/container/resource_container_cluster.go
index 20360c16e9a..86f4df9284e 100644
--- a/google/services/container/resource_container_cluster.go
+++ b/google/services/container/resource_container_cluster.go
@@ -1369,6 +1369,23 @@ func ResourceContainerCluster() *schema.Resource {
},
},
},
+ "additional_pod_ranges_config": {
+ Type: schema.TypeList,
+ MaxItems: 1,
+ Optional: true,
+ Description: `AdditionalPodRangesConfig is the configuration for additional pod secondary ranges supporting the ClusterUpdate message.`,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "pod_range_names": {
+ Type: schema.TypeSet,
+ MinItems: 1,
+ Required: true,
+ Elem: &schema.Schema{Type: schema.TypeString},
+ Description: `Name for pod secondary ipv4 range which has the actual range defined ahead.`,
+ },
+ },
+ },
+ },
},
},
},
@@ -2149,6 +2166,38 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er
}
}
+ if names, ok := d.GetOk("ip_allocation_policy.0.additional_pod_ranges_config.0.pod_range_names"); ok {
+ name := containerClusterFullName(project, location, clusterName)
+ additionalPodRangesConfig := &container.AdditionalPodRangesConfig{
+ PodRangeNames: tpgresource.ConvertStringSet(names.(*schema.Set)),
+ }
+
+ req := &container.UpdateClusterRequest{
+ Update: &container.ClusterUpdate{
+ AdditionalPodRangesConfig: additionalPodRangesConfig,
+ },
+ }
+
+ err = transport_tpg.Retry(transport_tpg.RetryOptions{
+ RetryFunc: func() error {
+ clusterUpdateCall := config.NewContainerClient(userAgent).Projects.Locations.Clusters.Update(name, req)
+ if config.UserProjectOverride {
+ clusterUpdateCall.Header().Add("X-Goog-User-Project", project)
+ }
+ op, err = clusterUpdateCall.Do()
+ return err
+ },
+ })
+ if err != nil {
+ return errwrap.Wrapf("Error updating AdditionalPodRangesConfig: {{err}}", err)
+ }
+
+ err = ContainerOperationWait(config, op, project, location, "updating AdditionalPodRangesConfig", userAgent, d.Timeout(schema.TimeoutCreate))
+ if err != nil {
+ return errwrap.Wrapf("Error while waiting to update AdditionalPodRangesConfig: {{err}}", err)
+ }
+ }
+
if err := resourceContainerClusterRead(d, meta); err != nil {
return err
}
@@ -3038,6 +3087,51 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
}
+ if d.HasChange("ip_allocation_policy.0.additional_pod_ranges_config") {
+ o, n := d.GetChange("ip_allocation_policy.0.additional_pod_ranges_config.0.pod_range_names")
+ old_names := o.(*schema.Set)
+ new_names := n.(*schema.Set)
+
+ // Filter unchanged names.
+ removed_names := old_names.Difference(new_names)
+ added_names := new_names.Difference(old_names)
+
+ var additional_config *container.AdditionalPodRangesConfig
+ var removed_config *container.AdditionalPodRangesConfig
+ if added_names.Len() > 0 {
+ var names []string
+ for _, name := range added_names.List() {
+ names = append(names, name.(string))
+ }
+ additional_config = &container.AdditionalPodRangesConfig{
+ PodRangeNames: names,
+ }
+ }
+ if removed_names.Len() > 0 {
+ var names []string
+ for _, name := range removed_names.List() {
+ names = append(names, name.(string))
+ }
+ removed_config = &container.AdditionalPodRangesConfig{
+ PodRangeNames: names,
+ }
+ }
+ req := &container.UpdateClusterRequest{
+ Update: &container.ClusterUpdate{
+ AdditionalPodRangesConfig: additional_config,
+ RemovedAdditionalPodRangesConfig: removed_config,
+ },
+ }
+
+ updateF := updateFunc(req, "updating AdditionalPodRangesConfig")
+ // Call update serially.
+ if err := transport_tpg.LockedCall(lockKey, updateF); err != nil {
+ return err
+ }
+
+ log.Printf("[INFO] GKE cluster %s's AdditionalPodRangesConfig has been updated", d.Id())
+ }
+
if n, ok := d.GetOk("node_pool.#"); ok {
for i := 0; i < n.(int); i++ {
nodePoolInfo, err := extractNodePoolInformationFromCluster(d, config, clusterName)
@@ -4108,6 +4202,25 @@ func flattenSecurityPostureConfig(spc *container.SecurityPostureConfig) []map[st
return []map[string]interface{}{result}
}
+func flattenAdditionalPodRangesConfig(ipAllocationPolicy *container.IPAllocationPolicy) []map[string]interface{} {
+ if ipAllocationPolicy == nil {
+ return nil
+ }
+ result := make(map[string]interface{})
+
+ if aprc := ipAllocationPolicy.AdditionalPodRangesConfig; aprc != nil {
+ if len(aprc.PodRangeNames) > 0 {
+ result["pod_range_names"] = aprc.PodRangeNames
+ } else {
+ return nil
+ }
+ } else {
+ return nil
+ }
+
+ return []map[string]interface{}{result}
+}
+
func expandNotificationConfig(configured interface{}) *container.NotificationConfig {
l := configured.([]interface{})
if len(l) == 0 || l[0] == nil {
@@ -4878,6 +4991,7 @@ func flattenIPAllocationPolicy(c *container.Cluster, d *schema.ResourceData, con
"services_secondary_range_name": p.ServicesSecondaryRangeName,
"stack_type": p.StackType,
"pod_cidr_overprovision_config": flattenPodCidrOverprovisionConfig(p.PodCidrOverprovisionConfig),
+ "additional_pod_ranges_config": flattenAdditionalPodRangesConfig(c.IpAllocationPolicy),
},
}, nil
}
diff --git a/google/services/container/resource_container_cluster_test.go b/google/services/container/resource_container_cluster_test.go
index 6c448c3c78b..f218b90db76 100644
--- a/google/services/container/resource_container_cluster_test.go
+++ b/google/services/container/resource_container_cluster_test.go
@@ -3267,6 +3267,80 @@ func TestAccContainerCluster_autopilot_net_admin(t *testing.T) {
})
}
+func TestAccContainerCluster_additional_pod_ranges_config_on_create(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_additional_pod_ranges_config(clusterName, 1),
+ },
+ {
+ ResourceName: "google_container_cluster.primary",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func TestAccContainerCluster_additional_pod_ranges_config_on_update(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_additional_pod_ranges_config(clusterName, 0),
+ },
+ {
+ ResourceName: "google_container_cluster.primary",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ {
+ Config: testAccContainerCluster_additional_pod_ranges_config(clusterName, 2),
+ },
+ {
+ ResourceName: "google_container_cluster.primary",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ {
+ Config: testAccContainerCluster_additional_pod_ranges_config(clusterName, 0),
+ },
+ {
+ ResourceName: "google_container_cluster.primary",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ {
+ Config: testAccContainerCluster_additional_pod_ranges_config(clusterName, 1),
+ },
+ {
+ ResourceName: "google_container_cluster.primary",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ {
+ Config: testAccContainerCluster_additional_pod_ranges_config(clusterName, 0),
+ },
+ {
+ ResourceName: "google_container_cluster.primary",
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
func testAccContainerCluster_masterAuthorizedNetworksDisabled(t *testing.T, resource_name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resource_name]
@@ -6858,3 +6932,83 @@ resource "google_container_cluster" "cluster" {
}
}`, policyName, cluster, np)
}
+
+func testAccContainerCluster_additional_pod_ranges_config(name string, nameCount int) string {
+ var podRangeNamesStr string
+ names := []string{"\"gke-autopilot-pods-add\",", "\"gke-autopilot-pods-add-2\""}
+ for i := 0; i < nameCount; i++ {
+ podRangeNamesStr += names[i]
+ }
+ var aprc string
+ if len(podRangeNamesStr) > 0 {
+ aprc = fmt.Sprintf(`
+ additional_pod_ranges_config {
+ pod_range_names = [%s]
+ }
+ `, podRangeNamesStr)
+ }
+
+ return fmt.Sprintf(`
+ resource "google_compute_network" "main" {
+ name = "%s"
+ auto_create_subnetworks = false
+ }
+ resource "google_compute_subnetwork" "main" {
+ ip_cidr_range = "10.10.0.0/16"
+ name = "%s"
+ network = google_compute_network.main.self_link
+ region = "us-central1"
+
+ secondary_ip_range {
+ range_name = "gke-autopilot-services"
+ ip_cidr_range = "10.11.0.0/20"
+ }
+
+ secondary_ip_range {
+ range_name = "gke-autopilot-pods"
+ ip_cidr_range = "10.12.0.0/16"
+ }
+
+ secondary_ip_range {
+ range_name = "gke-autopilot-pods-add"
+ ip_cidr_range = "10.100.0.0/16"
+ }
+ secondary_ip_range {
+ range_name = "gke-autopilot-pods-add-2"
+ ip_cidr_range = "100.0.0.0/16"
+ }
+ }
+ resource "google_container_cluster" "primary" {
+ name = "%s"
+ location = "us-central1"
+
+ enable_autopilot = true
+
+ release_channel {
+ channel = "REGULAR"
+ }
+
+ network = google_compute_network.main.name
+ subnetwork = google_compute_subnetwork.main.name
+
+ private_cluster_config {
+ enable_private_endpoint = false
+ enable_private_nodes = true
+ master_ipv4_cidr_block = "172.16.0.0/28"
+ }
+
+ # supresses permadiff
+ dns_config {
+ cluster_dns = "CLOUD_DNS"
+ cluster_dns_domain = "cluster.local"
+ cluster_dns_scope = "CLUSTER_SCOPE"
+ }
+
+ ip_allocation_policy {
+ cluster_secondary_range_name = "gke-autopilot-pods"
+ services_secondary_range_name = "gke-autopilot-services"
+ %s
+ }
+ }
+ `, name, name, name, aprc)
+}
diff --git a/website/docs/r/container_cluster.html.markdown b/website/docs/r/container_cluster.html.markdown
index d7fd8ca8d93..72888a221da 100644
--- a/website/docs/r/container_cluster.html.markdown
+++ b/website/docs/r/container_cluster.html.markdown
@@ -723,6 +723,16 @@ pick a specific range to use.
Default value is `IPV4`.
Possible values are `IPV4` and `IPV4_IPV6`.
+* `additional_pod_ranges_config` - (Optional) The configuration for additional pod secondary ranges at
+the cluster level. Used for Autopilot clusters and Standard clusters with which control of the
+secondary Pod IP address assignment to node pools isn't needed. Structure is [documented below](#nested_additional_pod_ranges_config).
+
+
+The `additional_pod_ranges_config` block supports:
+
+* `pod_range_names` - (Required) The names of the Pod ranges to add to the cluster.
+
+
The `master_auth` block supports:
* `client_certificate_config` - (Required) Whether client certificate authorization is enabled for this cluster. For example: