Skip to content

Commit 2750dc4

Browse files
Support additional_ip_ranges_config (adding multiple subnets to a cluster) (#14521) (#10451)
[upstream:d84e82b1e7cb4fd67fc922a8c0826731040b00f2] Signed-off-by: Modular Magician <[email protected]>
1 parent 0f58537 commit 2750dc4

File tree

8 files changed

+311
-5
lines changed

8 files changed

+311
-5
lines changed

.changelog/14521.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
container: Support additional_ip_ranges_config (adding multiple subnets to a cluster).
3+
```

google-beta/services/container/resource_container_cluster.go

Lines changed: 122 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1822,6 +1822,27 @@ func ResourceContainerCluster() *schema.Resource {
18221822
},
18231823
},
18241824
},
1825+
"additional_ip_ranges_config": {
1826+
Type: schema.TypeList,
1827+
Optional: true,
1828+
Description: `AdditionalIPRangesConfig is the configuration for individual additional subnetworks attached to the cluster`,
1829+
Elem: &schema.Resource{
1830+
Schema: map[string]*schema.Schema{
1831+
"subnetwork": {
1832+
Type: schema.TypeString,
1833+
Required: true,
1834+
DiffSuppressFunc: tpgresource.CompareSelfLinkOrResourceName,
1835+
Description: `Name of the subnetwork. This can be the full path of the subnetwork or just the name.`,
1836+
},
1837+
"pod_ipv4_range_names": {
1838+
Type: schema.TypeList,
1839+
Optional: true,
1840+
Description: `List of secondary ranges names within this subnetwork that can be used for pod IPs.`,
1841+
Elem: &schema.Schema{Type: schema.TypeString},
1842+
},
1843+
},
1844+
},
1845+
},
18251846
},
18261847
},
18271848
},
@@ -2663,7 +2684,7 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er
26632684
}
26642685
}
26652686

2666-
ipAllocationBlock, err := expandIPAllocationPolicy(d.Get("ip_allocation_policy"), d.Get("networking_mode").(string), d.Get("enable_autopilot").(bool))
2687+
ipAllocationBlock, aircs, err := expandIPAllocationPolicy(d.Get("ip_allocation_policy"), d, d.Get("networking_mode").(string), d.Get("enable_autopilot").(bool), config)
26672688
if err != nil {
26682689
return err
26692690
}
@@ -2889,6 +2910,10 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er
28892910

28902911
needUpdateAfterCreate := false
28912912

2913+
if len(aircs) > 0 {
2914+
needUpdateAfterCreate = true
2915+
}
2916+
28922917
// For now PSC based cluster don't support `enable_private_endpoint` on `create`, but only on `update` API call.
28932918
// If cluster is PSC based and enable_private_endpoint is set to true we will ignore it on `create` call and update cluster right after creation.
28942919
enablePrivateEndpointPSCCluster := isEnablePrivateEndpointPSCCluster(cluster)
@@ -3014,6 +3039,13 @@ func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) er
30143039
}
30153040
update.ForceSendFields = append(update.ForceSendFields, "DesiredAddonsConfig.GcePersistentDiskCsiDriverConfig.Enabled")
30163041
}
3042+
3043+
if len(aircs) > 0 {
3044+
update.DesiredAdditionalIpRangesConfig = &container.DesiredAdditionalIPRangesConfig{
3045+
AdditionalIpRangesConfigs: aircs,
3046+
}
3047+
}
3048+
30173049
req := &container.UpdateClusterRequest{Update: update}
30183050

30193051
err = transport_tpg.Retry(transport_tpg.RetryOptions{
@@ -4214,6 +4246,30 @@ func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) er
42144246
log.Printf("[INFO] GKE cluster %s's AdditionalPodRangesConfig has been updated", d.Id())
42154247
}
42164248

4249+
if d.HasChange("ip_allocation_policy.0.additional_ip_ranges_config") {
4250+
c := d.Get("ip_allocation_policy.0.additional_ip_ranges_config")
4251+
aircs, err := expandAdditionalIpRangesConfigs(c, d, config)
4252+
if err != nil {
4253+
return err
4254+
}
4255+
4256+
req := &container.UpdateClusterRequest{
4257+
Update: &container.ClusterUpdate{
4258+
DesiredAdditionalIpRangesConfig: &container.DesiredAdditionalIPRangesConfig{
4259+
AdditionalIpRangesConfigs: aircs,
4260+
},
4261+
},
4262+
}
4263+
4264+
updateF := updateFunc(req, "updating AdditionalIpRangesConfig")
4265+
// Call update serially.
4266+
if err := transport_tpg.LockedCall(lockKey, updateF); err != nil {
4267+
return err
4268+
}
4269+
4270+
log.Printf("[INFO] GKE cluster %s's AdditionalIpRangesConfig has been updated", d.Id())
4271+
}
4272+
42174273
if n, ok := d.GetOk("node_pool.#"); ok {
42184274
for i := 0; i < n.(int); i++ {
42194275
nodePoolInfo, err := extractNodePoolInformationFromCluster(d, config, clusterName)
@@ -5366,23 +5422,66 @@ func expandPodCidrOverprovisionConfig(configured interface{}) *container.PodCIDR
53665422
}
53675423
}
53685424

5369-
func expandIPAllocationPolicy(configured interface{}, networkingMode string, autopilot bool) (*container.IPAllocationPolicy, error) {
5425+
func expandPodIpv4RangeNames(configured interface{}) []string {
5426+
l := configured.([]interface{})
5427+
if len(l) == 0 || l[0] == nil {
5428+
return nil
5429+
}
5430+
var ranges []string
5431+
for _, rawRange := range l {
5432+
ranges = append(ranges, rawRange.(string))
5433+
}
5434+
return ranges
5435+
}
5436+
5437+
func expandAdditionalIpRangesConfigs(configured interface{}, d *schema.ResourceData, c *transport_tpg.Config) ([]*container.AdditionalIPRangesConfig, error) {
5438+
l := configured.([]interface{})
5439+
if len(l) == 0 || l[0] == nil {
5440+
return nil, nil
5441+
}
5442+
var additionalIpRangesConfig []*container.AdditionalIPRangesConfig
5443+
for _, rawConfig := range l {
5444+
config := rawConfig.(map[string]interface{})
5445+
subnetwork, err := tpgresource.ParseSubnetworkFieldValue(config["subnetwork"].(string), d, c)
5446+
if err != nil {
5447+
return nil, err
5448+
}
5449+
additionalIpRangesConfig = append(additionalIpRangesConfig, &container.AdditionalIPRangesConfig{
5450+
Subnetwork: subnetwork.RelativeLink(),
5451+
PodIpv4RangeNames: expandPodIpv4RangeNames(config["pod_ipv4_range_names"]),
5452+
})
5453+
}
5454+
5455+
return additionalIpRangesConfig, nil
5456+
}
5457+
5458+
func expandIPAllocationPolicy(configured interface{}, d *schema.ResourceData, networkingMode string, autopilot bool, c *transport_tpg.Config) (*container.IPAllocationPolicy, []*container.AdditionalIPRangesConfig, error) {
53705459
l := configured.([]interface{})
53715460
if len(l) == 0 || l[0] == nil {
53725461
if networkingMode == "VPC_NATIVE" {
5373-
return nil, nil
5462+
return nil, nil, nil
53745463
}
53755464
return &container.IPAllocationPolicy{
53765465
UseIpAliases: false,
53775466
UseRoutes: true,
53785467
StackType: "IPV4",
53795468
ForceSendFields: []string{"UseIpAliases"},
5380-
}, nil
5469+
}, nil, nil
53815470
}
53825471

53835472
config := l[0].(map[string]interface{})
53845473
stackType := config["stack_type"].(string)
53855474

5475+
// We expand and return additional_ip_ranges_config separately because
5476+
// this field is OUTPUT_ONLY for ClusterCreate RPCs. Instead, during the
5477+
// Terraform Create flow, we follow the CreateCluster (without
5478+
// additional_ip_ranges_config populated) with an UpdateCluster (_with_
5479+
// additional_ip_ranges_config populated).
5480+
additionalIpRangesConfigs, err := expandAdditionalIpRangesConfigs(config["additional_ip_ranges_config"], d, c)
5481+
if err != nil {
5482+
return nil, nil, err
5483+
}
5484+
53865485
return &container.IPAllocationPolicy{
53875486
UseIpAliases: networkingMode == "VPC_NATIVE" || networkingMode == "",
53885487
ClusterIpv4CidrBlock: config["cluster_ipv4_cidr_block"].(string),
@@ -5393,7 +5492,7 @@ func expandIPAllocationPolicy(configured interface{}, networkingMode string, aut
53935492
UseRoutes: networkingMode == "ROUTES",
53945493
StackType: stackType,
53955494
PodCidrOverprovisionConfig: expandPodCidrOverprovisionConfig(config["pod_cidr_overprovision_config"]),
5396-
}, nil
5495+
}, additionalIpRangesConfigs, nil
53975496
}
53985497

53995498
func expandMaintenancePolicy(d *schema.ResourceData, meta interface{}) *container.MaintenancePolicy {
@@ -6980,6 +7079,23 @@ func flattenPodCidrOverprovisionConfig(c *container.PodCIDROverprovisionConfig)
69807079
}
69817080
}
69827081

7082+
func flattenAdditionalIpRangesConfigs(c []*container.AdditionalIPRangesConfig) []map[string]interface{} {
7083+
if len(c) == 0 {
7084+
return nil
7085+
}
7086+
7087+
var outRanges []map[string]interface{}
7088+
for _, rangeConfig := range c {
7089+
outRangeConfig := map[string]interface{}{
7090+
"subnetwork": rangeConfig.Subnetwork,
7091+
"pod_ipv4_range_names": rangeConfig.PodIpv4RangeNames,
7092+
}
7093+
outRanges = append(outRanges, outRangeConfig)
7094+
}
7095+
7096+
return outRanges
7097+
}
7098+
69837099
func flattenIPAllocationPolicy(c *container.Cluster, d *schema.ResourceData, config *transport_tpg.Config) ([]map[string]interface{}, error) {
69847100
// If IP aliasing isn't enabled, none of the values in this block can be set.
69857101
if c == nil || c.IpAllocationPolicy == nil || !c.IpAllocationPolicy.UseIpAliases {
@@ -7010,6 +7126,7 @@ func flattenIPAllocationPolicy(c *container.Cluster, d *schema.ResourceData, con
70107126
"stack_type": p.StackType,
70117127
"pod_cidr_overprovision_config": flattenPodCidrOverprovisionConfig(p.PodCidrOverprovisionConfig),
70127128
"additional_pod_ranges_config": flattenAdditionalPodRangesConfig(c.IpAllocationPolicy),
7129+
"additional_ip_ranges_config": flattenAdditionalIpRangesConfigs(p.AdditionalIpRangesConfigs),
70137130
},
70147131
}, nil
70157132
}

google-beta/services/container/resource_container_cluster_meta.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ fields:
148148
- field: 'identity_service_config.enabled'
149149
- field: 'initial_node_count'
150150
- field: 'ip_allocation_policy.additional_pod_ranges_config.pod_range_names'
151+
- field: 'ip_allocation_policy.additional_ip_ranges_config.subnetwork'
152+
- field: 'ip_allocation_policy.additional_ip_ranges_config.pod_ipv4_range_names'
151153
- field: 'ip_allocation_policy.cluster_ipv4_cidr_block'
152154
- field: 'ip_allocation_policy.cluster_secondary_range_name'
153155
- field: 'ip_allocation_policy.pod_cidr_overprovision_config.disabled'

google-beta/services/container/resource_container_cluster_test.go

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14011,6 +14011,88 @@ resource "google_container_cluster" "primary" {
1401114011
`, name, networkName, subnetworkName, config)
1401214012
}
1401314013

