k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/kubelet/kuberuntime/helpers_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 "testing" 22 23 "github.com/stretchr/testify/assert" 24 25 v1 "k8s.io/api/core/v1" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/types" 28 utilfeature "k8s.io/apiserver/pkg/util/feature" 29 featuregatetesting "k8s.io/component-base/featuregate/testing" 30 runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" 31 runtimetesting "k8s.io/cri-api/pkg/apis/testing" 32 "k8s.io/kubernetes/pkg/features" 33 kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" 34 "k8s.io/utils/ptr" 35 ) 36 37 type podStatusProviderFunc func(uid types.UID, name, namespace string) (*kubecontainer.PodStatus, error) 38 39 func (f podStatusProviderFunc) GetPodStatus(_ context.Context, uid types.UID, name, namespace string) (*kubecontainer.PodStatus, error) { 40 return f(uid, name, namespace) 41 } 42 43 func TestIsInitContainerFailed(t *testing.T) { 44 tests := []struct { 45 status *kubecontainer.Status 46 isFailed bool 47 description string 48 }{ 49 { 50 status: &kubecontainer.Status{ 51 State: kubecontainer.ContainerStateExited, 52 ExitCode: 1, 53 }, 54 isFailed: true, 55 description: "Init container in exited state and non-zero exit code should return true", 56 }, 57 { 58 status: &kubecontainer.Status{ 59 State: kubecontainer.ContainerStateUnknown, 60 }, 61 isFailed: true, 62 description: "Init container in unknown state should return true", 63 }, 64 { 65 status: &kubecontainer.Status{ 66 Reason: "OOMKilled", 67 ExitCode: 0, 68 }, 69 isFailed: true, 70 description: "Init container which reason is OOMKilled should return true", 71 }, 72 { 73 status: &kubecontainer.Status{ 74 State: kubecontainer.ContainerStateExited, 75 ExitCode: 0, 76 }, 77 isFailed: false, 78 description: "Init container in exited state and zero exit code should return false", 79 }, 80 { 81 status: &kubecontainer.Status{ 82 State: kubecontainer.ContainerStateRunning, 83 }, 84 isFailed: false, 85 description: "Init container in running state should return false", 86 }, 87 { 88 status: &kubecontainer.Status{ 89 State: kubecontainer.ContainerStateCreated, 90 }, 91 isFailed: false, 92 description: "Init container in created state should return false", 93 }, 94 } 95 for i, test := range tests { 96 isFailed := isInitContainerFailed(test.status) 97 assert.Equal(t, test.isFailed, isFailed, "TestCase[%d]: %s", i, test.description) 98 } 99 } 100 101 func TestStableKey(t *testing.T) { 102 container := &v1.Container{ 103 Name: "test_container", 104 Image: "foo/image:v1", 105 } 106 pod := &v1.Pod{ 107 ObjectMeta: metav1.ObjectMeta{ 108 Name: "test_pod", 109 Namespace: "test_pod_namespace", 110 UID: "test_pod_uid", 111 }, 112 Spec: v1.PodSpec{ 113 Containers: []v1.Container{*container}, 114 }, 115 } 116 oldKey := getStableKey(pod, container) 117 118 // Updating the container image should change the key. 119 container.Image = "foo/image:v2" 120 newKey := getStableKey(pod, container) 121 assert.NotEqual(t, oldKey, newKey) 122 } 123 124 func TestToKubeContainer(t *testing.T) { 125 c := &runtimeapi.Container{ 126 Id: "test-id", 127 Metadata: &runtimeapi.ContainerMetadata{ 128 Name: "test-name", 129 Attempt: 1, 130 }, 131 Image: &runtimeapi.ImageSpec{Image: "test-image"}, 132 ImageId: "test-image-id", 133 ImageRef: "test-image-ref", 134 State: runtimeapi.ContainerState_CONTAINER_RUNNING, 135 Annotations: map[string]string{ 136 containerHashLabel: "1234", 137 }, 138 } 139 expect := &kubecontainer.Container{ 140 ID: kubecontainer.ContainerID{ 141 Type: runtimetesting.FakeRuntimeName, 142 ID: "test-id", 143 }, 144 Name: "test-name", 145 ImageID: "test-image-id", 146 ImageRef: "test-image-ref", 147 Image: "test-image", 148 ImageRuntimeHandler: "", 149 Hash: uint64(0x1234), 150 State: kubecontainer.ContainerStateRunning, 151 } 152 153 _, _, m, err := createTestRuntimeManager() 154 assert.NoError(t, err) 155 got, err := m.toKubeContainer(c) 156 assert.NoError(t, err) 157 assert.Equal(t, expect, got) 158 159 // unable to convert a nil pointer to a runtime container 160 _, err = m.toKubeContainer(nil) 161 assert.Error(t, err) 162 _, err = m.sandboxToKubeContainer(nil) 163 assert.Error(t, err) 164 } 165 166 func TestToKubeContainerWithRuntimeHandlerInImageSpecCri(t *testing.T) { 167 featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RuntimeClassInImageCriAPI, true) 168 c := &runtimeapi.Container{ 169 Id: "test-id", 170 Metadata: &runtimeapi.ContainerMetadata{ 171 Name: "test-name", 172 Attempt: 1, 173 }, 174 Image: &runtimeapi.ImageSpec{Image: "test-image", RuntimeHandler: "test-runtimeHandler"}, 175 ImageId: "test-image-id", 176 ImageRef: "test-image-ref", 177 State: runtimeapi.ContainerState_CONTAINER_RUNNING, 178 Annotations: map[string]string{ 179 containerHashLabel: "1234", 180 }, 181 } 182 expect := &kubecontainer.Container{ 183 ID: kubecontainer.ContainerID{ 184 Type: runtimetesting.FakeRuntimeName, 185 ID: "test-id", 186 }, 187 Name: "test-name", 188 ImageID: "test-image-id", 189 ImageRef: "test-image-ref", 190 Image: "test-image", 191 ImageRuntimeHandler: "test-runtimeHandler", 192 Hash: uint64(0x1234), 193 State: kubecontainer.ContainerStateRunning, 194 } 195 196 _, _, m, err := createTestRuntimeManager() 197 assert.NoError(t, err) 198 got, err := m.toKubeContainer(c) 199 assert.NoError(t, err) 200 assert.Equal(t, expect, got) 201 202 // unable to convert a nil pointer to a runtime container 203 _, err = m.toKubeContainer(nil) 204 assert.Error(t, err) 205 _, err = m.sandboxToKubeContainer(nil) 206 assert.Error(t, err) 207 } 208 209 func TestGetImageUser(t *testing.T) { 210 _, i, m, err := createTestRuntimeManager() 211 assert.NoError(t, err) 212 213 type image struct { 214 name string 215 uid *runtimeapi.Int64Value 216 username string 217 } 218 219 type imageUserValues struct { 220 // getImageUser can return (*int64)(nil) so comparing with *uid will break 221 // type cannot be *int64 as Golang does not allow to take the address of a numeric constant" 222 uid interface{} 223 username string 224 err error 225 } 226 227 tests := []struct { 228 description string 229 originalImage image 230 expectedImageUserValues imageUserValues 231 }{ 232 { 233 "image without username and uid should return (new(int64), \"\", nil)", 234 image{ 235 name: "test-image-ref1", 236 uid: (*runtimeapi.Int64Value)(nil), 237 username: "", 238 }, 239 imageUserValues{ 240 uid: int64(0), 241 username: "", 242 err: nil, 243 }, 244 }, 245 { 246 "image with username and no uid should return ((*int64)nil, imageStatus.Username, nil)", 247 image{ 248 name: "test-image-ref2", 249 uid: (*runtimeapi.Int64Value)(nil), 250 username: "testUser", 251 }, 252 imageUserValues{ 253 uid: (*int64)(nil), 254 username: "testUser", 255 err: nil, 256 }, 257 }, 258 { 259 "image with uid should return (*int64, \"\", nil)", 260 image{ 261 name: "test-image-ref3", 262 uid: &runtimeapi.Int64Value{ 263 Value: 2, 264 }, 265 username: "whatever", 266 }, 267 imageUserValues{ 268 uid: int64(2), 269 username: "", 270 err: nil, 271 }, 272 }, 273 } 274 275 i.SetFakeImages([]string{"test-image-ref1", "test-image-ref2", "test-image-ref3"}) 276 for j, test := range tests { 277 ctx := context.Background() 278 i.Images[test.originalImage.name].Username = test.originalImage.username 279 i.Images[test.originalImage.name].Uid = test.originalImage.uid 280 281 uid, username, err := m.getImageUser(ctx, test.originalImage.name) 282 assert.NoError(t, err, "TestCase[%d]", j) 283 284 if test.expectedImageUserValues.uid == (*int64)(nil) { 285 assert.Equal(t, test.expectedImageUserValues.uid, uid, "TestCase[%d]", j) 286 } else { 287 assert.Equal(t, test.expectedImageUserValues.uid, *uid, "TestCase[%d]", j) 288 } 289 assert.Equal(t, test.expectedImageUserValues.username, username, "TestCase[%d]", j) 290 } 291 } 292 293 func TestToRuntimeProtocol(t *testing.T) { 294 for _, test := range []struct { 295 name string 296 protocol string 297 expected runtimeapi.Protocol 298 }{ 299 { 300 name: "TCP protocol", 301 protocol: "TCP", 302 expected: runtimeapi.Protocol_TCP, 303 }, 304 { 305 name: "UDP protocol", 306 protocol: "UDP", 307 expected: runtimeapi.Protocol_UDP, 308 }, 309 { 310 name: "SCTP protocol", 311 protocol: "SCTP", 312 expected: runtimeapi.Protocol_SCTP, 313 }, 314 { 315 name: "unknown protocol", 316 protocol: "unknown", 317 expected: runtimeapi.Protocol_TCP, 318 }, 319 } { 320 t.Run(test.name, func(t *testing.T) { 321 if result := toRuntimeProtocol(v1.Protocol(test.protocol)); result != test.expected { 322 t.Errorf("expected %d but got %d", test.expected, result) 323 } 324 }) 325 } 326 } 327 328 func TestToKubeContainerState(t *testing.T) { 329 for _, test := range []struct { 330 name string 331 state int32 332 expected kubecontainer.State 333 }{ 334 { 335 name: "container created", 336 state: 0, 337 expected: kubecontainer.ContainerStateCreated, 338 }, 339 { 340 name: "container running", 341 state: 1, 342 expected: kubecontainer.ContainerStateRunning, 343 }, 344 { 345 name: "container exited", 346 state: 2, 347 expected: kubecontainer.ContainerStateExited, 348 }, 349 { 350 name: "unknown state", 351 state: 3, 352 expected: kubecontainer.ContainerStateUnknown, 353 }, 354 { 355 name: "not supported state", 356 state: 4, 357 expected: kubecontainer.ContainerStateUnknown, 358 }, 359 } { 360 t.Run(test.name, func(t *testing.T) { 361 if result := toKubeContainerState(runtimeapi.ContainerState(test.state)); result != test.expected { 362 t.Errorf("expected %s but got %s", test.expected, result) 363 } 364 }) 365 } 366 } 367 368 func TestGetAppArmorProfile(t *testing.T) { 369 tests := []struct { 370 name string 371 podProfile *v1.AppArmorProfile 372 expectedProfile *runtimeapi.SecurityProfile 373 expectedOldProfile string 374 expectError bool 375 }{{ 376 name: "no appArmor", 377 expectedProfile: nil, 378 }, { 379 name: "runtime default", 380 podProfile: &v1.AppArmorProfile{Type: v1.AppArmorProfileTypeRuntimeDefault}, 381 expectedProfile: &runtimeapi.SecurityProfile{ 382 ProfileType: runtimeapi.SecurityProfile_RuntimeDefault, 383 }, 384 expectedOldProfile: "runtime/default", 385 }, { 386 name: "unconfined", 387 podProfile: &v1.AppArmorProfile{Type: v1.AppArmorProfileTypeUnconfined}, 388 expectedProfile: &runtimeapi.SecurityProfile{ 389 ProfileType: runtimeapi.SecurityProfile_Unconfined, 390 }, 391 expectedOldProfile: "unconfined", 392 }, { 393 name: "localhost", 394 podProfile: &v1.AppArmorProfile{ 395 Type: v1.AppArmorProfileTypeLocalhost, 396 LocalhostProfile: ptr.To("test"), 397 }, 398 expectedProfile: &runtimeapi.SecurityProfile{ 399 ProfileType: runtimeapi.SecurityProfile_Localhost, 400 LocalhostRef: "test", 401 }, 402 expectedOldProfile: "localhost/test", 403 }, { 404 name: "invalid localhost", 405 podProfile: &v1.AppArmorProfile{ 406 Type: v1.AppArmorProfileTypeLocalhost, 407 }, 408 expectError: true, 409 }, { 410 name: "invalid type", 411 podProfile: &v1.AppArmorProfile{ 412 Type: "foo", 413 }, 414 expectError: true, 415 }} 416 417 for _, test := range tests { 418 t.Run(test.name, func(t *testing.T) { 419 pod := v1.Pod{ 420 ObjectMeta: metav1.ObjectMeta{ 421 Name: "bar", 422 }, 423 Spec: v1.PodSpec{ 424 SecurityContext: &v1.PodSecurityContext{ 425 AppArmorProfile: test.podProfile, 426 }, 427 Containers: []v1.Container{{Name: "foo"}}, 428 }, 429 } 430 431 actual, actualOld, err := getAppArmorProfile(&pod, &pod.Spec.Containers[0]) 432 433 if test.expectError { 434 assert.Error(t, err) 435 } else { 436 assert.NoError(t, err) 437 } 438 439 assert.Equal(t, test.expectedProfile, actual, "AppArmor profile") 440 assert.Equal(t, test.expectedOldProfile, actualOld, "old (deprecated) profile string") 441 }) 442 } 443 }