From fe8ceed1d5cd32a062c7c38a6a87d1fe74cced08 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Thu, 15 Mar 2018 13:28:30 -0700 Subject: [PATCH 1/3] Add support for GKE metadata concealment (#1199) * add support for gke metadata concealment * docs for metadata concealment --- google/node_config.go | 57 +++++++++++++++---- google/resource_container_cluster.go | 9 ++- google/resource_container_cluster_test.go | 48 ++++++++++++++++ google/resource_container_node_pool.go | 4 +- google/resource_container_node_pool_test.go | 52 +++++++++++++++++ .../docs/r/container_cluster.html.markdown | 11 ++++ 6 files changed, 167 insertions(+), 14 deletions(-) diff --git a/google/node_config.go b/google/node_config.go index ae0e3817..a7ac48de 100644 --- a/google/node_config.go +++ b/google/node_config.go @@ -130,6 +130,23 @@ var schemaNodeConfig = &schema.Schema{ ForceNew: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + + "workload_metadata_config": { + 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"}, false), + }, + }, + }, + }, }, }, } @@ -223,6 +240,13 @@ func expandNodeConfig(v interface{}) *containerBeta.NodeConfig { nc.MinCpuPlatform = v.(string) } + if v, ok := nodeConfig["workload_metadata_config"]; ok { + conf := v.([]interface{})[0].(map[string]interface{}) + nc.WorkloadMetadataConfig = &containerBeta.WorkloadMetadataConfig{ + NodeMetadata: conf["node_metadata"].(string), + } + } + return nc } @@ -234,17 +258,18 @@ func flattenNodeConfig(c *containerBeta.NodeConfig) []map[string]interface{} { } config = append(config, map[string]interface{}{ - "machine_type": c.MachineType, - "disk_size_gb": c.DiskSizeGb, - "guest_accelerator": c.Accelerators, - "local_ssd_count": c.LocalSsdCount, - "service_account": c.ServiceAccount, - "metadata": c.Metadata, - "image_type": c.ImageType, - "labels": c.Labels, - "tags": c.Tags, - "preemptible": c.Preemptible, - "min_cpu_platform": c.MinCpuPlatform, + "machine_type": c.MachineType, + "disk_size_gb": c.DiskSizeGb, + "guest_accelerator": c.Accelerators, + "local_ssd_count": c.LocalSsdCount, + "service_account": c.ServiceAccount, + "metadata": c.Metadata, + "image_type": c.ImageType, + "labels": c.Labels, + "tags": c.Tags, + "preemptible": c.Preemptible, + "min_cpu_platform": c.MinCpuPlatform, + "workload_metadata_config": flattenWorkloadMetadataConfig(c.WorkloadMetadataConfig), }) if len(c.OauthScopes) > 0 { @@ -253,3 +278,13 @@ func flattenNodeConfig(c *containerBeta.NodeConfig) []map[string]interface{} { return config } + +func flattenWorkloadMetadataConfig(c *containerBeta.WorkloadMetadataConfig) []map[string]interface{} { + result := []map[string]interface{}{} + if c != nil { + result = append(result, map[string]interface{}{ + "node_metadata": c.NodeMetadata, + }) + } + return result +} diff --git a/google/resource_container_cluster.go b/google/resource_container_cluster.go index 84ee93e6..41110fbc 100644 --- a/google/resource_container_cluster.go +++ b/google/resource_container_cluster.go @@ -18,7 +18,10 @@ import ( var ( instanceGroupManagerURL = regexp.MustCompile("^https://www.googleapis.com/compute/v1/projects/([a-z][a-z0-9-]{5}(?:[-a-z0-9]{0,23}[a-z0-9])?)/zones/([a-z0-9-]*)/instanceGroupManagers/([^/]*)") ContainerClusterBaseApiVersion = v1 - ContainerClusterVersionedFeatures = []Feature{Feature{Version: v1beta1, Item: "pod_security_policy_config"}} + ContainerClusterVersionedFeatures = []Feature{ + {Version: v1beta1, Item: "pod_security_policy_config"}, + {Version: v1beta1, Item: "node_config.*.workload_metadata_config"}, + } networkConfig = &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -697,7 +700,9 @@ func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) erro d.Set("monitoring_service", cluster.MonitoringService) d.Set("network", cluster.Network) d.Set("subnetwork", cluster.Subnetwork) - d.Set("node_config", flattenNodeConfig(cluster.NodeConfig)) + if err := d.Set("node_config", flattenNodeConfig(cluster.NodeConfig)); err != nil { + return err + } d.Set("zone", zoneName) d.Set("project", project) if cluster.AddonsConfig != nil { diff --git a/google/resource_container_cluster_test.go b/google/resource_container_cluster_test.go index 24b86600..4d640b05 100644 --- a/google/resource_container_cluster_test.go +++ b/google/resource_container_cluster_test.go @@ -369,6 +369,27 @@ func TestAccContainerCluster_withNodeConfigScopeAlias(t *testing.T) { }) } +func TestAccContainerCluster_withWorkloadMetadataConfig(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContainerCluster_withWorkloadMetadataConfig(), + Check: resource.ComposeTestCheckFunc( + testAccCheckContainerCluster( + "google_container_cluster.with_workload_metadata_config"), + resource.TestCheckResourceAttr("google_container_cluster.with_workload_metadata_config", + "node_config.0.workload_metadata_config.0.node_metadata", "SECURE"), + ), + }, + }, + }) +} + func TestAccContainerCluster_network(t *testing.T) { t.Parallel() @@ -1325,6 +1346,33 @@ resource "google_container_cluster" "with_node_config_scope_alias" { }`, acctest.RandString(10)) } +func testAccContainerCluster_withWorkloadMetadataConfig() string { + return fmt.Sprintf(` +data "google_container_engine_versions" "central1a" { + zone = "us-central1-a" +} + +resource "google_container_cluster" "with_workload_metadata_config" { + name = "cluster-test-%s" + zone = "us-central1-a" + initial_node_count = 1 + min_master_version = "${data.google_container_engine_versions.central1a.latest_master_version}" + node_version = "${data.google_container_engine_versions.central1a.latest_node_version}" + + node_config { + oauth_scopes = [ + "https://www.googleapis.com/auth/logging.write", + "https://www.googleapis.com/auth/monitoring" + ] + + workload_metadata_config { + node_metadata = "SECURE" + } + } +} +`, acctest.RandString(10)) +} + func testAccContainerCluster_networkRef() string { return fmt.Sprintf(` resource "google_compute_network" "container_network" { diff --git a/google/resource_container_node_pool.go b/google/resource_container_node_pool.go index aa733d92..dc819079 100644 --- a/google/resource_container_node_pool.go +++ b/google/resource_container_node_pool.go @@ -15,7 +15,9 @@ import ( var ( ContainerNodePoolBaseApiVersion = v1 - ContainerNodePoolVersionedFeatures = []Feature{} + ContainerNodePoolVersionedFeatures = []Feature{ + {Version: v1beta1, Item: "node_config.*.workload_metadata_config"}, + } ) func resourceContainerNodePool() *schema.Resource { diff --git a/google/resource_container_node_pool_test.go b/google/resource_container_node_pool_test.go index 2e41827b..37c28ba1 100644 --- a/google/resource_container_node_pool_test.go +++ b/google/resource_container_node_pool_test.go @@ -92,6 +92,26 @@ func TestAccContainerNodePool_withNodeConfig(t *testing.T) { }) } +func TestAccContainerNodePool_withWorkloadMetadataConfig(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckContainerClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccContainerNodePool_withWorkloadMetadataConfig(), + Check: resource.ComposeTestCheckFunc( + testAccCheckContainerNodePoolMatches("google_container_node_pool.with_workload_metadata_config"), + resource.TestCheckResourceAttr("google_container_node_pool.with_workload_metadata_config", + "node_config.0.workload_metadata_config.0.node_metadata", "SECURE"), + ), + }, + }, + }) +} + func TestAccContainerNodePool_withGPU(t *testing.T) { t.Parallel() @@ -617,6 +637,38 @@ resource "google_container_node_pool" "np_with_node_config" { }`, acctest.RandString(10), acctest.RandString(10)) } +func testAccContainerNodePool_withWorkloadMetadataConfig() string { + return fmt.Sprintf(` +data "google_container_engine_versions" "central1a" { + zone = "us-central1-a" +} + +resource "google_container_cluster" "cluster" { + name = "tf-cluster-nodepool-test-%s" + zone = "us-central1-a" + initial_node_count = 1 + min_master_version = "${data.google_container_engine_versions.central1a.latest_master_version}" +} + +resource "google_container_node_pool" "with_workload_metadata_config" { + name = "tf-nodepool-test-%s" + zone = "us-central1-a" + cluster = "${google_container_cluster.cluster.name}" + initial_node_count = 1 + node_config { + oauth_scopes = [ + "https://www.googleapis.com/auth/logging.write", + "https://www.googleapis.com/auth/monitoring" + ] + + workload_metadata_config { + node_metadata = "SECURE" + } + } +} +`, acctest.RandString(10), acctest.RandString(10)) +} + func testAccContainerNodePool_withGPU() string { return fmt.Sprintf(` resource "google_container_cluster" "cluster" { diff --git a/website/docs/r/container_cluster.html.markdown b/website/docs/r/container_cluster.html.markdown index ee67d1c7..ee60bcff 100644 --- a/website/docs/r/container_cluster.html.markdown +++ b/website/docs/r/container_cluster.html.markdown @@ -290,6 +290,9 @@ The `node_config` block supports: * `tags` - (Optional) The list of instance tags applied to all nodes. Tags are used to identify valid sources or targets for network firewalls. +* `workload_metadata_config` - (Optional) Metadata configuration to expose to workloads on the node pool. + Structure is documented below. + The `guest_accelerator` block supports: * `type` (Required) - The accelerator type resource to expose to this instance. E.g. `nvidia-tesla-k80`. @@ -301,6 +304,14 @@ The `pod_security_policy_config` block supports: * `enabled` (Required) - Enable the PodSecurityPolicy controller for this cluster. If enabled, pods must be valid under a PodSecurityPolicy to be created. +The `workload_metadata_config` block supports: + +* `node_metadata` (Required) How to expose the node metadata to the workload running on the node. + Accepted values are: + * UNSPECIFIED: Not Set + * SECURE: Prevent workloads not in hostNetwork from accessing certain VM metadata, specifically kube-env, which contains Kubelet credentials, and the instance identity token. See [Metadata Concealment](https://cloud.google.com/kubernetes-engine/docs/how-to/metadata-proxy) documentation. + * EXPOSE: Expose all VM metadata to pods. + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are From e1f9710bb28f9474ed9ec5b0a2e978e3d171a6f7 Mon Sep 17 00:00:00 2001 From: Dana Hoffman Date: Thu, 15 Mar 2018 13:28:57 -0700 Subject: [PATCH 2/3] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19d2e36e..e14b3c67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ FEATURES: IMPROVEMENTS: * container: un-deprecate `google_container_node_pool.initial_node_count` [GH-1176] * container: Add support for pod security policy [GH-1192] +* container: Add support for GKE metadata concealment [GH-1199] * compute: Rolling update support for instance group manager [GH-1137] * all: IAM resources now wait for propagation before reporting created. [GH-1197] From c223521d31b577e98414d4dccbc57b0c81f1c569 Mon Sep 17 00:00:00 2001 From: Paddy Carver Date: Thu, 15 Mar 2018 13:30:01 -0700 Subject: [PATCH 3/3] Remove commented out code. We don't really need dead code, and this is breaking our `make fmt` test because it gets indented differently in go 1.10 from in go 1.9. --- google/resource_dataproc_cluster.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/google/resource_dataproc_cluster.go b/google/resource_dataproc_cluster.go index c29cf060..16374639 100644 --- a/google/resource_dataproc_cluster.go +++ b/google/resource_dataproc_cluster.go @@ -801,9 +801,7 @@ func flattenPreemptibleInstanceGroupConfig(d *schema.ResourceData, icg *dataproc func flattenInstanceGroupConfig(d *schema.ResourceData, icg *dataproc.InstanceGroupConfig) []map[string]interface{} { disk := map[string]interface{}{} - data := map[string]interface{}{ - //"instance_names": []string{}, - } + data := map[string]interface{}{} if icg != nil { data["num_instances"] = icg.NumInstances