14014+
func TestAccContainerCluster_additional_ip_ranges_config_on_create(t *testing.T) {
14015+
t.Parallel()
14016+
14017+
clusterName := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(t, 10))
14018+
acctest.VcrTest(t, resource.TestCase{
14019+
PreCheck: func() { acctest.AccTestPreCheck(t) },
14020+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
14021+
CheckDestroy: testAccCheckContainerClusterDestroyProducer(t),
14022+
Steps: []resource.TestStep{
14023+
{
14024+
Config: testAccContainerCluster_additional_ip_ranges_config(clusterName, 2, 2),
14025+
},
14026+
{
14027+
ResourceName: "google_container_cluster.primary",
14028+
ImportState: true,
14029+
ImportStateVerify: true,
14030+
ImportStateVerifyIgnore: []string{"deletion_protection"},
14031+
Check: resource.TestCheckResourceAttrSet("google_container_cluster.primary", "node_pool.0.network_config.subnetwork"),
14032+
},
14033+
},
14034+
})
14035+
}
14036+
14037+
func TestAccContainerCluster_additional_ip_ranges_config_on_update(t *testing.T) {
14038+
t.Parallel()
14039+
14040+
clusterName := fmt.Sprintf("tf-test-cluster-%s", acctest.RandString(t, 10))
14041+
acctest.VcrTest(t, resource.TestCase{
14042+
PreCheck: func() { acctest.AccTestPreCheck(t) },
14043+
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
14044+
CheckDestroy: testAccCheckContainerClusterDestroyProducer(t),
14045+
Steps: []resource.TestStep{
14046+
{
14047+
Config: testAccContainerCluster_additional_ip_ranges_config(clusterName, 0, 0),
14048+
},
14049+
{
14050+
ResourceName: "google_container_cluster.primary",
14051+
ImportState: true,
14052+
ImportStateVerify: true,
14053+
ImportStateVerifyIgnore: []string{"deletion_protection"},
14054+
Check: resource.TestCheckResourceAttrSet("google_container_cluster.primary", "node_pool.0.network_config.subnetwork"),
14055+
},
14056+
{
14057+
Config: testAccContainerCluster_additional_ip_ranges_config(clusterName, 1, 1),
14058+
},
14059+
{
14060+
ResourceName: "google_container_cluster.primary",
14061+
ImportState: true,
14062+
ImportStateVerify: true,
14063+
ImportStateVerifyIgnore: []string{"deletion_protection"},
14064+
},
14065+
{
14066+
Config: testAccContainerCluster_additional_ip_ranges_config(clusterName, 0, 0),
14067+
},
14068+
{
14069+
ResourceName: "google_container_cluster.primary",
14070+
ImportState: true,
14071+
ImportStateVerify: true,
14072+
ImportStateVerifyIgnore: []string{"deletion_protection"},
14073+
},
14074+
{
14075+
Config: testAccContainerCluster_additional_ip_ranges_config(clusterName, 2, 2),
14076+
},
14077+
{
14078+
ResourceName: "google_container_cluster.primary",
14079+
ImportState: true,
14080+
ImportStateVerify: true,
14081+
ImportStateVerifyIgnore: []string{"deletion_protection"},
14082+
},
14083+
{
14084+
Config: testAccContainerCluster_additional_ip_ranges_config(clusterName, 0, 0),
14085+
},
14086+
{
14087+
ResourceName: "google_container_cluster.primary",
14088+
ImportState: true,
14089+
ImportStateVerify: true,
14090+
ImportStateVerifyIgnore: []string{"deletion_protection"},
14091+
},
14092+
},
14093+
})
14094+
}
14095+
1401414096
func TestAccContainerCluster_withAnonymousAuthenticationConfig(t *testing.T) {
1401514097
t.Parallel()
1401614098

@@ -14051,6 +14133,89 @@ func TestAccContainerCluster_withAnonymousAuthenticationConfig(t *testing.T) {
1405114133
})
1405214134
}
1405314135

