1
16
17 package windows
18
19 import (
20 "context"
21 "fmt"
22
23 v1 "k8s.io/api/core/v1"
24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25 "k8s.io/apimachinery/pkg/util/uuid"
26 "k8s.io/kubernetes/test/e2e/feature"
27 "k8s.io/kubernetes/test/e2e/framework"
28 e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
29 e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
30 imageutils "k8s.io/kubernetes/test/utils/image"
31 admissionapi "k8s.io/pod-security-admission/api"
32
33 "github.com/onsi/ginkgo/v2"
34 "github.com/onsi/gomega"
35 )
36
37 const (
38 emptyDirVolumePath = "C:\\test-volume"
39 hostMapPath = "C:\\tmp"
40 containerName = "test-container"
41 volumeName = "test-volume"
42 )
43
44 var (
45 image = imageutils.GetE2EImage(imageutils.Pause)
46 )
47
48 var _ = sigDescribe(feature.Windows, "Windows volume mounts", skipUnlessWindows(func() {
49 f := framework.NewDefaultFramework("windows-volumes")
50 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged
51 var (
52 emptyDirSource = v1.VolumeSource{
53 EmptyDir: &v1.EmptyDirVolumeSource{
54 Medium: v1.StorageMediumDefault,
55 },
56 }
57 hostPathDirectoryOrCreate = v1.HostPathDirectoryOrCreate
58 hostMapSource = v1.VolumeSource{
59 HostPath: &v1.HostPathVolumeSource{
60 Path: hostMapPath,
61 Type: &hostPathDirectoryOrCreate,
62 },
63 }
64 )
65 ginkgo.BeforeEach(func() {
66 e2eskipper.SkipUnlessNodeOSDistroIs("windows")
67 })
68
69 ginkgo.Context("check volume mount permissions", func() {
70
71 ginkgo.It("container should have readOnly permissions on emptyDir", func(ctx context.Context) {
72
73 ginkgo.By("creating a container with readOnly permissions on emptyDir volume")
74 doReadOnlyTest(ctx, f, emptyDirSource, emptyDirVolumePath)
75
76 ginkgo.By("creating two containers, one with readOnly permissions the other with read-write permissions on emptyDir volume")
77 doReadWriteReadOnlyTest(ctx, f, emptyDirSource, emptyDirVolumePath)
78 })
79
80 ginkgo.It("container should have readOnly permissions on hostMapPath", func(ctx context.Context) {
81
82 ginkgo.By("creating a container with readOnly permissions on hostMap volume")
83 doReadOnlyTest(ctx, f, hostMapSource, hostMapPath)
84
85 ginkgo.By("creating two containers, one with readOnly permissions the other with read-write permissions on hostMap volume")
86 doReadWriteReadOnlyTest(ctx, f, hostMapSource, hostMapPath)
87 })
88
89 })
90 }))
91
92 func doReadOnlyTest(ctx context.Context, f *framework.Framework, source v1.VolumeSource, volumePath string) {
93 var (
94 filePath = volumePath + "\\test-file.txt"
95 podName = "pod-" + string(uuid.NewUUID())
96 pod = testPodWithROVolume(podName, source, volumePath)
97 )
98 pod.Spec.NodeSelector = map[string]string{
99 "kubernetes.io/os": "windows",
100 }
101
102 pod = e2epod.NewPodClient(f).CreateSync(ctx, pod)
103 ginkgo.By("verifying that pod has the correct nodeSelector")
104 gomega.Expect(pod.Spec.NodeSelector).To(gomega.HaveKeyWithValue("kubernetes.io/os", "windows"), "pod.spec.nodeSelector")
105
106 cmd := []string{"cmd", "/c", "echo windows-volume-test", ">", filePath}
107
108 ginkgo.By("verifying that pod will get an error when writing to a volume that is readonly")
109 _, stderr, _ := e2epod.ExecCommandInContainerWithFullOutput(f, podName, containerName, cmd...)
110 gomega.Expect(stderr).To(gomega.Equal("Access is denied."))
111 }
112
113 func doReadWriteReadOnlyTest(ctx context.Context, f *framework.Framework, source v1.VolumeSource, volumePath string) {
114 var (
115 filePath = volumePath + "\\test-file" + string(uuid.NewUUID())
116 podName = "pod-" + string(uuid.NewUUID())
117 pod = testPodWithROVolume(podName, source, volumePath)
118 rwcontainerName = containerName + "-rw"
119 )
120 pod.Spec.NodeSelector = map[string]string{
121 "kubernetes.io/os": "windows",
122 }
123
124 rwcontainer := v1.Container{
125 Name: containerName + "-rw",
126 Image: image,
127 VolumeMounts: []v1.VolumeMount{
128 {
129 Name: volumeName,
130 MountPath: volumePath,
131 },
132 },
133 }
134
135 pod.Spec.Containers = append(pod.Spec.Containers, rwcontainer)
136 pod = e2epod.NewPodClient(f).CreateSync(ctx, pod)
137
138 ginkgo.By("verifying that pod has the correct nodeSelector")
139 gomega.Expect(pod.Spec.NodeSelector).To(gomega.HaveKeyWithValue("kubernetes.io/os", "windows"), "pod.spec.nodeSelector")
140
141 ginkgo.By("verifying that pod can write to a volume with read/write access")
142 writecmd := []string{"cmd", "/c", "echo windows-volume-test", ">", filePath}
143 stdoutRW, stderrRW, errRW := e2epod.ExecCommandInContainerWithFullOutput(f, podName, rwcontainerName, writecmd...)
144 msg := fmt.Sprintf("cmd: %v, stdout: %q, stderr: %q", writecmd, stdoutRW, stderrRW)
145 framework.ExpectNoError(errRW, msg)
146
147 ginkgo.By("verifying that pod will get an error when writing to a volume that is readonly")
148 _, stderr, _ := e2epod.ExecCommandInContainerWithFullOutput(f, podName, containerName, writecmd...)
149 gomega.Expect(stderr).To(gomega.Equal("Access is denied."))
150
151 ginkgo.By("verifying that pod can read from a volume that is readonly")
152 readcmd := []string{"cmd", "/c", "type", filePath}
153 readout, readerr, err := e2epod.ExecCommandInContainerWithFullOutput(f, podName, containerName, readcmd...)
154 readmsg := fmt.Sprintf("cmd: %v, stdout: %q, stderr: %q", readcmd, readout, readerr)
155 gomega.Expect(readout).To(gomega.Equal("windows-volume-test"))
156 framework.ExpectNoError(err, readmsg)
157 }
158
159
160
161 func testPodWithROVolume(podName string, source v1.VolumeSource, path string) *v1.Pod {
162 return &v1.Pod{
163 TypeMeta: metav1.TypeMeta{
164 Kind: "Pod",
165 APIVersion: "v1",
166 },
167 ObjectMeta: metav1.ObjectMeta{
168 Name: podName,
169 },
170 Spec: v1.PodSpec{
171 Containers: []v1.Container{
172 {
173 Name: containerName,
174 Image: image,
175 VolumeMounts: []v1.VolumeMount{
176 {
177 Name: volumeName,
178 MountPath: path,
179 ReadOnly: true,
180 },
181 },
182 },
183 },
184 RestartPolicy: v1.RestartPolicyNever,
185 Volumes: []v1.Volume{
186 {
187 Name: volumeName,
188 VolumeSource: source,
189 },
190 },
191 },
192 }
193 }
194
View as plain text