Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding e2e tests to validate volume mounts in HostProcessContainers on Windows #105996

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
231 changes: 229 additions & 2 deletions test/e2e/windows/host_process.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,53 @@ 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"
"k8s.io/apimachinery/pkg/fields"
"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")
Expand Down Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking this should only be done in the case of failure so it would help understand why this test might have failed from reading the test logs. I don't think the logs would be verbose in the success state so It might be fine here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, right now i'll just output SUCCESS which shouldn't bloat the logs too much.


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")
Expand Down