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