14136+
func testAccContainerCluster_additional_ip_ranges_config(name string, additionalSubnetCount int, secondaryRangeCount int) string {
14137+
var subnetStr string
14138+
var additionalIpRangesStr string
14139+
cumulativeRangeIndex := 0
14140+
for subnetIndex := 0; subnetIndex < additionalSubnetCount; subnetIndex++ {
14141+
var secondaryRangeStr string
14142+
var podIpv4RangeStr string
14143+
for rangeIndex := 0; rangeIndex < secondaryRangeCount; rangeIndex++ {
14144+
secondaryRangeStr += fmt.Sprintf(`
14145+
secondary_ip_range {
14146+
range_name = "range-%d"
14147+
ip_cidr_range = "10.0.%d.0/24"
14148+
}
14149+
`, cumulativeRangeIndex, cumulativeRangeIndex)
14150+
14151+
podIpv4RangeStr += fmt.Sprintf("google_compute_subnetwork.extra_%d.secondary_ip_range[%d].range_name", subnetIndex, rangeIndex)
14152+
if rangeIndex != secondaryRangeCount-1 {
14153+
podIpv4RangeStr += ", "
14154+
}
14155+
cumulativeRangeIndex++
14156+
}
14157+
14158+
subnetStr += fmt.Sprintf(`
14159+
resource "google_compute_subnetwork" "extra_%d" {
14160+
ip_cidr_range = "10.1.%d.0/24"
14161+
name = "tf-test-subnet-%d"
14162+
network = google_compute_network.main.self_link
14163+
region = "us-central1"
14164+
%s
14165+
}
14166+
`, subnetIndex, subnetIndex, subnetIndex, secondaryRangeStr)
14167+
14168+
additionalIpRangesStr += fmt.Sprintf(`
14169+
additional_ip_ranges_config {
14170+
subnetwork = google_compute_subnetwork.extra_%d.id
14171+
pod_ipv4_range_names = [%s]
14172+
}
14173+
`, subnetIndex, podIpv4RangeStr)
14174+
}
14175+
14176+
return fmt.Sprintf(`
14177+
resource "google_compute_network" "main" {
14178+
name = "%s"
14179+
auto_create_subnetworks = false
14180+
}
14181+
14182+
resource "google_compute_subnetwork" "main" {
14183+
ip_cidr_range = "10.2.0.0/24"
14184+
name = "main"
14185+
network = google_compute_network.main.self_link
14186+
region = "us-central1"
14187+
14188+
secondary_ip_range {
14189+
range_name = "services"
14190+
ip_cidr_range = "10.3.0.0/16"
14191+
}
14192+
14193+
secondary_ip_range {
14194+
range_name = "pods"
14195+
ip_cidr_range = "10.4.0.0/16"
14196+
}
14197+
}
14198+
14199+
%s
14200+
14201+
resource "google_container_cluster" "primary" {
14202+
name = "%s"
14203+
location = "us-central1-a"
14204+
network = google_compute_network.main.name
14205+
subnetwork = google_compute_subnetwork.main.name
14206+
initial_node_count = 1
14207+
14208+
ip_allocation_policy {
14209+
cluster_secondary_range_name = "pods"
14210+
services_secondary_range_name = "services"
14211+
%s
14212+
}
14213+
14214+
deletion_protection = false
14215+
}
14216+
`, name, subnetStr, name, additionalIpRangesStr)
14217+
}
14218+
1405414219
func testAccContainerCluster_withAnonymousAuthenticationConfig(name, networkName, subnetworkName string, mode string) string {
1405514220
return fmt.Sprintf(`
1405614221
resource "google_container_cluster" "primary" {

0 commit comments

Comments
 (0)