From 06d1d57d4434398e7431198799a88795572d0811 Mon Sep 17 00:00:00 2001 From: Gavin Williams Date: Thu, 8 Jan 2026 14:18:45 +0000 Subject: [PATCH 1/3] feat: Add support for User namespaces This commit updates the pod related resources in order to add support for the User Namepaces feature which was enabled by default in `1.33` [1]. As part of this: * Updated `PodSpec` to add `host_users` field, which is optional and defaults to `true` * Update corresponding functions to get and set `host_users` field. * Added tests to relevant resources Fixes: #2818 [1] https://kubernetes.io/blog/2025/04/25/userns-enabled-by-default/ --- .../resource_kubernetes_daemon_set_v1.go | 2 +- .../resource_kubernetes_daemon_set_v1_test.go | 60 ++++++++++++++++++ .../resource_kubernetes_deployment_v1_test.go | 61 ++++++++++++++++++ kubernetes/resource_kubernetes_pod_v1_test.go | 46 ++++++++++++++ ...esource_kubernetes_stateful_set_v1_test.go | 62 +++++++++++++++++++ kubernetes/schema_pod_spec.go | 10 ++- kubernetes/structures_pod.go | 6 ++ 7 files changed, 244 insertions(+), 3 deletions(-) diff --git a/kubernetes/resource_kubernetes_daemon_set_v1.go b/kubernetes/resource_kubernetes_daemon_set_v1.go index b115cd853a..4b61985f6d 100644 --- a/kubernetes/resource_kubernetes_daemon_set_v1.go +++ b/kubernetes/resource_kubernetes_daemon_set_v1.go @@ -140,7 +140,7 @@ func resourceKubernetesDaemonSetSchemaV1() map[string]*schema.Schema { }, "wait_for_rollout": { Type: schema.TypeBool, - Description: "Wait for the rollout of the deployment to complete. Defaults to true.", + Description: "Wait for the rollout of the daemon set to complete. Defaults to true.", Default: true, Optional: true, }, diff --git a/kubernetes/resource_kubernetes_daemon_set_v1_test.go b/kubernetes/resource_kubernetes_daemon_set_v1_test.go index 2fbad42b22..431c722b8b 100644 --- a/kubernetes/resource_kubernetes_daemon_set_v1_test.go +++ b/kubernetes/resource_kubernetes_daemon_set_v1_test.go @@ -218,6 +218,35 @@ func TestAccKubernetesDaemonSetV1_initContainer(t *testing.T) { }) } +func TestAccKubernetesDaemonSetV1_host_users(t *testing.T) { + name := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "kubernetes_daemon_set_v1.test" + imageName := busyboxImage + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + skipIfClusterVersionLessThan(t, "1.25.0") // User namespaces is beta in 1.25 + }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckKubernetesDaemonSetV1Destroy, + Steps: []resource.TestStep{ + { + Config: testAccKubernetesDaemonSetV1ConfigHostUsers(name, imageName, true), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "spec.0.template.0.spec.0.host_users", "true"), + ), + }, + { + Config: testAccKubernetesDaemonSetV1ConfigHostUsers(name, imageName, false), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "spec.0.template.0.spec.0.host_users", "false"), + ), + }, + }, + }) +} + func TestAccKubernetesDaemonSetV1_noTopLevelLabels(t *testing.T) { var conf appsv1.DaemonSet resourceName := "kubernetes_daemon_set_v1.test" @@ -1394,3 +1423,34 @@ func testAccKubernetesDaemonSetV1ConfigMinimalWithTemplateNamespace(name, imageN } `, name, imageName) } + +func testAccKubernetesDaemonSetV1ConfigHostUsers(name, image string, hostUsers bool) string { + return fmt.Sprintf(` +resource "kubernetes_daemon_set_v1" "test" { + metadata { + name = "%s" + } + spec { + selector { + match_labels = { + app = "tf-acc-test" + } + } + template { + metadata { + labels = { + app = "tf-acc-test" + } + } + spec { + host_users = %t + container { + image = "%s" + name = "test" + } + } + } + } +} +`, name, hostUsers, image) +} diff --git a/kubernetes/resource_kubernetes_deployment_v1_test.go b/kubernetes/resource_kubernetes_deployment_v1_test.go index ebbb9f422e..06ec590e86 100644 --- a/kubernetes/resource_kubernetes_deployment_v1_test.go +++ b/kubernetes/resource_kubernetes_deployment_v1_test.go @@ -692,6 +692,35 @@ func TestAccKubernetesDeploymentV1_with_container_security_context_seccomp_local }) } +func TestAccKubernetesDeploymentV1_host_users(t *testing.T) { + name := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "kubernetes_deployment_v1.test" + imageName := busyboxImage + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + skipIfClusterVersionLessThan(t, "1.25.0") // User namespaces is beta in 1.25 + }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckKubernetesDeploymentV1Destroy, + Steps: []resource.TestStep{ + { + Config: testAccKubernetesDeploymentV1ConfigHostUsers(name, imageName, true), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "spec.0.template.0.spec.0.host_users", "true"), + ), + }, + { + Config: testAccKubernetesDeploymentV1ConfigHostUsers(name, imageName, false), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "spec.0.template.0.spec.0.host_users", "false"), + ), + }, + }, + }) +} + func TestAccKubernetesDeploymentV1_with_volume_mount(t *testing.T) { var conf appsv1.Deployment @@ -3247,3 +3276,35 @@ func testAccKubernetesDeploymentV1ConfigWithResourceFieldSelector(rcName, imageN } `, rcName, imageName, resourceName, divisor) } + +func testAccKubernetesDeploymentV1ConfigHostUsers(name, image string, hostUsers bool) string { + return fmt.Sprintf(` +resource "kubernetes_deployment_v1" "test" { + metadata { + name = "%s" + } + spec { + replicas = 1 + selector { + match_labels = { + app = "tf-acc-test" + } + } + template { + metadata { + labels = { + app = "tf-acc-test" + } + } + spec { + host_users = %t + container { + image = "%s" + name = "test" + } + } + } + } +} +`, name, hostUsers, image) +} diff --git a/kubernetes/resource_kubernetes_pod_v1_test.go b/kubernetes/resource_kubernetes_pod_v1_test.go index 4cfa9e0b46..096875b4fb 100644 --- a/kubernetes/resource_kubernetes_pod_v1_test.go +++ b/kubernetes/resource_kubernetes_pod_v1_test.go @@ -753,6 +753,35 @@ func TestAccKubernetesPodV1_with_container_security_context(t *testing.T) { }) } +func TestAccKubernetesPodV1_host_users(t *testing.T) { + name := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "kubernetes_pod_v1.test" + imageName := busyboxImage + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + skipIfClusterVersionLessThan(t, "1.25.0") // User namespaces is beta in 1.25 + }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckKubernetesPodV1Destroy, + Steps: []resource.TestStep{ + { + Config: testAccKubernetesPodV1ConfigHostUsers(name, imageName, true), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "spec.0.host_users", "true"), + ), + }, + { + Config: testAccKubernetesPodV1ConfigHostUsers(name, imageName, false), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "spec.0.host_users", "false"), + ), + }, + }, + }) +} + func TestAccKubernetesPodV1_with_volume_mount(t *testing.T) { var conf api.Pod @@ -3672,3 +3701,20 @@ resource "kubernetes_pod_v1" "test" { } `, secretName, podName, imageName) } + +func testAccKubernetesPodV1ConfigHostUsers(name, image string, hostUsers bool) string { + return fmt.Sprintf(` +resource "kubernetes_pod_v1" "test" { + metadata { + name = "%s" + } + spec { + host_users = %t + container { + image = "%s" + name = "test" + } + } +} +`, name, hostUsers, image) +} diff --git a/kubernetes/resource_kubernetes_stateful_set_v1_test.go b/kubernetes/resource_kubernetes_stateful_set_v1_test.go index d56626bead..9b6a01de47 100644 --- a/kubernetes/resource_kubernetes_stateful_set_v1_test.go +++ b/kubernetes/resource_kubernetes_stateful_set_v1_test.go @@ -336,6 +336,35 @@ func TestAccKubernetesStatefulSetV1_Update(t *testing.T) { }) } +func TestAccKubernetesStatefulSetV1_host_users(t *testing.T) { + name := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "kubernetes_stateful_set_v1.test" + imageName := busyboxImage + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + skipIfClusterVersionLessThan(t, "1.25.0") // User namespaces is beta in 1.25 + }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckKubernetesStatefulSetV1Destroy, + Steps: []resource.TestStep{ + { + Config: testAccKubernetesStatefulSetV1ConfigHostUsers(name, imageName, true), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "spec.0.template.0.spec.0.host_users", "true"), + ), + }, + { + Config: testAccKubernetesStatefulSetV1ConfigHostUsers(name, imageName, false), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "spec.0.template.0.spec.0.host_users", "false"), + ), + }, + }, + }) +} + func TestAccKubernetesStatefulSetV1_waitForRollout(t *testing.T) { var conf1, conf2 appsv1.StatefulSet imageName := busyboxImage @@ -1478,3 +1507,36 @@ func testAccKubernetesStatefulSetV1ConfigMinimalWithTemplateNamespace(name, imag } `, name, imageName) } + +func testAccKubernetesStatefulSetV1ConfigHostUsers(name, image string, hostUsers bool) string { + return fmt.Sprintf(` +resource "kubernetes_stateful_set_v1" "test" { + metadata { + name = "%s" + } + spec { + replicas = 1 + selector { + match_labels = { + app = "tf-acc-test" + } + } + service_name = "nginx" + template { + metadata { + labels = { + app = "tf-acc-test" + } + } + spec { + host_users = %t + container { + image = "%s" + name = "test" + } + } + } + } +} +`, name, hostUsers, image) +} diff --git a/kubernetes/schema_pod_spec.go b/kubernetes/schema_pod_spec.go index c760b58cca..ca93f9af63 100644 --- a/kubernetes/schema_pod_spec.go +++ b/kubernetes/schema_pod_spec.go @@ -186,7 +186,6 @@ func podSpecFields(isUpdatable, isComputed bool) map[string]*schema.Schema { Default: conditionalDefault(!isComputed, false), Description: "Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified.", }, - "host_pid": { Type: schema.TypeBool, Optional: true, @@ -195,7 +194,14 @@ func podSpecFields(isUpdatable, isComputed bool) map[string]*schema.Schema { Default: conditionalDefault(!isComputed, false), Description: "Use the host's pid namespace.", }, - + "host_users": { + Type: schema.TypeBool, + Optional: true, + Computed: isComputed, + ForceNew: !isUpdatable, + Default: conditionalDefault(!isComputed, true), + Description: "Use the host's user namespace. Optional: Defaults to true.", + }, "hostname": { Type: schema.TypeString, Optional: true, diff --git a/kubernetes/structures_pod.go b/kubernetes/structures_pod.go index 808d1efc8e..e4d96aa5e9 100644 --- a/kubernetes/structures_pod.go +++ b/kubernetes/structures_pod.go @@ -91,6 +91,9 @@ func flattenPodSpec(in v1.PodSpec, isTemplate bool) ([]interface{}, error) { att["host_ipc"] = in.HostIPC att["host_network"] = in.HostNetwork att["host_pid"] = in.HostPID + if in.HostUsers != nil { + att["host_users"] = *in.HostUsers + } if in.Hostname != "" { att["hostname"] = in.Hostname @@ -795,6 +798,9 @@ func expandPodSpec(p []interface{}) (*v1.PodSpec, error) { if v, ok := in["host_pid"]; ok { obj.HostPID = v.(bool) } + if v, ok := in["host_users"]; ok { + obj.HostUsers = ptr.To(v.(bool)) + } if v, ok := in["hostname"]; ok { obj.Hostname = v.(string) From 86f00977459f1e62bfc0afde98c0f02218d2fcbb Mon Sep 17 00:00:00 2001 From: Gavin Williams Date: Thu, 8 Jan 2026 14:28:26 +0000 Subject: [PATCH 2/3] Add changelog --- .changelog/2828.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changelog/2828.txt diff --git a/.changelog/2828.txt b/.changelog/2828.txt new file mode 100644 index 0000000000..512a9f9001 --- /dev/null +++ b/.changelog/2828.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +Add User Namespaces support to following resources: +* `kubernetes_pod_v1` +* `kubernetes_daemonset_v1` +* `kubernetes_deployment_v1` +* `kubernetes_stateful_set_v1` +``` From ee64d252f9bc9f7dd93304b3f25833511344089f Mon Sep 17 00:00:00 2001 From: Gavin Williams Date: Thu, 8 Jan 2026 14:30:12 +0000 Subject: [PATCH 3/3] Fix changelog --- .changelog/2828.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changelog/2828.txt b/.changelog/2828.txt index 512a9f9001..0a654bf794 100644 --- a/.changelog/2828.txt +++ b/.changelog/2828.txt @@ -1,7 +1,7 @@ ```release-note:enhancement Add User Namespaces support to following resources: -* `kubernetes_pod_v1` -* `kubernetes_daemonset_v1` +* `kubernetes_daemon_set_v1` * `kubernetes_deployment_v1` +* `kubernetes_pod_v1` * `kubernetes_stateful_set_v1` ```