diff --git a/google-beta/resource_container_cluster.go b/google-beta/resource_container_cluster.go index d00fad60fb..d4ba9c6108 100644 --- a/google-beta/resource_container_cluster.go +++ b/google-beta/resource_container_cluster.go @@ -78,6 +78,7 @@ func resourceContainerCluster() *schema.Resource { CustomizeDiff: customdiff.All( resourceContainerClusterIpAllocationCustomizeDiff, + resourceContainerClusterIpAllocationSubnetworkCustomizeDiff, resourceNodeConfigEmptyGuestAccelerator, containerClusterPrivateClusterConfigCustomDiff, ), @@ -627,24 +628,30 @@ func resourceContainerCluster() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "use_ip_aliases": { - Type: schema.TypeBool, - Optional: true, - Default: true, - ForceNew: true, + Type: schema.TypeBool, + Deprecated: "This field is being removed in 3.0.0. If set to true, remove it from your config. If false, remove i.", + Optional: true, + Default: true, + ForceNew: true, }, // GKE creates subnetwork automatically "create_subnetwork": { - Type: schema.TypeBool, - Optional: true, - ForceNew: true, + Type: schema.TypeBool, + Deprecated: "This field is being removed in 3.0.0. Define an explicit google_compute_subnetwork and use subnetwork instead.", + Computed: true, + Optional: true, + // Simulated with CustomizeDiff resourceContainerClusterIpAllocationSubnetworkCustomizeDiff + // ForceNew: true, ConflictsWith: ipAllocationRangeFields, }, "subnetwork_name": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, + Type: schema.TypeString, + Deprecated: "This field is being removed in 3.0.0. Define an explicit google_compute_subnetwork and use subnetwork instead.", + Optional: true, + // Simulated with CustomizeDiff resourceContainerClusterIpAllocationSubnetworkCustomizeDiff + // ForceNew: true, ConflictsWith: ipAllocationRangeFields, }, @@ -666,10 +673,12 @@ func resourceContainerCluster() *schema.Resource { DiffSuppressFunc: cidrOrSizeDiffSuppress, }, "node_ipv4_cidr_block": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, + Type: schema.TypeString, + Deprecated: "This field is being removed in 3.0.0. Define an explicit google_compute_subnetwork and use subnetwork instead.", + Optional: true, + Computed: true, + // Simulated with CustomizeDiff resourceContainerClusterIpAllocationSubnetworkCustomizeDiff + // ForceNew: true, ConflictsWith: ipAllocationRangeFields, DiffSuppressFunc: cidrOrSizeDiffSuppress, }, @@ -949,6 +958,47 @@ func resourceContainerClusterIpAllocationCustomizeDiffFunc(diff TerraformResourc return diff.Clear("ip_allocation_policy") } +func resourceContainerClusterIpAllocationSubnetworkCustomizeDiff(diff *schema.ResourceDiff, meta interface{}) error { + o, n := diff.GetChange("ip_allocation_policy") + + oList := o.([]interface{}) + nList := n.([]interface{}) + if len(oList) != 1 || len(nList) != 1 { + // we only care about the case where the field is set in both, so return + // early if that's not the case. If a user is moving from VPC-native to + // routes-based, they'll change a different incompatible field. + return nil + } + + nMap := nList[0].(map[string]interface{}) + oMap := oList[0].(map[string]interface{}) + + // If the values aren't identical and the new value is not empty, forcenew. + if oMap["create_subnetwork"] != nMap["create_subnetwork"] { + log.Printf("[DEBUG] Reading subnet values") + if nMap["create_subnetwork"] != false { + diff.ForceNew("ip_allocation_policy.0.create_subnetwork") + } else if oMap["create_subnetwork"] == true && nMap["create_subnetwork"] == false { + diff.Clear("ip_allocation_policy.0.create_subnetwork") + } + } + + if oMap["subnetwork_name"] != nMap["subnetwork_name"] { + if nMap["subnetwork_name"] != "" { + diff.ForceNew("ip_allocation_policy.0.subnetwork_name") + } + } + + // This value is Computed and will be unknown when compared. + if oMap["node_ipv4_cidr_block"] != nMap["node_ipv4_cidr_block"] { + if nMap["node_ipv4_cidr_block"] != "" { + diff.ForceNew("ip_allocation_policy.0.node_ipv4_cidr_block") + } + } + + return nil +} + func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) diff --git a/google-beta/resource_container_cluster_test.go b/google-beta/resource_container_cluster_test.go index aa9d15fc91..ad08a06d6b 100644 --- a/google-beta/resource_container_cluster_test.go +++ b/google-beta/resource_container_cluster_test.go @@ -1328,6 +1328,15 @@ func TestAccContainerCluster_withIPAllocationPolicy_createSubnetwork(t *testing. ImportState: true, ImportStateVerify: true, }, + { + Config: testAccContainerCluster_withIPAllocationPolicy_createSubnetworkUpdated(cluster), + }, + { + ResourceName: "google_container_cluster.with_ip_allocation_policy", + ImportStateIdPrefix: "us-central1-a/", + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -3036,27 +3045,67 @@ resource "google_container_cluster" "with_ip_allocation_policy" { func testAccContainerCluster_withIPAllocationPolicy_createSubnetwork(cluster string) string { return fmt.Sprintf(` +resource "google_compute_network" "container_network" { + name = "%s-network" + auto_create_subnetworks = false +} + resource "google_container_cluster" "with_ip_allocation_policy" { - name = "%s" - zone = "us-central1-a" + name = "%s" + location = "us-central1" + network = "${google_compute_network.container_network.name}" - initial_node_count = 1 - ip_allocation_policy { - use_ip_aliases = true - create_subnetwork = true - } -}`, cluster) + initial_node_count = 1 + + ip_allocation_policy { + use_ip_aliases = true + create_subnetwork = true + subnetwork_name = "%s-subnet" + cluster_ipv4_cidr_block = "10.0.0.0/16" + services_ipv4_cidr_block = "10.1.0.0/16" + node_ipv4_cidr_block = "10.2.0.0/16" + } +}`, cluster, cluster, cluster) +} + +func testAccContainerCluster_withIPAllocationPolicy_createSubnetworkUpdated(cluster string) string { + return fmt.Sprintf(` +resource "google_compute_network" "container_network" { + name = "%s-network" + auto_create_subnetworks = false +} + +resource "google_container_cluster" "with_ip_allocation_policy" { + name = "%s" + location = "us-central1" + network = "${google_compute_network.container_network.name}" + subnetwork = "%s-subnet" + + initial_node_count = 1 + + ip_allocation_policy { + use_ip_aliases = true + cluster_ipv4_cidr_block = "10.0.0.0/16" + services_ipv4_cidr_block = "10.1.0.0/16" + } +}`, cluster, cluster, cluster) } func testAccContainerCluster_withIPAllocationPolicy_explicitEmpty(cluster string) string { return fmt.Sprintf(` +resource "google_compute_network" "container_network" { + name = "%s-network" + auto_create_subnetworks = false +} + resource "google_container_cluster" "with_ip_allocation_policy" { name = "%s" zone = "us-central1-a" initial_node_count = 1 + ip_allocation_policy = [] -}`, cluster) +}`, cluster, cluster) } func testAccContainerCluster_withResourceUsageExportConfig(clusterName, datasetId string, resourceUsage bool) string { diff --git a/website/docs/version_3_upgrade.html.markdown b/website/docs/version_3_upgrade.html.markdown index 62a5a1e002..e8d6426dcf 100644 --- a/website/docs/version_3_upgrade.html.markdown +++ b/website/docs/version_3_upgrade.html.markdown @@ -93,6 +93,102 @@ provider "google" { ## Resource: `google_container_cluster` +### Automatic subnetwork creation for VPC-native clusters removed + +Automatic creation of subnetworks in GKE has been removed. Now, users of +VPC-native clusters will always need to provide a `google_compute_subnetwork` +resource to use `ip_allocation_policy`. Routes-based clusters are unaffected. + +Representing resources managed by another source in Terraform is painful, and +leads to confusing patterns that often involve unnecessarily recreating user +resources. A number of fields in GKE are dedicated to a feature that allows +users to create a GKE-managed subnetwork. + +This is a great fit for an imperative tool like `gcloud`, but with Terraform +it's relatively easy to specify a subnetwork in config alongside the cluster. +Not only is it more explicit, it allows the subnetwork to be repurposed or +persist through cluster deletion. + +Particularly, Shared VPC was incompatible with `create_subnetwork`, and +`node_ipv4_cidr` was easy to confuse with +`ip_allocation_policy.node_ipv4_cidr_block`. + +#### Detailed changes: + +* `ip_allocation_policy.node_ipv4_cidr_block` removed (This controls the primary range of the created subnetwork) +* `ip_allocation_policy.create_subnetwork`, `ip_allocation_policy.subnetwork_name` removed +* `ip_allocation_policy.use_ip_aliases` removed + * Enablement is now based on `ip_allocation_policy` being defined instead +* Conflict added between `node_ipv4_cidr`, `ip_allocation_policy` + +#### Upgrade instructions + +1. Remove the removed fields from `google_container_cluster` +1. Add a `google_compute_subnetwork` to your config, import it using `terraform import` +1. Reference the subnetwork using the `subnetwork` field on your `google_container_cluster` + +-> Note that subnetworks originaly created as part of `create_subnetwork` will +be deleted alongside the cluster if there are no other users of the subnetwork. +If it's deleted, `terraform apply` will recreate the same subnetwork except that +it won't be connected to any GKE cluster. + +#### Old Config + +```hcl +resource "google_compute_network" "container_network" { + name = "container-network" + auto_create_subnetworks = false +} + +resource "google_container_cluster" "primary" { + name = "my-cluster" + location = "us-central1" + network = "${google_compute_network.container_network.name}" + + initial_node_count = 1 + + ip_allocation_policy { + use_ip_aliases = true + create_subnetwork = true + cluster_ipv4_cidr_block = "10.0.0.0/16" + services_ipv4_cidr_block = "10.1.0.0/16" + node_ipv4_cidr_block = "10.2.0.0/16" + } +} +``` + +#### New Config + +```hcl +resource "google_compute_network" "container_network" { + name = "container-network" + auto_create_subnetworks = false +} + +resource "google_compute_subnetwork" "container_subnetwork" { + name = "container-subnetwork" + description = "auto-created subnetwork for cluster \"my-cluster\"" + ip_cidr_range = "10.2.0.0/16" + region = "us-central1" + network = "${google_compute_network.container_network.self_link}" +} + +resource "google_container_cluster" "primary" { + name = "my-cluster" + location = "us-central1" + network = "${google_compute_network.container_network.name}" + subnetwork = "${google_compute_subnetwork.container_subnetwork.name}" + + initial_node_count = 1 + + ip_allocation_policy { + use_ip_aliases = true + cluster_ipv4_cidr_block = "10.0.0.0/16" + services_ipv4_cidr_block = "10.1.0.0/16" + } +} +``` + ### `logging_service` and `monitoring_service` defaults changed GKE Stackdriver Monitoring (the GKE-specific Stackdriver experience) is now