k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/kubelet/kuberuntime/kuberuntime_sandbox_test.go (about) 1 /* 2 Copyright 2016 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 kuberuntime 18 19 import ( 20 "context" 21 "fmt" 22 "os" 23 "path/filepath" 24 "testing" 25 26 "github.com/stretchr/testify/assert" 27 "github.com/stretchr/testify/require" 28 v1 "k8s.io/api/core/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 utilfeature "k8s.io/apiserver/pkg/util/feature" 31 featuregatetesting "k8s.io/component-base/featuregate/testing" 32 runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" 33 "k8s.io/kubernetes/pkg/features" 34 containertest "k8s.io/kubernetes/pkg/kubelet/container/testing" 35 "k8s.io/kubernetes/pkg/kubelet/runtimeclass" 36 rctest "k8s.io/kubernetes/pkg/kubelet/runtimeclass/testing" 37 "k8s.io/utils/pointer" 38 ) 39 40 const testPodLogsDirectory = "/var/log/pods" 41 42 func TestGeneratePodSandboxConfig(t *testing.T) { 43 _, _, m, err := createTestRuntimeManager() 44 require.NoError(t, err) 45 pod := newTestPod() 46 47 expectedLogDirectory := filepath.Join(testPodLogsDirectory, pod.Namespace+"_"+pod.Name+"_12345678") 48 expectedLabels := map[string]string{ 49 "io.kubernetes.pod.name": pod.Name, 50 "io.kubernetes.pod.namespace": pod.Namespace, 51 "io.kubernetes.pod.uid": string(pod.UID), 52 } 53 expectedMetadata := &runtimeapi.PodSandboxMetadata{ 54 Name: pod.Name, 55 Namespace: pod.Namespace, 56 Uid: string(pod.UID), 57 Attempt: uint32(1), 58 } 59 expectedPortMappings := []*runtimeapi.PortMapping{ 60 { 61 HostPort: 8080, 62 }, 63 } 64 65 podSandboxConfig, err := m.generatePodSandboxConfig(pod, 1) 66 assert.NoError(t, err) 67 assert.Equal(t, expectedLabels, podSandboxConfig.Labels) 68 assert.Equal(t, expectedLogDirectory, podSandboxConfig.LogDirectory) 69 assert.Equal(t, expectedMetadata, podSandboxConfig.Metadata) 70 assert.Equal(t, expectedPortMappings, podSandboxConfig.PortMappings) 71 } 72 73 // TestCreatePodSandbox tests creating sandbox and its corresponding pod log directory. 74 func TestCreatePodSandbox(t *testing.T) { 75 ctx := context.Background() 76 fakeRuntime, _, m, err := createTestRuntimeManager() 77 require.NoError(t, err) 78 pod := newTestPod() 79 80 fakeOS := m.osInterface.(*containertest.FakeOS) 81 fakeOS.MkdirAllFn = func(path string, perm os.FileMode) error { 82 // Check pod logs root directory is created. 83 assert.Equal(t, filepath.Join(testPodLogsDirectory, pod.Namespace+"_"+pod.Name+"_12345678"), path) 84 assert.Equal(t, os.FileMode(0755), perm) 85 return nil 86 } 87 id, _, err := m.createPodSandbox(ctx, pod, 1) 88 assert.NoError(t, err) 89 assert.Contains(t, fakeRuntime.Called, "RunPodSandbox") 90 sandboxes, err := fakeRuntime.ListPodSandbox(ctx, &runtimeapi.PodSandboxFilter{Id: id}) 91 assert.NoError(t, err) 92 assert.Equal(t, len(sandboxes), 1) 93 assert.Equal(t, sandboxes[0].Id, fmt.Sprintf("%s_%s_%s_1", pod.Name, pod.Namespace, pod.UID)) 94 assert.Equal(t, sandboxes[0].State, runtimeapi.PodSandboxState_SANDBOX_READY) 95 } 96 97 func TestGeneratePodSandboxLinuxConfigSeccomp(t *testing.T) { 98 _, _, m, err := createTestRuntimeManager() 99 require.NoError(t, err) 100 101 tests := []struct { 102 description string 103 pod *v1.Pod 104 expectedProfile v1.SeccompProfileType 105 }{ 106 { 107 description: "no seccomp defined at pod level should return runtime/default", 108 pod: newSeccompPod(nil, nil, "", "runtime/default"), 109 expectedProfile: v1.SeccompProfileTypeRuntimeDefault, 110 }, 111 { 112 description: "seccomp field defined at pod level should not be honoured", 113 pod: newSeccompPod(&v1.SeccompProfile{Type: v1.SeccompProfileTypeUnconfined}, nil, "", ""), 114 expectedProfile: v1.SeccompProfileTypeRuntimeDefault, 115 }, 116 { 117 description: "seccomp field defined at container level should not be honoured", 118 pod: newSeccompPod(nil, &v1.SeccompProfile{Type: v1.SeccompProfileTypeUnconfined}, "", ""), 119 expectedProfile: v1.SeccompProfileTypeRuntimeDefault, 120 }, 121 { 122 description: "seccomp annotation defined at pod level should not be honoured", 123 pod: newSeccompPod(nil, nil, "unconfined", ""), 124 expectedProfile: v1.SeccompProfileTypeRuntimeDefault, 125 }, 126 { 127 description: "seccomp annotation defined at container level should not be honoured", 128 pod: newSeccompPod(nil, nil, "", "unconfined"), 129 expectedProfile: v1.SeccompProfileTypeRuntimeDefault, 130 }, 131 } 132 133 for i, test := range tests { 134 config, _ := m.generatePodSandboxLinuxConfig(test.pod) 135 actualProfile := config.SecurityContext.Seccomp.ProfileType.String() 136 assert.EqualValues(t, test.expectedProfile, actualProfile, "TestCase[%d]: %s", i, test.description) 137 } 138 } 139 140 // TestCreatePodSandbox_RuntimeClass tests creating sandbox with RuntimeClasses enabled. 141 func TestCreatePodSandbox_RuntimeClass(t *testing.T) { 142 ctx := context.Background() 143 rcm := runtimeclass.NewManager(rctest.NewPopulatedClient()) 144 defer rctest.StartManagerSync(rcm)() 145 146 fakeRuntime, _, m, err := createTestRuntimeManager() 147 require.NoError(t, err) 148 m.runtimeClassManager = rcm 149 150 tests := map[string]struct { 151 rcn *string 152 expectedHandler string 153 expectError bool 154 }{ 155 "unspecified RuntimeClass": {rcn: nil, expectedHandler: ""}, 156 "valid RuntimeClass": {rcn: pointer.String(rctest.SandboxRuntimeClass), expectedHandler: rctest.SandboxRuntimeHandler}, 157 "missing RuntimeClass": {rcn: pointer.String("phantom"), expectError: true}, 158 } 159 for name, test := range tests { 160 t.Run(name, func(t *testing.T) { 161 fakeRuntime.Called = []string{} 162 pod := newTestPod() 163 pod.Spec.RuntimeClassName = test.rcn 164 165 id, _, err := m.createPodSandbox(ctx, pod, 1) 166 if test.expectError { 167 assert.Error(t, err) 168 } else { 169 assert.NoError(t, err) 170 assert.Contains(t, fakeRuntime.Called, "RunPodSandbox") 171 assert.Equal(t, test.expectedHandler, fakeRuntime.Sandboxes[id].RuntimeHandler) 172 } 173 }) 174 } 175 } 176 177 func newTestPod() *v1.Pod { 178 return &v1.Pod{ 179 ObjectMeta: metav1.ObjectMeta{ 180 UID: "12345678", 181 Name: "bar", 182 Namespace: "new", 183 }, 184 Spec: v1.PodSpec{ 185 Containers: []v1.Container{ 186 { 187 Name: "foo", 188 Image: "busybox", 189 ImagePullPolicy: v1.PullIfNotPresent, 190 Ports: []v1.ContainerPort{ 191 { 192 HostPort: 8080, 193 }, 194 }, 195 }, 196 }, 197 }, 198 } 199 } 200 201 func newSeccompPod(podFieldProfile, containerFieldProfile *v1.SeccompProfile, podAnnotationProfile, containerAnnotationProfile string) *v1.Pod { 202 pod := newTestPod() 203 if podFieldProfile != nil { 204 pod.Spec.SecurityContext = &v1.PodSecurityContext{ 205 SeccompProfile: podFieldProfile, 206 } 207 } 208 if containerFieldProfile != nil { 209 pod.Spec.Containers[0].SecurityContext = &v1.SecurityContext{ 210 SeccompProfile: containerFieldProfile, 211 } 212 } 213 return pod 214 } 215 216 func TestGeneratePodSandboxWindowsConfig_HostProcess(t *testing.T) { 217 _, _, m, err := createTestRuntimeManager() 218 require.NoError(t, err) 219 220 const containerName = "container" 221 gmsaCreds := "gmsa-creds" 222 userName := "SYSTEM" 223 trueVar := true 224 falseVar := false 225 226 testCases := []struct { 227 name string 228 podSpec *v1.PodSpec 229 expectedWindowsConfig *runtimeapi.WindowsPodSandboxConfig 230 expectedError error 231 }{ 232 { 233 name: "Empty PodSecurityContext", 234 podSpec: &v1.PodSpec{ 235 Containers: []v1.Container{{ 236 Name: containerName, 237 }}, 238 }, 239 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{ 240 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{}, 241 }, 242 expectedError: nil, 243 }, 244 { 245 name: "GMSACredentialSpec in PodSecurityContext", 246 podSpec: &v1.PodSpec{ 247 SecurityContext: &v1.PodSecurityContext{ 248 WindowsOptions: &v1.WindowsSecurityContextOptions{ 249 GMSACredentialSpec: &gmsaCreds, 250 }, 251 }, 252 Containers: []v1.Container{{ 253 Name: containerName, 254 }}, 255 }, 256 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{ 257 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{ 258 CredentialSpec: "gmsa-creds", 259 }, 260 }, 261 expectedError: nil, 262 }, 263 { 264 name: "RunAsUserName in PodSecurityContext", 265 podSpec: &v1.PodSpec{ 266 SecurityContext: &v1.PodSecurityContext{ 267 WindowsOptions: &v1.WindowsSecurityContextOptions{ 268 RunAsUserName: &userName, 269 }, 270 }, 271 Containers: []v1.Container{{ 272 Name: containerName, 273 }}, 274 }, 275 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{ 276 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{ 277 RunAsUsername: "SYSTEM", 278 }, 279 }, 280 expectedError: nil, 281 }, 282 { 283 name: "Pod with HostProcess containers and non-HostProcess containers", 284 podSpec: &v1.PodSpec{ 285 SecurityContext: &v1.PodSecurityContext{ 286 WindowsOptions: &v1.WindowsSecurityContextOptions{ 287 HostProcess: &trueVar, 288 }, 289 }, 290 Containers: []v1.Container{{ 291 Name: containerName, 292 }, { 293 Name: containerName, 294 SecurityContext: &v1.SecurityContext{ 295 WindowsOptions: &v1.WindowsSecurityContextOptions{ 296 HostProcess: &falseVar, 297 }, 298 }, 299 }}, 300 }, 301 expectedWindowsConfig: nil, 302 expectedError: fmt.Errorf("pod must not contain both HostProcess and non-HostProcess containers"), 303 }, 304 { 305 name: "Pod with HostProcess containers and HostNetwork not set", 306 podSpec: &v1.PodSpec{ 307 SecurityContext: &v1.PodSecurityContext{ 308 WindowsOptions: &v1.WindowsSecurityContextOptions{ 309 HostProcess: &trueVar, 310 }, 311 }, 312 Containers: []v1.Container{{ 313 Name: containerName, 314 }}, 315 }, 316 expectedWindowsConfig: nil, 317 expectedError: fmt.Errorf("hostNetwork is required if Pod contains HostProcess containers"), 318 }, 319 { 320 name: "Pod with HostProcess containers and HostNetwork set", 321 podSpec: &v1.PodSpec{ 322 HostNetwork: true, 323 SecurityContext: &v1.PodSecurityContext{ 324 WindowsOptions: &v1.WindowsSecurityContextOptions{ 325 HostProcess: &trueVar, 326 }, 327 }, 328 Containers: []v1.Container{{ 329 Name: containerName, 330 }}, 331 }, 332 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{ 333 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{ 334 HostProcess: true, 335 }, 336 }, 337 expectedError: nil, 338 }, 339 { 340 name: "Pod's WindowsOptions.HostProcess set to false and pod has HostProcess containers", 341 podSpec: &v1.PodSpec{ 342 HostNetwork: true, 343 SecurityContext: &v1.PodSecurityContext{ 344 WindowsOptions: &v1.WindowsSecurityContextOptions{ 345 HostProcess: &falseVar, 346 }, 347 }, 348 Containers: []v1.Container{{ 349 Name: containerName, 350 SecurityContext: &v1.SecurityContext{ 351 WindowsOptions: &v1.WindowsSecurityContextOptions{ 352 HostProcess: &trueVar, 353 }, 354 }, 355 }}, 356 }, 357 expectedWindowsConfig: nil, 358 expectedError: fmt.Errorf("pod must not contain any HostProcess containers if Pod's WindowsOptions.HostProcess is set to false"), 359 }, 360 { 361 name: "Pod's security context doesn't specify HostProcess containers but Container's security context does", 362 podSpec: &v1.PodSpec{ 363 HostNetwork: true, 364 Containers: []v1.Container{{ 365 Name: containerName, 366 SecurityContext: &v1.SecurityContext{ 367 WindowsOptions: &v1.WindowsSecurityContextOptions{ 368 HostProcess: &trueVar, 369 }, 370 }, 371 }}, 372 }, 373 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{ 374 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{ 375 HostProcess: true, 376 }, 377 }, 378 expectedError: nil, 379 }, 380 } 381 382 for _, testCase := range testCases { 383 t.Run(testCase.name, func(t *testing.T) { 384 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WindowsHostNetwork, false) 385 pod := &v1.Pod{} 386 pod.Spec = *testCase.podSpec 387 388 wc, err := m.generatePodSandboxWindowsConfig(pod) 389 390 assert.Equal(t, testCase.expectedWindowsConfig, wc) 391 assert.Equal(t, testCase.expectedError, err) 392 }) 393 } 394 } 395 396 func TestGeneratePodSandboxWindowsConfig_HostNetwork(t *testing.T) { 397 _, _, m, err := createTestRuntimeManager() 398 require.NoError(t, err) 399 400 const containerName = "container" 401 402 testCases := []struct { 403 name string 404 hostNetworkFeatureEnabled bool 405 podSpec *v1.PodSpec 406 expectedWindowsConfig *runtimeapi.WindowsPodSandboxConfig 407 }{ 408 { 409 name: "feature disabled, hostNetwork=false", 410 hostNetworkFeatureEnabled: false, 411 podSpec: &v1.PodSpec{ 412 HostNetwork: false, 413 Containers: []v1.Container{{Name: containerName}}, 414 }, 415 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{ 416 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{}, 417 }, 418 }, 419 { 420 name: "feature disabled, hostNetwork=true", 421 hostNetworkFeatureEnabled: false, 422 podSpec: &v1.PodSpec{ 423 HostNetwork: true, 424 Containers: []v1.Container{{Name: containerName}}, 425 }, 426 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{ 427 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{}, 428 }}, 429 { 430 name: "feature enabled, hostNetwork=false", 431 hostNetworkFeatureEnabled: true, 432 podSpec: &v1.PodSpec{ 433 HostNetwork: false, 434 Containers: []v1.Container{{Name: containerName}}, 435 }, 436 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{ 437 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{ 438 NamespaceOptions: &runtimeapi.WindowsNamespaceOption{ 439 Network: runtimeapi.NamespaceMode_POD, 440 }, 441 }, 442 }, 443 }, 444 { 445 name: "feature enabled, hostNetwork=true", 446 hostNetworkFeatureEnabled: true, 447 podSpec: &v1.PodSpec{ 448 HostNetwork: true, 449 Containers: []v1.Container{{Name: containerName}}, 450 }, 451 expectedWindowsConfig: &runtimeapi.WindowsPodSandboxConfig{ 452 SecurityContext: &runtimeapi.WindowsSandboxSecurityContext{ 453 NamespaceOptions: &runtimeapi.WindowsNamespaceOption{ 454 Network: runtimeapi.NamespaceMode_NODE, 455 }, 456 }, 457 }, 458 }, 459 } 460 461 for _, testCase := range testCases { 462 t.Run(testCase.name, func(t *testing.T) { 463 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WindowsHostNetwork, testCase.hostNetworkFeatureEnabled) 464 pod := &v1.Pod{} 465 pod.Spec = *testCase.podSpec 466 467 wc, err := m.generatePodSandboxWindowsConfig(pod) 468 469 assert.Equal(t, testCase.expectedWindowsConfig, wc) 470 assert.Equal(t, nil, err) 471 }) 472 } 473 }