@@ -10,6 +10,7 @@ import (
1010 "k8s.io/kubectl/pkg/cmd/attach"
1111 "k8s.io/kubectl/pkg/cmd/exec"
1212 "k8s.io/pod-security-admission/api"
13+ utilexec "k8s.io/utils/exec"
1314
1415 fakekubeclient "k8s.io/client-go/kubernetes/fake"
1516 fakecorev1client "k8s.io/client-go/kubernetes/typed/core/v1/fake"
@@ -266,3 +267,213 @@ func TestCreateLabelMap(t *testing.T) {
266267 })
267268 }
268269}
270+
271+ func TestContainerExitCode (t * testing.T ) {
272+ tests := []struct {
273+ name string
274+ pod * corev1.Pod
275+ containerName string
276+ expected int32
277+ }{
278+ {
279+ name : "terminated with non-zero exit code" ,
280+ pod : & corev1.Pod {
281+ Status : corev1.PodStatus {
282+ ContainerStatuses : []corev1.ContainerStatus {
283+ {
284+ Name : "debug" ,
285+ State : corev1.ContainerState {Terminated : & corev1.ContainerStateTerminated {ExitCode : 42 }},
286+ },
287+ },
288+ },
289+ },
290+ containerName : "debug" ,
291+ expected : 42 ,
292+ },
293+ {
294+ name : "terminated with exit code 0" ,
295+ pod : & corev1.Pod {
296+ Status : corev1.PodStatus {
297+ ContainerStatuses : []corev1.ContainerStatus {
298+ {
299+ Name : "debug" ,
300+ State : corev1.ContainerState {Terminated : & corev1.ContainerStateTerminated {ExitCode : 0 }},
301+ },
302+ },
303+ },
304+ },
305+ containerName : "debug" ,
306+ expected : 0 ,
307+ },
308+ {
309+ name : "container still running" ,
310+ pod : & corev1.Pod {
311+ Status : corev1.PodStatus {
312+ ContainerStatuses : []corev1.ContainerStatus {
313+ {
314+ Name : "debug" ,
315+ State : corev1.ContainerState {Running : & corev1.ContainerStateRunning {}},
316+ },
317+ },
318+ },
319+ },
320+ containerName : "debug" ,
321+ expected : - 1 ,
322+ },
323+ {
324+ name : "container not found" ,
325+ pod : & corev1.Pod {
326+ Status : corev1.PodStatus {
327+ ContainerStatuses : []corev1.ContainerStatus {
328+ {
329+ Name : "other" ,
330+ State : corev1.ContainerState {Terminated : & corev1.ContainerStateTerminated {ExitCode : 1 }},
331+ },
332+ },
333+ },
334+ },
335+ containerName : "debug" ,
336+ expected : - 1 ,
337+ },
338+ {
339+ name : "init container terminated with non-zero exit code" ,
340+ pod : & corev1.Pod {
341+ Status : corev1.PodStatus {
342+ InitContainerStatuses : []corev1.ContainerStatus {
343+ {
344+ Name : "init" ,
345+ State : corev1.ContainerState {Terminated : & corev1.ContainerStateTerminated {ExitCode : 137 }},
346+ },
347+ },
348+ },
349+ },
350+ containerName : "init" ,
351+ expected : 137 ,
352+ },
353+ {
354+ name : "empty pod status" ,
355+ pod : & corev1.Pod {},
356+ containerName : "debug" ,
357+ expected : - 1 ,
358+ },
359+ }
360+
361+ for _ , tt := range tests {
362+ t .Run (tt .name , func (t * testing.T ) {
363+ got := containerExitCode (tt .pod , tt .containerName )
364+ if got != tt .expected {
365+ t .Errorf ("containerExitCode() = %d, want %d" , got , tt .expected )
366+ }
367+ })
368+ }
369+ }
370+
371+ func TestExitCodeError (t * testing.T ) {
372+ tests := []struct {
373+ name string
374+ pod * corev1.Pod
375+ containerName string
376+ expectError bool
377+ expectedExitCode int
378+ }{
379+ {
380+ name : "non-zero exit code returns CodeExitError" ,
381+ pod : & corev1.Pod {
382+ Status : corev1.PodStatus {
383+ ContainerStatuses : []corev1.ContainerStatus {
384+ {
385+ Name : "debug" ,
386+ State : corev1.ContainerState {Terminated : & corev1.ContainerStateTerminated {ExitCode : 42 }},
387+ },
388+ },
389+ },
390+ },
391+ containerName : "debug" ,
392+ expectError : true ,
393+ expectedExitCode : 42 ,
394+ },
395+ {
396+ name : "exit code 0 returns nil" ,
397+ pod : & corev1.Pod {
398+ Status : corev1.PodStatus {
399+ ContainerStatuses : []corev1.ContainerStatus {
400+ {
401+ Name : "debug" ,
402+ State : corev1.ContainerState {Terminated : & corev1.ContainerStateTerminated {ExitCode : 0 }},
403+ },
404+ },
405+ },
406+ },
407+ containerName : "debug" ,
408+ expectError : false ,
409+ },
410+ {
411+ name : "container not terminated returns nil" ,
412+ pod : & corev1.Pod {
413+ Status : corev1.PodStatus {
414+ ContainerStatuses : []corev1.ContainerStatus {
415+ {
416+ Name : "debug" ,
417+ State : corev1.ContainerState {Running : & corev1.ContainerStateRunning {}},
418+ },
419+ },
420+ },
421+ },
422+ containerName : "debug" ,
423+ expectError : false ,
424+ },
425+ {
426+ name : "container not found returns nil" ,
427+ pod : & corev1.Pod {
428+ Status : corev1.PodStatus {
429+ ContainerStatuses : []corev1.ContainerStatus {
430+ {
431+ Name : "other" ,
432+ State : corev1.ContainerState {Terminated : & corev1.ContainerStateTerminated {ExitCode : 1 }},
433+ },
434+ },
435+ },
436+ },
437+ containerName : "debug" ,
438+ expectError : false ,
439+ },
440+ {
441+ name : "exit code 127 returns correct code" ,
442+ pod : & corev1.Pod {
443+ Status : corev1.PodStatus {
444+ ContainerStatuses : []corev1.ContainerStatus {
445+ {
446+ Name : "debug" ,
447+ State : corev1.ContainerState {Terminated : & corev1.ContainerStateTerminated {ExitCode : 127 }},
448+ },
449+ },
450+ },
451+ },
452+ containerName : "debug" ,
453+ expectError : true ,
454+ expectedExitCode : 127 ,
455+ },
456+ }
457+
458+ for _ , tt := range tests {
459+ t .Run (tt .name , func (t * testing.T ) {
460+ err := exitCodeError (tt .pod , tt .containerName )
461+ if tt .expectError {
462+ if err == nil {
463+ t .Fatalf ("expected error, got nil" )
464+ }
465+ exitErr , ok := err .(utilexec.ExitError )
466+ if ! ok {
467+ t .Fatalf ("expected utilexec.ExitError, got %T" , err )
468+ }
469+ if exitErr .ExitStatus () != tt .expectedExitCode {
470+ t .Errorf ("ExitStatus() = %d, want %d" , exitErr .ExitStatus (), tt .expectedExitCode )
471+ }
472+ } else {
473+ if err != nil {
474+ t .Errorf ("expected nil, got %v" , err )
475+ }
476+ }
477+ })
478+ }
479+ }
0 commit comments