@@ -37,41 +37,63 @@ var _ = describe("GPU job processing", func() {
3737 cs = f .ClientSet
3838 })
3939
40- f .It ("Should run a job on a gpu node [Zalando] [GPU]" , f .WithSlow (), func (ctx context.Context ) {
41- ns := f .Namespace .Name
42- nameprefix := "gpu-test-"
43- labels := map [string ]string {
44- "application" : "vector-add" ,
45- }
40+ f .It ("Should run a vector-add job on a gpu node [Zalando] [GPU]" , f .WithSlow (), func (ctx context.Context ) {
41+ runGPUTest (ctx , f , cs , "gpu-test-" , "nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda12.5.0-ubi8" , nil , "PASSED" )
42+ })
4643
47- By ("Creating a vector pod which runs on a GPU node" )
48- pod := createVectorPod (nameprefix , ns , labels )
49- _ , err := cs .CoreV1 ().Pods (ns ).Create (ctx , pod , metav1.CreateOptions {})
50- framework .ExpectNoError (err , "Could not create POD %s" , pod .Name )
51- framework .ExpectNoError (e2epod .WaitForPodSuccessInNamespaceTimeout (ctx , f .ClientSet , pod .Name , pod .Namespace , 15 * time .Minute ))
52- for {
53- p , err := cs .CoreV1 ().Pods (ns ).Get (ctx , pod .Name , metav1.GetOptions {})
54- if err != nil {
55- framework .ExpectNoError (err , "Could not get POD %s" , pod .Name )
56- return
57- }
58- if p .Status .ContainerStatuses [0 ].State .Terminated == nil {
59- time .Sleep (10 * time .Second )
60- continue
61- }
62- n := p .Status .ContainerStatuses [0 ].State .Terminated .ExitCode
63- if n != 0 {
64- framework .ExpectNoError (fmt .Errorf ("expected POD %s to terminate with exit code 0" , pod .Name ))
65- return
66- }
67- logs , err := getPodLogs (cs , ns , pod .Name , "cuda-vector-add" , false )
68- framework .ExpectNoError (err , "Should be able to get logs for pod %v" , pod .Name )
69- regex := regexp .MustCompile ("PASSED" )
70- if regex .MatchString (logs ) {
71- return
72- }
73- framework .ExpectNoError (err , "Expected vector job to succeed" )
74- return
75- }
44+ f .It ("Should compile and run a CUDA kernel on a gpu node [Zalando] [GPU]" , f .WithSlow (), func (ctx context.Context ) {
45+ runGPUTest (ctx , f , cs , "gpu-test-" , "nvidia/cuda:13.2.1-devel-ubuntu24.04" , []string {"bash" , "-c" , `cat > /tmp/t.cu <<EOF
46+ #include <cstdio>
47+ __global__ void add(int *a){ *a += 41; }
48+ int main(){int *d,h=1; cudaMalloc(&d,4); cudaMemcpy(d,&h,4,cudaMemcpyHostToDevice);add<<<1,1>>>(d); cudaDeviceSynchronize();cudaError_t e=cudaGetLastError();if(e){ printf("FAIL: %s\n", cudaGetErrorString(e)); return 1; }cudaMemcpy(&h,d,4,cudaMemcpyDeviceToHost);printf("%s (result=%d)\n", h==42?"PASS":"FAIL", h); return h==42?0:1;}
49+ EOF
50+ nvcc --version | grep release
51+ nvcc -o /tmp/t /tmp/t.cu && /tmp/t` }, "PASS" )
52+ })
53+
54+ f .It ("Should run a PyTorch CUDA job on a gpu node [Zalando] [GPU]" , f .WithSlow (), func (ctx context.Context ) {
55+ runGPUTest (ctx , f , cs , "gpu-test-" , "pytorch/pytorch:2.12.1-cuda13.2-cudnn9-runtime" , []string {"python" , "-c" ,
56+ "import torch; v=torch.version.cuda; assert torch.cuda.is_available(); " +
57+ "assert tuple(map(int,v.split('.')))>=(13,2), f'CUDA {v} < 13.2'; " +
58+ "x=torch.rand(3,device='cuda'); torch.cuda.synchronize(); " +
59+ "print(f'PASS: torch {torch.__version__}, CUDA {v}, {torch.cuda.get_device_name()}')" ,
60+ }, "PASS" )
7661 })
7762})
63+
64+ func runGPUTest (ctx context.Context , f * framework.Framework , cs kubernetes.Interface , nameprefix , image string , command []string , logPattern string ) {
65+ ns := f .Namespace .Name
66+ labels := map [string ]string {
67+ "application" : "vector-add" ,
68+ }
69+
70+ By ("Creating a vector pod which runs on a GPU node" )
71+ pod := createVectorPod (nameprefix , ns , labels , image , command )
72+ _ , err := cs .CoreV1 ().Pods (ns ).Create (ctx , pod , metav1.CreateOptions {})
73+ framework .ExpectNoError (err , "Could not create POD %s" , pod .Name )
74+ framework .ExpectNoError (e2epod .WaitForPodSuccessInNamespaceTimeout (ctx , f .ClientSet , pod .Name , pod .Namespace , 15 * time .Minute ))
75+ for {
76+ p , err := cs .CoreV1 ().Pods (ns ).Get (ctx , pod .Name , metav1.GetOptions {})
77+ if err != nil {
78+ framework .ExpectNoError (err , "Could not get POD %s" , pod .Name )
79+ return
80+ }
81+ if p .Status .ContainerStatuses [0 ].State .Terminated == nil {
82+ time .Sleep (10 * time .Second )
83+ continue
84+ }
85+ n := p .Status .ContainerStatuses [0 ].State .Terminated .ExitCode
86+ if n != 0 {
87+ framework .ExpectNoError (fmt .Errorf ("expected POD %s to terminate with exit code 0" , pod .Name ))
88+ return
89+ }
90+ logs , err := getPodLogs (cs , ns , pod .Name , "main" , false )
91+ framework .ExpectNoError (err , "Should be able to get logs for pod %v" , pod .Name )
92+ regex := regexp .MustCompile (logPattern )
93+ if regex .MatchString (logs ) {
94+ return
95+ }
96+ framework .ExpectNoError (err , "Expected vector job to succeed" )
97+ return
98+ }
99+ }
0 commit comments