diff --git a/test/e2e/windows/host_process.go b/test/e2e/windows/host_process.go index eb7ad35c9a2..458aab0c386 100644 --- a/test/e2e/windows/host_process.go +++ b/test/e2e/windows/host_process.go @@ -22,8 +22,6 @@ import ( "strings" "time" - e2epod "k8s.io/kubernetes/test/e2e/framework/pod" - "github.com/onsi/ginkgo" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -31,10 +29,46 @@ import ( "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/test/e2e/framework" + e2epod "k8s.io/kubernetes/test/e2e/framework/pod" e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" imageutils "k8s.io/kubernetes/test/utils/image" ) +const ( + validation_script = `if (-not(Test-Path $env:CONTAINER_SANDBOX_MOUNT_POINT\etc\emptydir)) { + throw "Cannot find emptydir volume" + } + if (-not(Test-Path $env:CONTAINER_SANDBOX_MOUNT_POINT\etc\configmap\text.txt)) { + throw "Cannot find text.txt in configmap-volume" + } + $c = Get-Content -Path $env:CONTAINER_SANDBOX_MOUNT_POINT\etc\configmap\text.txt + if ($c -ne "Lorem ipsum dolor sit amet") { + throw "Contents of /etc/configmap/text.txt are not as expected" + } + if (-not(Test-Path $env:CONTAINER_SANDBOX_MOUNT_POINT\etc\hostpath)) { + throw "Cannot find hostpath volume" + } + if (-not(Test-Path $env:CONTAINER_SANDBOX_MOUNT_POINT\etc\downwardapi\podname)) { + throw "Cannot find podname file in downward-api volume" + } + $c = Get-Content -Path $env:CONTAINER_SANDBOX_MOUNT_POINT\etc\downwardapi\podname + if ($c -ne "host-process-volume-mounts") { + throw "Contents of /etc/downward-api/podname are not as expected" + } + if (-not(Test-Path $env:CONTAINER_SANDBOX_MOUNT_POINT\etc\secret\foo.txt)) { + throw "Cannot find file foo.txt in secret volume" + } + $c = Get-Content $env:CONTAINER_SANDBOX_MOUNT_POINT\etc\secret\foo.txt + if ($c -ne "bar") { + Write-Output $c + throw "Contents of /etc/secret/foo.txt are not as expected" + } + if ($env:NODE_NAME_TEST -ne $env:COMPUTERNAME) { + throw "NODE_NAME_TEST env var does not equal COMPUTERNAME" + } + Write-Output "SUCCESS"` +) + var _ = SIGDescribe("[Feature:WindowsHostProcessContainers] [Excluded:WindowsDocker] [MinimumKubeletVersion:1.22] HostProcess containers", func() { ginkgo.BeforeEach(func() { e2eskipper.SkipUnlessNodeOSDistroIs("windows") @@ -383,8 +417,201 @@ var _ = SIGDescribe("[Feature:WindowsHostProcessContainers] [Excluded:WindowsDoc } }) + + ginkgo.It("should support various volume mount types", func() { + ns := f.Namespace + + ginkgo.By("Creating a configmap containing test data and a validation script") + configMap := &v1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "sample-config-map", + }, + Data: map[string]string{ + "text": "Lorem ipsum dolor sit amet", + "validation-script": validation_script, + }, + } + _, err := f.ClientSet.CoreV1().ConfigMaps(ns.Name).Create(context.TODO(), configMap, metav1.CreateOptions{}) + framework.ExpectNoError(err, "unable to create create configmap") + + ginkgo.By("Creating a secret containing test data") + secret := &v1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "sample-secret", + }, + Type: v1.SecretTypeOpaque, + Data: map[string][]byte{ + "foo": []byte("bar"), + }, + } + _, err = f.ClientSet.CoreV1().Secrets(ns.Name).Create(context.TODO(), secret, metav1.CreateOptions{}) + framework.ExpectNoError(err, "unable to create secret") + + ginkgo.By("Creating a pod with a HostProcess container that uses various types of volume mounts") + + podAndContainerName := "host-process-volume-mounts" + pod := makeTestPodWithVolumeMounts(podAndContainerName) + + f.PodClient().Create(pod) + + ginkgo.By("Waiting for pod to run") + f.PodClient().WaitForFinish(podAndContainerName, 3*time.Minute) + + logs, err := e2epod.GetPodLogs(f.ClientSet, ns.Name, podAndContainerName, podAndContainerName) + framework.ExpectNoError(err, "Error getting pod logs") + framework.Logf("Container logs: %s", logs) + + ginkgo.By("Then ensuring pod finished running successfully") + p, err := f.ClientSet.CoreV1().Pods(ns.Name).Get( + context.TODO(), + podAndContainerName, + metav1.GetOptions{}) + + framework.ExpectNoError(err, "Error retrieving pod") + framework.ExpectEqual(p.Status.Phase, v1.PodSucceeded) + }) + }) +func makeTestPodWithVolumeMounts(name string) *v1.Pod { + trueVar := true + username := "NT AUTHORITY\\SYSTEM" + hostPathDirectoryOrCreate := v1.HostPathDirectoryOrCreate + return &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: v1.PodSpec{ + SecurityContext: &v1.PodSecurityContext{ + WindowsOptions: &v1.WindowsSecurityContextOptions{ + HostProcess: &trueVar, + RunAsUserName: &username, + }, + }, + HostNetwork: true, + Containers: []v1.Container{ + { + Image: imageutils.GetE2EImage(imageutils.BusyBox), + Name: name, + Command: []string{"powershell.exe", "./etc/configmap/validationscript.ps1"}, + Env: []v1.EnvVar{ + { + Name: "NODE_NAME_TEST", + ValueFrom: &v1.EnvVarSource{ + FieldRef: &v1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }, + }, + }, + }, + VolumeMounts: []v1.VolumeMount{ + { + Name: "emptydir-volume", + MountPath: "/etc/emptydir", + }, + { + Name: "configmap-volume", + MountPath: "/etc/configmap", + }, + { + Name: "hostpath-volume", + MountPath: "/etc/hostpath", + }, + { + Name: "downwardapi-volume", + MountPath: "/etc/downwardapi", + }, + { + Name: "secret-volume", + MountPath: "/etc/secret", + }, + }, + }, + }, + RestartPolicy: v1.RestartPolicyNever, + NodeSelector: map[string]string{ + "kubernetes.io/os": "windows", + }, + Volumes: []v1.Volume{ + { + Name: "emptydir-volume", + VolumeSource: v1.VolumeSource{ + EmptyDir: &v1.EmptyDirVolumeSource{ + Medium: v1.StorageMediumDefault, + }, + }, + }, + { + Name: "configmap-volume", + VolumeSource: v1.VolumeSource{ + ConfigMap: &v1.ConfigMapVolumeSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "sample-config-map", + }, + Items: []v1.KeyToPath{ + { + Key: "text", + Path: "text.txt", + }, + { + Key: "validation-script", + Path: "validationscript.ps1", + }, + }, + }, + }, + }, + { + Name: "hostpath-volume", + VolumeSource: v1.VolumeSource{ + HostPath: &v1.HostPathVolumeSource{ + Path: "/var/hostpath", + Type: &hostPathDirectoryOrCreate, + }, + }, + }, + { + Name: "downwardapi-volume", + VolumeSource: v1.VolumeSource{ + DownwardAPI: &v1.DownwardAPIVolumeSource{ + Items: []v1.DownwardAPIVolumeFile{ + { + Path: "podname", + FieldRef: &v1.ObjectFieldSelector{ + FieldPath: "metadata.name", + }, + }, + }, + }, + }, + }, + { + Name: "secret-volume", + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: "sample-secret", + Items: []v1.KeyToPath{ + { + Key: "foo", + Path: "foo.txt", + }, + }, + }, + }, + }, + }, + }, + } +} + func SkipUnlessWindowsHostProcessContainersEnabled() { if !framework.TestContext.FeatureGates[string(features.WindowsHostProcessContainers)] { e2eskipper.Skipf("Skipping test because feature 'WindowsHostProcessContainers' is not enabled")