k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/kubelet/kuberuntime/kuberuntime_image_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 "encoding/json" 22 "fmt" 23 "testing" 24 25 "github.com/stretchr/testify/assert" 26 "github.com/stretchr/testify/require" 27 28 v1 "k8s.io/api/core/v1" 29 "k8s.io/apimachinery/pkg/util/sets" 30 runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" 31 "k8s.io/kubernetes/pkg/credentialprovider" 32 kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" 33 ) 34 35 func TestPullImage(t *testing.T) { 36 ctx := context.Background() 37 _, _, fakeManager, err := createTestRuntimeManager() 38 assert.NoError(t, err) 39 40 imageRef, err := fakeManager.PullImage(ctx, kubecontainer.ImageSpec{Image: "busybox"}, nil, nil) 41 assert.NoError(t, err) 42 assert.Equal(t, "busybox", imageRef) 43 44 images, err := fakeManager.ListImages(ctx) 45 assert.NoError(t, err) 46 assert.Equal(t, 1, len(images)) 47 assert.Equal(t, images[0].RepoTags, []string{"busybox"}) 48 } 49 50 func TestPullImageWithError(t *testing.T) { 51 ctx := context.Background() 52 _, fakeImageService, fakeManager, err := createTestRuntimeManager() 53 assert.NoError(t, err) 54 55 // trying to pull an image with an invalid name should return an error 56 imageRef, err := fakeManager.PullImage(ctx, kubecontainer.ImageSpec{Image: ":invalid"}, nil, nil) 57 assert.Error(t, err) 58 assert.Equal(t, "", imageRef) 59 60 fakeImageService.InjectError("PullImage", fmt.Errorf("test-error")) 61 imageRef, err = fakeManager.PullImage(ctx, kubecontainer.ImageSpec{Image: "busybox"}, nil, nil) 62 assert.Error(t, err) 63 assert.Equal(t, "", imageRef) 64 65 images, err := fakeManager.ListImages(ctx) 66 assert.NoError(t, err) 67 assert.Equal(t, 0, len(images)) 68 } 69 70 func TestPullImageWithInvalidImageName(t *testing.T) { 71 _, fakeImageService, fakeManager, err := createTestRuntimeManager() 72 assert.NoError(t, err) 73 74 imageList := []string{"FAIL", "http://fail", "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"} 75 fakeImageService.SetFakeImages(imageList) 76 for _, val := range imageList { 77 ctx := context.Background() 78 imageRef, err := fakeManager.PullImage(ctx, kubecontainer.ImageSpec{Image: val}, nil, nil) 79 assert.Error(t, err) 80 assert.Equal(t, "", imageRef) 81 82 } 83 } 84 85 func TestListImages(t *testing.T) { 86 ctx := context.Background() 87 _, fakeImageService, fakeManager, err := createTestRuntimeManager() 88 assert.NoError(t, err) 89 90 images := []string{"1111", "2222", "3333"} 91 expected := sets.NewString(images...) 92 fakeImageService.SetFakeImages(images) 93 94 actualImages, err := fakeManager.ListImages(ctx) 95 assert.NoError(t, err) 96 actual := sets.NewString() 97 for _, i := range actualImages { 98 actual.Insert(i.ID) 99 } 100 101 assert.Equal(t, expected.List(), actual.List()) 102 } 103 104 func TestListImagesPinnedField(t *testing.T) { 105 ctx := context.Background() 106 _, fakeImageService, fakeManager, err := createTestRuntimeManager() 107 assert.NoError(t, err) 108 109 imagesPinned := map[string]bool{ 110 "1111": false, 111 "2222": true, 112 "3333": false, 113 } 114 imageList := []string{} 115 for image, pinned := range imagesPinned { 116 fakeImageService.SetFakeImagePinned(image, pinned) 117 imageList = append(imageList, image) 118 } 119 fakeImageService.SetFakeImages(imageList) 120 121 actualImages, err := fakeManager.ListImages(ctx) 122 assert.NoError(t, err) 123 for _, image := range actualImages { 124 assert.Equal(t, imagesPinned[image.ID], image.Pinned) 125 } 126 } 127 128 func TestListImagesWithError(t *testing.T) { 129 ctx := context.Background() 130 _, fakeImageService, fakeManager, err := createTestRuntimeManager() 131 assert.NoError(t, err) 132 133 fakeImageService.InjectError("ListImages", fmt.Errorf("test-failure")) 134 135 actualImages, err := fakeManager.ListImages(ctx) 136 assert.Error(t, err) 137 assert.Nil(t, actualImages) 138 } 139 140 func TestGetImageRef(t *testing.T) { 141 ctx := context.Background() 142 _, fakeImageService, fakeManager, err := createTestRuntimeManager() 143 assert.NoError(t, err) 144 145 image := "busybox" 146 fakeImageService.SetFakeImages([]string{image}) 147 imageRef, err := fakeManager.GetImageRef(ctx, kubecontainer.ImageSpec{Image: image}) 148 assert.NoError(t, err) 149 assert.Equal(t, image, imageRef) 150 } 151 152 func TestImageSize(t *testing.T) { 153 ctx := context.Background() 154 _, fakeImageService, fakeManager, err := createTestRuntimeManager() 155 assert.NoError(t, err) 156 157 const imageSize = uint64(64) 158 fakeImageService.SetFakeImageSize(imageSize) 159 image := "busybox" 160 fakeImageService.SetFakeImages([]string{image}) 161 actualSize, err := fakeManager.GetImageSize(ctx, kubecontainer.ImageSpec{Image: image}) 162 assert.NoError(t, err) 163 assert.Equal(t, imageSize, actualSize) 164 } 165 166 func TestGetImageRefImageNotAvailableLocally(t *testing.T) { 167 ctx := context.Background() 168 _, _, fakeManager, err := createTestRuntimeManager() 169 assert.NoError(t, err) 170 171 image := "busybox" 172 173 imageRef, err := fakeManager.GetImageRef(ctx, kubecontainer.ImageSpec{Image: image}) 174 assert.NoError(t, err) 175 176 imageNotAvailableLocallyRef := "" 177 assert.Equal(t, imageNotAvailableLocallyRef, imageRef) 178 } 179 180 func TestGetImageRefWithError(t *testing.T) { 181 ctx := context.Background() 182 _, fakeImageService, fakeManager, err := createTestRuntimeManager() 183 assert.NoError(t, err) 184 185 image := "busybox" 186 187 fakeImageService.InjectError("ImageStatus", fmt.Errorf("test-error")) 188 189 imageRef, err := fakeManager.GetImageRef(ctx, kubecontainer.ImageSpec{Image: image}) 190 assert.Error(t, err) 191 assert.Equal(t, "", imageRef) 192 } 193 194 func TestRemoveImage(t *testing.T) { 195 ctx := context.Background() 196 _, fakeImageService, fakeManager, err := createTestRuntimeManager() 197 assert.NoError(t, err) 198 199 _, err = fakeManager.PullImage(ctx, kubecontainer.ImageSpec{Image: "busybox"}, nil, nil) 200 assert.NoError(t, err) 201 assert.Equal(t, 1, len(fakeImageService.Images)) 202 203 err = fakeManager.RemoveImage(ctx, kubecontainer.ImageSpec{Image: "busybox"}) 204 assert.NoError(t, err) 205 assert.Equal(t, 0, len(fakeImageService.Images)) 206 } 207 208 func TestRemoveImageNoOpIfImageNotLocal(t *testing.T) { 209 ctx := context.Background() 210 _, _, fakeManager, err := createTestRuntimeManager() 211 assert.NoError(t, err) 212 213 err = fakeManager.RemoveImage(ctx, kubecontainer.ImageSpec{Image: "busybox"}) 214 assert.NoError(t, err) 215 } 216 217 func TestRemoveImageWithError(t *testing.T) { 218 ctx := context.Background() 219 _, fakeImageService, fakeManager, err := createTestRuntimeManager() 220 assert.NoError(t, err) 221 222 _, err = fakeManager.PullImage(ctx, kubecontainer.ImageSpec{Image: "busybox"}, nil, nil) 223 assert.NoError(t, err) 224 assert.Equal(t, 1, len(fakeImageService.Images)) 225 226 fakeImageService.InjectError("RemoveImage", fmt.Errorf("test-failure")) 227 228 err = fakeManager.RemoveImage(ctx, kubecontainer.ImageSpec{Image: "busybox"}) 229 assert.Error(t, err) 230 assert.Equal(t, 1, len(fakeImageService.Images)) 231 } 232 233 func TestImageStats(t *testing.T) { 234 ctx := context.Background() 235 _, fakeImageService, fakeManager, err := createTestRuntimeManager() 236 assert.NoError(t, err) 237 238 const imageSize = 64 239 fakeImageService.SetFakeImageSize(imageSize) 240 images := []string{"1111", "2222", "3333"} 241 fakeImageService.SetFakeImages(images) 242 243 actualStats, err := fakeManager.ImageStats(ctx) 244 assert.NoError(t, err) 245 expectedStats := &kubecontainer.ImageStats{TotalStorageBytes: imageSize * uint64(len(images))} 246 assert.Equal(t, expectedStats, actualStats) 247 } 248 249 func TestImageStatsWithError(t *testing.T) { 250 ctx := context.Background() 251 _, fakeImageService, fakeManager, err := createTestRuntimeManager() 252 assert.NoError(t, err) 253 254 fakeImageService.InjectError("ListImages", fmt.Errorf("test-failure")) 255 256 actualImageStats, err := fakeManager.ImageStats(ctx) 257 assert.Error(t, err) 258 assert.Nil(t, actualImageStats) 259 } 260 261 func TestPullWithSecrets(t *testing.T) { 262 ctx := context.Background() 263 // auth value is equivalent to: "username":"passed-user","password":"passed-password" 264 dockerCfg := map[string]map[string]string{"index.docker.io/v1/": {"email": "passed-email", "auth": "cGFzc2VkLXVzZXI6cGFzc2VkLXBhc3N3b3Jk"}} 265 dockercfgContent, err := json.Marshal(dockerCfg) 266 if err != nil { 267 t.Errorf("unexpected error: %v", err) 268 } 269 270 dockerConfigJSON := map[string]map[string]map[string]string{"auths": dockerCfg} 271 dockerConfigJSONContent, err := json.Marshal(dockerConfigJSON) 272 if err != nil { 273 t.Errorf("unexpected error: %v", err) 274 } 275 276 tests := map[string]struct { 277 imageName string 278 passedSecrets []v1.Secret 279 builtInDockerConfig credentialprovider.DockerConfig 280 expectedAuth *runtimeapi.AuthConfig 281 }{ 282 "no matching secrets": { 283 "ubuntu", 284 []v1.Secret{}, 285 credentialprovider.DockerConfig(map[string]credentialprovider.DockerConfigEntry{}), 286 nil, 287 }, 288 "default keyring secrets": { 289 "ubuntu", 290 []v1.Secret{}, 291 credentialprovider.DockerConfig(map[string]credentialprovider.DockerConfigEntry{ 292 "index.docker.io/v1/": {Username: "built-in", Password: "password", Provider: nil}, 293 }), 294 &runtimeapi.AuthConfig{Username: "built-in", Password: "password"}, 295 }, 296 "default keyring secrets unused": { 297 "ubuntu", 298 []v1.Secret{}, 299 credentialprovider.DockerConfig(map[string]credentialprovider.DockerConfigEntry{ 300 "extraneous": {Username: "built-in", Password: "password", Provider: nil}, 301 }), 302 nil, 303 }, 304 "builtin keyring secrets, but use passed": { 305 "ubuntu", 306 []v1.Secret{{Type: v1.SecretTypeDockercfg, Data: map[string][]byte{v1.DockerConfigKey: dockercfgContent}}}, 307 credentialprovider.DockerConfig(map[string]credentialprovider.DockerConfigEntry{ 308 "index.docker.io/v1/": {Username: "built-in", Password: "password", Provider: nil}, 309 }), 310 &runtimeapi.AuthConfig{Username: "passed-user", Password: "passed-password"}, 311 }, 312 "builtin keyring secrets, but use passed with new docker config": { 313 "ubuntu", 314 []v1.Secret{{Type: v1.SecretTypeDockerConfigJson, Data: map[string][]byte{v1.DockerConfigJsonKey: dockerConfigJSONContent}}}, 315 credentialprovider.DockerConfig(map[string]credentialprovider.DockerConfigEntry{ 316 "index.docker.io/v1/": {Username: "built-in", Password: "password", Provider: nil}, 317 }), 318 &runtimeapi.AuthConfig{Username: "passed-user", Password: "passed-password"}, 319 }, 320 } 321 for description, test := range tests { 322 builtInKeyRing := &credentialprovider.BasicDockerKeyring{} 323 builtInKeyRing.Add(test.builtInDockerConfig) 324 _, fakeImageService, fakeManager, err := customTestRuntimeManager(builtInKeyRing) 325 require.NoError(t, err) 326 327 _, err = fakeManager.PullImage(ctx, kubecontainer.ImageSpec{Image: test.imageName}, test.passedSecrets, nil) 328 require.NoError(t, err) 329 fakeImageService.AssertImagePulledWithAuth(t, &runtimeapi.ImageSpec{Image: test.imageName, Annotations: make(map[string]string)}, test.expectedAuth, description) 330 } 331 } 332 333 func TestPullWithSecretsWithError(t *testing.T) { 334 ctx := context.Background() 335 336 dockerCfg := map[string]map[string]map[string]string{ 337 "auths": { 338 "index.docker.io/v1/": { 339 "email": "passed-email", 340 "auth": "cGFzc2VkLXVzZXI6cGFzc2VkLXBhc3N3b3Jk", 341 }, 342 }, 343 } 344 345 dockerConfigJSON, err := json.Marshal(dockerCfg) 346 if err != nil { 347 t.Fatal(err) 348 } 349 350 for _, test := range []struct { 351 name string 352 imageName string 353 passedSecrets []v1.Secret 354 shouldInjectError bool 355 }{ 356 { 357 name: "invalid docker secret", 358 imageName: "ubuntu", 359 passedSecrets: []v1.Secret{{Type: v1.SecretTypeDockercfg, Data: map[string][]byte{v1.DockerConfigKey: []byte("invalid")}}}, 360 }, 361 { 362 name: "secret provided, pull failed", 363 imageName: "ubuntu", 364 passedSecrets: []v1.Secret{ 365 {Type: v1.SecretTypeDockerConfigJson, Data: map[string][]byte{v1.DockerConfigKey: dockerConfigJSON}}, 366 }, 367 shouldInjectError: true, 368 }, 369 } { 370 t.Run(test.name, func(t *testing.T) { 371 _, fakeImageService, fakeManager, err := createTestRuntimeManager() 372 assert.NoError(t, err) 373 374 if test.shouldInjectError { 375 fakeImageService.InjectError("PullImage", fmt.Errorf("test-error")) 376 } 377 378 imageRef, err := fakeManager.PullImage(ctx, kubecontainer.ImageSpec{Image: test.imageName}, test.passedSecrets, nil) 379 assert.Error(t, err) 380 assert.Equal(t, "", imageRef) 381 382 images, err := fakeManager.ListImages(ctx) 383 assert.NoError(t, err) 384 assert.Equal(t, 0, len(images)) 385 }) 386 } 387 } 388 389 func TestPullThenListWithAnnotations(t *testing.T) { 390 ctx := context.Background() 391 _, _, fakeManager, err := createTestRuntimeManager() 392 assert.NoError(t, err) 393 394 imageSpec := kubecontainer.ImageSpec{ 395 Image: "12345", 396 Annotations: []kubecontainer.Annotation{ 397 {Name: "kubernetes.io/runtimehandler", Value: "handler_name"}, 398 }, 399 } 400 401 _, err = fakeManager.PullImage(ctx, imageSpec, nil, nil) 402 assert.NoError(t, err) 403 404 images, err := fakeManager.ListImages(ctx) 405 assert.NoError(t, err) 406 assert.Equal(t, 1, len(images)) 407 assert.Equal(t, images[0].Spec, imageSpec) 408 }