1
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
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
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 }
409
View as plain text