From 5005970ea1b1323e9b175608b08ac41a7a1c50cd Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Wed, 20 May 2020 18:32:47 +0000 Subject: [PATCH] Allow update of node pool workload metadata config (#3512) * update of node pool workload metadata * fix forcenew for nested field as well * add set to util for forcenewing field schema, move to utils * add back forcenew for sandbox config Signed-off-by: Modular Magician --- .changelog/3512.txt | 3 + google/node_config.go | 374 ++++++++++++------------- google/resource_container_cluster.go | 17 +- google/resource_container_node_pool.go | 3 +- google/utils.go | 13 + 5 files changed, 220 insertions(+), 190 deletions(-) create mode 100644 .changelog/3512.txt diff --git a/.changelog/3512.txt b/.changelog/3512.txt new file mode 100644 index 00000000000..0a8a69fd5de --- /dev/null +++ b/.changelog/3512.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +container: Added update support for `node_config.workload_metadata_config` to `google_container_node_pool` +``` diff --git a/google/node_config.go b/google/node_config.go index bf2579f4728..652aa419a3a 100644 --- a/google/node_config.go +++ b/google/node_config.go @@ -16,228 +16,228 @@ var defaultOauthScopes = []string{ "https://www.googleapis.com/auth/trace.append", } -var schemaNodeConfig = &schema.Schema{ - Type: schema.TypeList, - Optional: true, - Computed: true, - ForceNew: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "disk_size_gb": { - Type: schema.TypeInt, - Optional: true, - Computed: true, - ForceNew: true, - ValidateFunc: validation.IntAtLeast(10), - }, +func schemaNodeConfig() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Computed: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "disk_size_gb": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.IntAtLeast(10), + }, - "disk_type": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{"pd-standard", "pd-ssd"}, false), - }, + "disk_type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"pd-standard", "pd-ssd"}, false), + }, - "guest_accelerator": { - Type: schema.TypeList, - Optional: true, - Computed: true, - ForceNew: true, - // Legacy config mode allows removing GPU's from an existing resource - // See https://www.terraform.io/docs/configuration/attr-as-blocks.html - ConfigMode: schema.SchemaConfigModeAttr, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "count": { - Type: schema.TypeInt, - Required: true, - ForceNew: true, - }, - "type": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - DiffSuppressFunc: compareSelfLinkOrResourceName, + "guest_accelerator": { + Type: schema.TypeList, + Optional: true, + Computed: true, + ForceNew: true, + // Legacy config mode allows removing GPU's from an existing resource + // See https://www.terraform.io/docs/configuration/attr-as-blocks.html + ConfigMode: schema.SchemaConfigModeAttr, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "count": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + DiffSuppressFunc: compareSelfLinkOrResourceName, + }, }, }, }, - }, - "image_type": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, + "image_type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, - "labels": { - Type: schema.TypeMap, - Optional: true, - // Computed=true because GKE Sandbox will automatically add labels to nodes that can/cannot run sandboxed pods. - Computed: true, - ForceNew: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, + "labels": { + Type: schema.TypeMap, + Optional: true, + // Computed=true because GKE Sandbox will automatically add labels to nodes that can/cannot run sandboxed pods. + Computed: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, - "local_ssd_count": { - Type: schema.TypeInt, - Optional: true, - Computed: true, - ForceNew: true, - ValidateFunc: validation.IntAtLeast(0), - }, + "local_ssd_count": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.IntAtLeast(0), + }, - "machine_type": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, + "machine_type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, - "metadata": { - Type: schema.TypeMap, - Optional: true, - Computed: true, - ForceNew: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, + "metadata": { + Type: schema.TypeMap, + Optional: true, + Computed: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, - "min_cpu_platform": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - }, + "min_cpu_platform": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, - "oauth_scopes": { - Type: schema.TypeSet, - Optional: true, - Computed: true, - ForceNew: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - StateFunc: func(v interface{}) string { - return canonicalizeServiceScope(v.(string)) + "oauth_scopes": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + StateFunc: func(v interface{}) string { + return canonicalizeServiceScope(v.(string)) + }, }, + DiffSuppressFunc: containerClusterAddedScopesSuppress, + Set: stringScopeHashcode, }, - DiffSuppressFunc: containerClusterAddedScopesSuppress, - Set: stringScopeHashcode, - }, - "preemptible": { - Type: schema.TypeBool, - Optional: true, - ForceNew: true, - Default: false, - }, + "preemptible": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + }, - "service_account": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, + "service_account": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, - "tags": { - Type: schema.TypeList, - Optional: true, - ForceNew: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, + "tags": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, - "shielded_instance_config": { - Type: schema.TypeList, - Optional: true, - Computed: true, - ForceNew: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "enable_secure_boot": { - Type: schema.TypeBool, - Optional: true, - ForceNew: true, - Default: false, - }, - "enable_integrity_monitoring": { - Type: schema.TypeBool, - Optional: true, - ForceNew: true, - Default: true, + "shielded_instance_config": { + Type: schema.TypeList, + Optional: true, + Computed: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enable_secure_boot": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + }, + "enable_integrity_monitoring": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Default: true, + }, }, }, }, - }, - "taint": { - Type: schema.TypeList, - Optional: true, - // Computed=true because GKE Sandbox will automatically add taints to nodes that can/cannot run sandboxed pods. - Computed: true, - ForceNew: true, - // Legacy config mode allows explicitly defining an empty taint. - // See https://www.terraform.io/docs/configuration/attr-as-blocks.html - ConfigMode: schema.SchemaConfigModeAttr, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "key": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "value": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "effect": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{"NO_SCHEDULE", "PREFER_NO_SCHEDULE", "NO_EXECUTE"}, false), + "taint": { + Type: schema.TypeList, + Optional: true, + // Computed=true because GKE Sandbox will automatically add taints to nodes that can/cannot run sandboxed pods. + Computed: true, + ForceNew: true, + // Legacy config mode allows explicitly defining an empty taint. + // See https://www.terraform.io/docs/configuration/attr-as-blocks.html + ConfigMode: schema.SchemaConfigModeAttr, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "value": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "effect": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"NO_SCHEDULE", "PREFER_NO_SCHEDULE", "NO_EXECUTE"}, false), + }, }, }, }, - }, - "workload_metadata_config": { - Removed: "This field is in beta. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/guides/provider_versions.html for more details.", - Computed: true, - Type: schema.TypeList, - Optional: true, - ForceNew: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "node_metadata": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{"UNSPECIFIED", "SECURE", "EXPOSE", "GKE_METADATA_SERVER"}, false), + "workload_metadata_config": { + Removed: "This field is in beta. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/guides/provider_versions.html for more details.", + Computed: true, + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "node_metadata": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"UNSPECIFIED", "SECURE", "EXPOSE", "GKE_METADATA_SERVER"}, false), + }, }, }, }, - }, - "sandbox_config": { - Removed: "This field is in beta. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/guides/provider_versions.html for more details.", - Computed: true, - Type: schema.TypeList, - Optional: true, - ForceNew: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "sandbox_type": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{"gvisor"}, false), + "sandbox_config": { + Removed: "This field is in beta. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/guides/provider_versions.html for more details.", + Computed: true, + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "sandbox_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"gvisor"}, false), + }, }, }, }, }, }, - }, + } } func expandNodeConfig(v interface{}) *containerBeta.NodeConfig { diff --git a/google/resource_container_cluster.go b/google/resource_container_cluster.go index 896c3882841..899c9e71a5f 100644 --- a/google/resource_container_cluster.go +++ b/google/resource_container_cluster.go @@ -55,8 +55,23 @@ var ( "addons_config.0.network_policy_config", "addons_config.0.cloudrun_config", } + + forceNewClusterNodeConfigFields = []string{} ) +// This uses the node pool nodeConfig schema but sets +// node-pool-only updatable fields to ForceNew +func clusterSchemaNodeConfig() *schema.Schema { + nodeConfigSch := schemaNodeConfig() + schemaMap := nodeConfigSch.Elem.(*schema.Resource).Schema + for _, k := range forceNewClusterNodeConfigFields { + if sch, ok := schemaMap[k]; ok { + changeFieldSchemaToForceNew(sch) + } + } + return nodeConfigSch +} + func rfc5545RecurrenceDiffSuppress(k, o, n string, d *schema.ResourceData) bool { // This diff gets applied in the cloud console if you specify // "FREQ=DAILY" in your config and add a maintenance exclusion. @@ -559,7 +574,7 @@ func resourceContainerCluster() *schema.Resource { }, }, - "node_config": schemaNodeConfig, + "node_config": clusterSchemaNodeConfig(), "node_pool": { Type: schema.TypeList, diff --git a/google/resource_container_node_pool.go b/google/resource_container_node_pool.go index c0f197ab115..9a1ddbecd3d 100644 --- a/google/resource_container_node_pool.go +++ b/google/resource_container_node_pool.go @@ -181,7 +181,7 @@ var schemaNodePool = map[string]*schema.Schema{ ForceNew: true, }, - "node_config": schemaNodeConfig, + "node_config": schemaNodeConfig(), "node_count": { Type: schema.TypeInt, @@ -711,7 +711,6 @@ func nodePoolUpdate(d *schema.ResourceData, meta interface{}, nodePoolInfo *Node log.Printf("[INFO] Updated image type in Node Pool %s", d.Id()) } - if prefix == "" { d.SetPartial("node_config") } diff --git a/google/utils.go b/google/utils.go index 29f89051028..b7cf61b9a5c 100644 --- a/google/utils.go +++ b/google/utils.go @@ -426,3 +426,16 @@ func migrateStateNoop(v int, is *terraform.InstanceState, meta interface{}) (*te func expandString(v interface{}, d TerraformResourceData, config *Config) (string, error) { return v.(string), nil } + +func changeFieldSchemaToForceNew(sch *schema.Schema) { + sch.ForceNew = true + switch sch.Type { + case schema.TypeList: + case schema.TypeSet: + if nestedR, ok := sch.Elem.(*schema.Resource); ok { + for _, nestedSch := range nestedR.Schema { + changeFieldSchemaToForceNew(nestedSch) + } + } + } +}