k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/windows/volumes.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 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 _ = sigDescribe(feature.Windows, "Windows volume mounts", skipUnlessWindows(func() { 45 f := framework.NewDefaultFramework("windows-volumes") 46 f.NamespacePodSecurityLevel = admissionapi.LevelPrivileged 47 var ( 48 emptyDirSource = v1.VolumeSource{ 49 EmptyDir: &v1.EmptyDirVolumeSource{ 50 Medium: v1.StorageMediumDefault, 51 }, 52 } 53 hostPathDirectoryOrCreate = v1.HostPathDirectoryOrCreate 54 hostMapSource = v1.VolumeSource{ 55 HostPath: &v1.HostPathVolumeSource{ 56 Path: hostMapPath, 57 Type: &hostPathDirectoryOrCreate, 58 }, 59 } 60 ) 61 ginkgo.BeforeEach(func() { 62 e2eskipper.SkipUnlessNodeOSDistroIs("windows") 63 }) 64 65 ginkgo.Context("check volume mount permissions", func() { 66 67 ginkgo.It("container should have readOnly permissions on emptyDir", func(ctx context.Context) { 68 69 ginkgo.By("creating a container with readOnly permissions on emptyDir volume") 70 doReadOnlyTest(ctx, f, emptyDirSource, emptyDirVolumePath) 71 72 ginkgo.By("creating two containers, one with readOnly permissions the other with read-write permissions on emptyDir volume") 73 doReadWriteReadOnlyTest(ctx, f, emptyDirSource, emptyDirVolumePath) 74 }) 75 76 ginkgo.It("container should have readOnly permissions on hostMapPath", func(ctx context.Context) { 77 78 ginkgo.By("creating a container with readOnly permissions on hostMap volume") 79 doReadOnlyTest(ctx, f, hostMapSource, hostMapPath) 80 81 ginkgo.By("creating two containers, one with readOnly permissions the other with read-write permissions on hostMap volume") 82 doReadWriteReadOnlyTest(ctx, f, hostMapSource, hostMapPath) 83 }) 84 85 }) 86 })) 87 88 func doReadOnlyTest(ctx context.Context, f *framework.Framework, source v1.VolumeSource, volumePath string) { 89 var ( 90 filePath = volumePath + "\\test-file.txt" 91 podName = "pod-" + string(uuid.NewUUID()) 92 pod = testPodWithROVolume(podName, source, volumePath) 93 ) 94 pod.Spec.NodeSelector = map[string]string{ 95 "kubernetes.io/os": "windows", 96 } 97 98 pod = e2epod.NewPodClient(f).CreateSync(ctx, pod) 99 ginkgo.By("verifying that pod has the correct nodeSelector") 100 gomega.Expect(pod.Spec.NodeSelector).To(gomega.HaveKeyWithValue("kubernetes.io/os", "windows"), "pod.spec.nodeSelector") 101 102 cmd := []string{"cmd", "/c", "echo windows-volume-test", ">", filePath} 103 104 ginkgo.By("verifying that pod will get an error when writing to a volume that is readonly") 105 _, stderr, _ := e2epod.ExecCommandInContainerWithFullOutput(f, podName, containerName, cmd...) 106 gomega.Expect(stderr).To(gomega.Equal("Access is denied.")) 107 } 108 109 func doReadWriteReadOnlyTest(ctx context.Context, f *framework.Framework, source v1.VolumeSource, volumePath string) { 110 var ( 111 filePath = volumePath + "\\test-file" + string(uuid.NewUUID()) 112 podName = "pod-" + string(uuid.NewUUID()) 113 pod = testPodWithROVolume(podName, source, volumePath) 114 rwcontainerName = containerName + "-rw" 115 ) 116 pod.Spec.NodeSelector = map[string]string{ 117 "kubernetes.io/os": "windows", 118 } 119 120 rwcontainer := v1.Container{ 121 Name: containerName + "-rw", 122 Image: imageutils.GetE2EImage(imageutils.Pause), 123 VolumeMounts: []v1.VolumeMount{ 124 { 125 Name: volumeName, 126 MountPath: volumePath, 127 }, 128 }, 129 } 130 131 pod.Spec.Containers = append(pod.Spec.Containers, rwcontainer) 132 pod = e2epod.NewPodClient(f).CreateSync(ctx, pod) 133 134 ginkgo.By("verifying that pod has the correct nodeSelector") 135 gomega.Expect(pod.Spec.NodeSelector).To(gomega.HaveKeyWithValue("kubernetes.io/os", "windows"), "pod.spec.nodeSelector") 136 137 ginkgo.By("verifying that pod can write to a volume with read/write access") 138 writecmd := []string{"cmd", "/c", "echo windows-volume-test", ">", filePath} 139 stdoutRW, stderrRW, errRW := e2epod.ExecCommandInContainerWithFullOutput(f, podName, rwcontainerName, writecmd...) 140 msg := fmt.Sprintf("cmd: %v, stdout: %q, stderr: %q", writecmd, stdoutRW, stderrRW) 141 framework.ExpectNoError(errRW, msg) 142 143 ginkgo.By("verifying that pod will get an error when writing to a volume that is readonly") 144 _, stderr, _ := e2epod.ExecCommandInContainerWithFullOutput(f, podName, containerName, writecmd...) 145 gomega.Expect(stderr).To(gomega.Equal("Access is denied.")) 146 147 ginkgo.By("verifying that pod can read from a volume that is readonly") 148 readcmd := []string{"cmd", "/c", "type", filePath} 149 readout, readerr, err := e2epod.ExecCommandInContainerWithFullOutput(f, podName, containerName, readcmd...) 150 readmsg := fmt.Sprintf("cmd: %v, stdout: %q, stderr: %q", readcmd, readout, readerr) 151 gomega.Expect(readout).To(gomega.Equal("windows-volume-test")) 152 framework.ExpectNoError(err, readmsg) 153 } 154 155 // testPodWithROVolume makes a minimal pod defining a volume input source. Similarly to 156 // other tests for sig-windows this should append a nodeSelector for windows. 157 func testPodWithROVolume(podName string, source v1.VolumeSource, path string) *v1.Pod { 158 return &v1.Pod{ 159 TypeMeta: metav1.TypeMeta{ 160 Kind: "Pod", 161 APIVersion: "v1", 162 }, 163 ObjectMeta: metav1.ObjectMeta{ 164 Name: podName, 165 }, 166 Spec: v1.PodSpec{ 167 Containers: []v1.Container{ 168 { 169 Name: containerName, 170 Image: imageutils.GetE2EImage(imageutils.Pause), 171 VolumeMounts: []v1.VolumeMount{ 172 { 173 Name: volumeName, 174 MountPath: path, 175 ReadOnly: true, 176 }, 177 }, 178 }, 179 }, 180 RestartPolicy: v1.RestartPolicyNever, 181 Volumes: []v1.Volume{ 182 { 183 Name: volumeName, 184 VolumeSource: source, 185 }, 186 }, 187 }, 188 } 189 }