k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/kubelet/kubelet_pods_linux_test.go (about) 1 //go:build linux 2 // +build linux 3 4 /* 5 Copyright 2018 The Kubernetes Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 */ 19 20 package kubelet 21 22 import ( 23 "testing" 24 25 "github.com/stretchr/testify/assert" 26 v1 "k8s.io/api/core/v1" 27 28 runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" 29 _ "k8s.io/kubernetes/pkg/apis/core/install" 30 kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" 31 volumetest "k8s.io/kubernetes/pkg/volume/testing" 32 "k8s.io/kubernetes/pkg/volume/util/hostutil" 33 "k8s.io/kubernetes/pkg/volume/util/subpath" 34 ) 35 36 func TestMakeMounts(t *testing.T) { 37 bTrue := true 38 propagationHostToContainer := v1.MountPropagationHostToContainer 39 propagationBidirectional := v1.MountPropagationBidirectional 40 propagationNone := v1.MountPropagationNone 41 42 testCases := map[string]struct { 43 container v1.Container 44 podVolumes kubecontainer.VolumeMap 45 supportsRRO bool 46 expectErr bool 47 expectedErrMsg string 48 expectedMounts []kubecontainer.Mount 49 }{ 50 "valid mounts in unprivileged container": { 51 podVolumes: kubecontainer.VolumeMap{ 52 "disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}}, 53 "disk4": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/host"}}, 54 "disk5": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/var/lib/kubelet/podID/volumes/empty/disk5"}}, 55 }, 56 container: v1.Container{ 57 Name: "container1", 58 VolumeMounts: []v1.VolumeMount{ 59 { 60 MountPath: "/etc/hosts", 61 Name: "disk", 62 ReadOnly: false, 63 MountPropagation: &propagationHostToContainer, 64 }, 65 { 66 MountPath: "/mnt/path3", 67 Name: "disk", 68 ReadOnly: true, 69 MountPropagation: &propagationNone, 70 }, 71 { 72 MountPath: "/mnt/path4", 73 Name: "disk4", 74 ReadOnly: false, 75 }, 76 { 77 MountPath: "/mnt/path5", 78 Name: "disk5", 79 ReadOnly: false, 80 }, 81 }, 82 }, 83 expectedMounts: []kubecontainer.Mount{ 84 { 85 Name: "disk", 86 ContainerPath: "/etc/hosts", 87 HostPath: "/mnt/disk", 88 ReadOnly: false, 89 SELinuxRelabel: false, 90 Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER, 91 }, 92 { 93 Name: "disk", 94 ContainerPath: "/mnt/path3", 95 HostPath: "/mnt/disk", 96 ReadOnly: true, 97 SELinuxRelabel: false, 98 Propagation: runtimeapi.MountPropagation_PROPAGATION_PRIVATE, 99 }, 100 { 101 Name: "disk4", 102 ContainerPath: "/mnt/path4", 103 HostPath: "/mnt/host", 104 ReadOnly: false, 105 SELinuxRelabel: false, 106 Propagation: runtimeapi.MountPropagation_PROPAGATION_PRIVATE, 107 }, 108 { 109 Name: "disk5", 110 ContainerPath: "/mnt/path5", 111 HostPath: "/var/lib/kubelet/podID/volumes/empty/disk5", 112 ReadOnly: false, 113 SELinuxRelabel: false, 114 Propagation: runtimeapi.MountPropagation_PROPAGATION_PRIVATE, 115 }, 116 }, 117 expectErr: false, 118 }, 119 "valid mounts in privileged container": { 120 podVolumes: kubecontainer.VolumeMap{ 121 "disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}}, 122 "disk4": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/host"}}, 123 "disk5": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/var/lib/kubelet/podID/volumes/empty/disk5"}}, 124 }, 125 container: v1.Container{ 126 Name: "container1", 127 VolumeMounts: []v1.VolumeMount{ 128 { 129 MountPath: "/etc/hosts", 130 Name: "disk", 131 ReadOnly: false, 132 MountPropagation: &propagationBidirectional, 133 }, 134 { 135 MountPath: "/mnt/path3", 136 Name: "disk", 137 ReadOnly: true, 138 MountPropagation: &propagationHostToContainer, 139 }, 140 { 141 MountPath: "/mnt/path4", 142 Name: "disk4", 143 ReadOnly: false, 144 }, 145 }, 146 SecurityContext: &v1.SecurityContext{ 147 Privileged: &bTrue, 148 }, 149 }, 150 expectedMounts: []kubecontainer.Mount{ 151 { 152 Name: "disk", 153 ContainerPath: "/etc/hosts", 154 HostPath: "/mnt/disk", 155 ReadOnly: false, 156 SELinuxRelabel: false, 157 Propagation: runtimeapi.MountPropagation_PROPAGATION_BIDIRECTIONAL, 158 }, 159 { 160 Name: "disk", 161 ContainerPath: "/mnt/path3", 162 HostPath: "/mnt/disk", 163 ReadOnly: true, 164 SELinuxRelabel: false, 165 Propagation: runtimeapi.MountPropagation_PROPAGATION_HOST_TO_CONTAINER, 166 }, 167 { 168 Name: "disk4", 169 ContainerPath: "/mnt/path4", 170 HostPath: "/mnt/host", 171 ReadOnly: false, 172 SELinuxRelabel: false, 173 Propagation: runtimeapi.MountPropagation_PROPAGATION_PRIVATE, 174 }, 175 }, 176 expectErr: false, 177 }, 178 "invalid absolute SubPath": { 179 podVolumes: kubecontainer.VolumeMap{ 180 "disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}}, 181 }, 182 container: v1.Container{ 183 VolumeMounts: []v1.VolumeMount{ 184 { 185 MountPath: "/mnt/path3", 186 SubPath: "/must/not/be/absolute", 187 Name: "disk", 188 ReadOnly: true, 189 }, 190 }, 191 }, 192 expectErr: true, 193 expectedErrMsg: "error SubPath `/must/not/be/absolute` must not be an absolute path", 194 }, 195 "invalid SubPath with backsteps": { 196 podVolumes: kubecontainer.VolumeMap{ 197 "disk": kubecontainer.VolumeInfo{Mounter: &stubVolume{path: "/mnt/disk"}}, 198 }, 199 container: v1.Container{ 200 VolumeMounts: []v1.VolumeMount{ 201 { 202 MountPath: "/mnt/path3", 203 SubPath: "no/backsteps/../allowed", 204 Name: "disk", 205 ReadOnly: true, 206 }, 207 }, 208 }, 209 expectErr: true, 210 expectedErrMsg: "unable to provision SubPath `no/backsteps/../allowed`: must not contain '..'", 211 }, 212 "volume doesn't exist": { 213 podVolumes: kubecontainer.VolumeMap{}, 214 container: v1.Container{ 215 VolumeMounts: []v1.VolumeMount{ 216 { 217 MountPath: "/mnt/path3", 218 Name: "disk", 219 ReadOnly: true, 220 }, 221 }, 222 }, 223 expectErr: true, 224 expectedErrMsg: "cannot find volume \"disk\" to mount into container \"\"", 225 }, 226 "volume mounter is nil": { 227 podVolumes: kubecontainer.VolumeMap{ 228 "disk": kubecontainer.VolumeInfo{}, 229 }, 230 container: v1.Container{ 231 VolumeMounts: []v1.VolumeMount{ 232 { 233 MountPath: "/mnt/path3", 234 Name: "disk", 235 ReadOnly: true, 236 }, 237 }, 238 }, 239 expectErr: true, 240 expectedErrMsg: "cannot find volume \"disk\" to mount into container \"\"", 241 }, 242 } 243 244 for name, tc := range testCases { 245 t.Run(name, func(t *testing.T) { 246 fhu := hostutil.NewFakeHostUtil(nil) 247 fsp := &subpath.FakeSubpath{} 248 pod := v1.Pod{ 249 Spec: v1.PodSpec{ 250 HostNetwork: true, 251 }, 252 } 253 254 mounts, _, err := makeMounts(&pod, "/pod", &tc.container, "fakepodname", "", []string{""}, tc.podVolumes, fhu, fsp, nil, tc.supportsRRO) 255 256 // validate only the error if we expect an error 257 if tc.expectErr { 258 if err == nil || err.Error() != tc.expectedErrMsg { 259 t.Fatalf("expected error message `%s` but got `%v`", tc.expectedErrMsg, err) 260 } 261 return 262 } 263 264 // otherwise validate the mounts 265 if err != nil { 266 t.Fatal(err) 267 } 268 269 assert.Equal(t, tc.expectedMounts, mounts, "mounts of container %+v", tc.container) 270 }) 271 } 272 } 273 274 func TestMakeBlockVolumes(t *testing.T) { 275 testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) 276 defer testKubelet.Cleanup() 277 kubelet := testKubelet.kubelet 278 testCases := map[string]struct { 279 container v1.Container 280 podVolumes kubecontainer.VolumeMap 281 expectErr bool 282 expectedErrMsg string 283 expectedDevices []kubecontainer.DeviceInfo 284 }{ 285 "valid volumeDevices in container": { 286 podVolumes: kubecontainer.VolumeMap{ 287 "disk1": kubecontainer.VolumeInfo{BlockVolumeMapper: &stubBlockVolume{dirPath: "/dev/", volName: "sda"}}, 288 "disk2": kubecontainer.VolumeInfo{BlockVolumeMapper: &stubBlockVolume{dirPath: "/dev/disk/by-path/", volName: "diskPath"}, ReadOnly: true}, 289 "disk3": kubecontainer.VolumeInfo{BlockVolumeMapper: &stubBlockVolume{dirPath: "/dev/disk/by-id/", volName: "diskUuid"}}, 290 "disk4": kubecontainer.VolumeInfo{BlockVolumeMapper: &stubBlockVolume{dirPath: "/var/lib/", volName: "rawdisk"}, ReadOnly: true}, 291 }, 292 container: v1.Container{ 293 Name: "container1", 294 VolumeDevices: []v1.VolumeDevice{ 295 { 296 DevicePath: "/dev/sda", 297 Name: "disk1", 298 }, 299 { 300 DevicePath: "/dev/xvda", 301 Name: "disk2", 302 }, 303 { 304 DevicePath: "/dev/xvdb", 305 Name: "disk3", 306 }, 307 { 308 DevicePath: "/mnt/rawdisk", 309 Name: "disk4", 310 }, 311 }, 312 }, 313 expectedDevices: []kubecontainer.DeviceInfo{ 314 { 315 PathInContainer: "/dev/sda", 316 PathOnHost: "/dev/sda", 317 Permissions: "mrw", 318 }, 319 { 320 PathInContainer: "/dev/xvda", 321 PathOnHost: "/dev/disk/by-path/diskPath", 322 Permissions: "r", 323 }, 324 { 325 PathInContainer: "/dev/xvdb", 326 PathOnHost: "/dev/disk/by-id/diskUuid", 327 Permissions: "mrw", 328 }, 329 { 330 PathInContainer: "/mnt/rawdisk", 331 PathOnHost: "/var/lib/rawdisk", 332 Permissions: "r", 333 }, 334 }, 335 expectErr: false, 336 }, 337 "invalid absolute Path": { 338 podVolumes: kubecontainer.VolumeMap{ 339 "disk": kubecontainer.VolumeInfo{BlockVolumeMapper: &stubBlockVolume{dirPath: "/dev/", volName: "sda"}}, 340 }, 341 container: v1.Container{ 342 VolumeDevices: []v1.VolumeDevice{ 343 { 344 DevicePath: "must/be/absolute", 345 Name: "disk", 346 }, 347 }, 348 }, 349 expectErr: true, 350 expectedErrMsg: "error DevicePath `must/be/absolute` must be an absolute path", 351 }, 352 "volume doesn't exist": { 353 podVolumes: kubecontainer.VolumeMap{}, 354 container: v1.Container{ 355 VolumeDevices: []v1.VolumeDevice{ 356 { 357 DevicePath: "/dev/sdaa", 358 Name: "disk", 359 }, 360 }, 361 }, 362 expectErr: true, 363 expectedErrMsg: "cannot find volume \"disk\" to pass into container \"\"", 364 }, 365 "volume BlockVolumeMapper is nil": { 366 podVolumes: kubecontainer.VolumeMap{ 367 "disk": kubecontainer.VolumeInfo{}, 368 }, 369 container: v1.Container{ 370 VolumeDevices: []v1.VolumeDevice{ 371 { 372 DevicePath: "/dev/sdzz", 373 Name: "disk", 374 }, 375 }, 376 }, 377 expectErr: true, 378 expectedErrMsg: "cannot find volume \"disk\" to pass into container \"\"", 379 }, 380 } 381 382 for name, tc := range testCases { 383 t.Run(name, func(t *testing.T) { 384 pod := v1.Pod{ 385 Spec: v1.PodSpec{ 386 HostNetwork: true, 387 }, 388 } 389 blkutil := volumetest.NewBlockVolumePathHandler() 390 blkVolumes, err := kubelet.makeBlockVolumes(&pod, &tc.container, tc.podVolumes, blkutil) 391 // validate only the error if we expect an error 392 if tc.expectErr { 393 if err == nil || err.Error() != tc.expectedErrMsg { 394 t.Fatalf("expected error message `%s` but got `%v`", tc.expectedErrMsg, err) 395 } 396 return 397 } 398 // otherwise validate the devices 399 if err != nil { 400 t.Fatal(err) 401 } 402 assert.Equal(t, tc.expectedDevices, blkVolumes, "devices of container %+v", tc.container) 403 }) 404 } 405 }