1
16
17 package dra
18
19 import (
20 "context"
21 "fmt"
22 "net"
23 "os"
24 "path/filepath"
25 "sync/atomic"
26 "testing"
27 "time"
28
29 "github.com/stretchr/testify/assert"
30 "google.golang.org/grpc"
31 v1 "k8s.io/api/core/v1"
32 resourcev1alpha2 "k8s.io/api/resource/v1alpha2"
33 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34 "k8s.io/apimachinery/pkg/util/sets"
35 "k8s.io/client-go/kubernetes/fake"
36 "k8s.io/dynamic-resource-allocation/resourceclaim"
37 drapbv1 "k8s.io/kubelet/pkg/apis/dra/v1alpha3"
38 "k8s.io/kubernetes/pkg/kubelet/cm/dra/plugin"
39 "k8s.io/kubernetes/pkg/kubelet/cm/dra/state"
40 kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
41 )
42
43 const (
44 driverName = "test-cdi-device"
45 driverClassName = "test"
46 )
47
48 type fakeDRADriverGRPCServer struct {
49 drapbv1.UnimplementedNodeServer
50 driverName string
51 timeout *time.Duration
52 prepareResourceCalls atomic.Uint32
53 unprepareResourceCalls atomic.Uint32
54 }
55
56 func (s *fakeDRADriverGRPCServer) NodePrepareResources(ctx context.Context, req *drapbv1.NodePrepareResourcesRequest) (*drapbv1.NodePrepareResourcesResponse, error) {
57 s.prepareResourceCalls.Add(1)
58
59 if s.timeout != nil {
60 time.Sleep(*s.timeout)
61 }
62 deviceName := "claim-" + req.Claims[0].Uid
63 result := s.driverName + "/" + driverClassName + "=" + deviceName
64 return &drapbv1.NodePrepareResourcesResponse{Claims: map[string]*drapbv1.NodePrepareResourceResponse{req.Claims[0].Uid: {CDIDevices: []string{result}}}}, nil
65 }
66
67 func (s *fakeDRADriverGRPCServer) NodeUnprepareResources(ctx context.Context, req *drapbv1.NodeUnprepareResourcesRequest) (*drapbv1.NodeUnprepareResourcesResponse, error) {
68 s.unprepareResourceCalls.Add(1)
69
70 if s.timeout != nil {
71 time.Sleep(*s.timeout)
72 }
73 return &drapbv1.NodeUnprepareResourcesResponse{Claims: map[string]*drapbv1.NodeUnprepareResourceResponse{req.Claims[0].Uid: {}}}, nil
74 }
75
76 type tearDown func()
77
78 type fakeDRAServerInfo struct {
79
80 server *fakeDRADriverGRPCServer
81
82 socketName string
83
84 teardownFn tearDown
85 }
86
87 func setupFakeDRADriverGRPCServer(shouldTimeout bool, pluginClientTimeout *time.Duration) (fakeDRAServerInfo, error) {
88 socketDir, err := os.MkdirTemp("", "dra")
89 if err != nil {
90 return fakeDRAServerInfo{
91 server: nil,
92 socketName: "",
93 teardownFn: nil,
94 }, err
95 }
96
97 socketName := filepath.Join(socketDir, "server.sock")
98 stopCh := make(chan struct{})
99
100 teardown := func() {
101 close(stopCh)
102 os.RemoveAll(socketName)
103 }
104
105 l, err := net.Listen("unix", socketName)
106 if err != nil {
107 teardown()
108 return fakeDRAServerInfo{
109 server: nil,
110 socketName: "",
111 teardownFn: nil,
112 }, err
113 }
114
115 s := grpc.NewServer()
116 fakeDRADriverGRPCServer := &fakeDRADriverGRPCServer{
117 driverName: driverName,
118 }
119 if shouldTimeout {
120 timeout := *pluginClientTimeout * 2
121 fakeDRADriverGRPCServer.timeout = &timeout
122 }
123
124 drapbv1.RegisterNodeServer(s, fakeDRADriverGRPCServer)
125
126 go func() {
127 go s.Serve(l)
128 <-stopCh
129 s.GracefulStop()
130 }()
131
132 return fakeDRAServerInfo{
133 server: fakeDRADriverGRPCServer,
134 socketName: socketName,
135 teardownFn: teardown,
136 }, nil
137 }
138
139 func TestNewManagerImpl(t *testing.T) {
140 kubeClient := fake.NewSimpleClientset()
141 for _, test := range []struct {
142 description string
143 stateFileDirectory string
144 wantErr bool
145 }{
146 {
147 description: "invalid directory path",
148 stateFileDirectory: "",
149 wantErr: true,
150 },
151 {
152 description: "valid directory path",
153 stateFileDirectory: t.TempDir(),
154 },
155 } {
156 t.Run(test.description, func(t *testing.T) {
157 manager, err := NewManagerImpl(kubeClient, test.stateFileDirectory, "worker")
158 if test.wantErr {
159 assert.Error(t, err)
160 return
161 }
162
163 assert.NoError(t, err)
164 assert.NotNil(t, manager.cache)
165 assert.NotNil(t, manager.kubeClient)
166 })
167 }
168 }
169
170 func TestGetResources(t *testing.T) {
171 kubeClient := fake.NewSimpleClientset()
172 resourceClaimName := "test-pod-claim-1"
173
174 for _, test := range []struct {
175 description string
176 container *v1.Container
177 pod *v1.Pod
178 claimInfo *ClaimInfo
179 wantErr bool
180 }{
181 {
182 description: "claim info with annotations",
183 container: &v1.Container{
184 Name: "test-container",
185 Resources: v1.ResourceRequirements{
186 Claims: []v1.ResourceClaim{
187 {
188 Name: "test-pod-claim-1",
189 },
190 },
191 },
192 },
193 pod: &v1.Pod{
194 ObjectMeta: metav1.ObjectMeta{
195 Name: "test-pod",
196 Namespace: "test-namespace",
197 },
198 Spec: v1.PodSpec{
199 ResourceClaims: []v1.PodResourceClaim{
200 {
201 Name: "test-pod-claim-1",
202 Source: v1.ClaimSource{ResourceClaimName: &resourceClaimName},
203 },
204 },
205 },
206 },
207 claimInfo: &ClaimInfo{
208 annotations: map[string][]kubecontainer.Annotation{
209 "test-plugin": {
210 {
211 Name: "test-annotation",
212 Value: "123",
213 },
214 },
215 },
216 ClaimInfoState: state.ClaimInfoState{
217 ClaimName: "test-pod-claim-1",
218 CDIDevices: map[string][]string{
219 driverName: {"123"},
220 },
221 Namespace: "test-namespace",
222 },
223 },
224 },
225 {
226 description: "claim info without annotations",
227 container: &v1.Container{
228 Name: "test-container",
229 Resources: v1.ResourceRequirements{
230 Claims: []v1.ResourceClaim{
231 {
232 Name: "test-pod-claim-1",
233 },
234 },
235 },
236 },
237 pod: &v1.Pod{
238 ObjectMeta: metav1.ObjectMeta{
239 Name: "test-pod",
240 Namespace: "test-namespace",
241 },
242 Spec: v1.PodSpec{
243 ResourceClaims: []v1.PodResourceClaim{
244 {
245 Name: "test-pod-claim-1",
246 Source: v1.ClaimSource{ResourceClaimName: &resourceClaimName},
247 },
248 },
249 },
250 },
251 claimInfo: &ClaimInfo{
252 ClaimInfoState: state.ClaimInfoState{
253 ClaimName: "test-pod-claim-1",
254 CDIDevices: map[string][]string{
255 driverName: {"123"},
256 },
257 Namespace: "test-namespace",
258 },
259 },
260 },
261 {
262 description: "no claim info",
263 container: &v1.Container{
264 Name: "test-container",
265 Resources: v1.ResourceRequirements{
266 Claims: []v1.ResourceClaim{
267 {
268 Name: "test-pod-claim-1",
269 },
270 },
271 },
272 },
273 pod: &v1.Pod{
274 ObjectMeta: metav1.ObjectMeta{
275 Name: "test-pod",
276 Namespace: "test-namespace",
277 },
278 Spec: v1.PodSpec{
279 ResourceClaims: []v1.PodResourceClaim{
280 {
281 Name: "test-pod-claim-1",
282 },
283 },
284 },
285 },
286 wantErr: true,
287 },
288 } {
289 t.Run(test.description, func(t *testing.T) {
290 manager, err := NewManagerImpl(kubeClient, t.TempDir(), "worker")
291 assert.NoError(t, err)
292
293 if test.claimInfo != nil {
294 manager.cache.add(test.claimInfo)
295 }
296
297 containerInfo, err := manager.GetResources(test.pod, test.container)
298 if test.wantErr {
299 assert.Error(t, err)
300 return
301 }
302
303 assert.NoError(t, err)
304 assert.Equal(t, test.claimInfo.CDIDevices[driverName][0], containerInfo.CDIDevices[0].Name)
305 })
306 }
307 }
308
309 func getFakeNode() (*v1.Node, error) {
310 return &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "worker"}}, nil
311 }
312
313 func TestPrepareResources(t *testing.T) {
314 fakeKubeClient := fake.NewSimpleClientset()
315
316 for _, test := range []struct {
317 description string
318 driverName string
319 pod *v1.Pod
320 claimInfo *ClaimInfo
321 resourceClaim *resourcev1alpha2.ResourceClaim
322 wantErr bool
323 wantTimeout bool
324 wantResourceSkipped bool
325 ExpectedPrepareCalls uint32
326 }{
327 {
328 description: "failed to fetch ResourceClaim",
329 driverName: driverName,
330 pod: &v1.Pod{
331 ObjectMeta: metav1.ObjectMeta{
332 Name: "test-pod",
333 Namespace: "test-namespace",
334 UID: "test-reserved",
335 },
336 Spec: v1.PodSpec{
337 ResourceClaims: []v1.PodResourceClaim{
338 {
339 Name: "test-pod-claim-0",
340 Source: v1.ClaimSource{
341 ResourceClaimName: func() *string {
342 s := "test-pod-claim-0"
343 return &s
344 }(),
345 },
346 },
347 },
348 },
349 },
350 wantErr: true,
351 },
352 {
353 description: "plugin does not exist",
354 driverName: "this-plugin-does-not-exist",
355 pod: &v1.Pod{
356 ObjectMeta: metav1.ObjectMeta{
357 Name: "test-pod",
358 Namespace: "test-namespace",
359 UID: "test-reserved",
360 },
361 Spec: v1.PodSpec{
362 ResourceClaims: []v1.PodResourceClaim{
363 {
364 Name: "test-pod-claim-1",
365 Source: v1.ClaimSource{
366 ResourceClaimName: func() *string {
367 s := "test-pod-claim-1"
368 return &s
369 }(),
370 },
371 },
372 },
373 Containers: []v1.Container{
374 {
375 Resources: v1.ResourceRequirements{
376 Claims: []v1.ResourceClaim{
377 {
378 Name: "test-pod-claim-1",
379 },
380 },
381 },
382 },
383 },
384 },
385 },
386 resourceClaim: &resourcev1alpha2.ResourceClaim{
387 ObjectMeta: metav1.ObjectMeta{
388 Name: "test-pod-claim-1",
389 Namespace: "test-namespace",
390 UID: "test-reserved",
391 },
392 Spec: resourcev1alpha2.ResourceClaimSpec{
393 ResourceClassName: "test-class",
394 },
395 Status: resourcev1alpha2.ResourceClaimStatus{
396 DriverName: driverName,
397 Allocation: &resourcev1alpha2.AllocationResult{
398 ResourceHandles: []resourcev1alpha2.ResourceHandle{
399 {Data: "test-data", DriverName: driverName},
400 },
401 },
402 ReservedFor: []resourcev1alpha2.ResourceClaimConsumerReference{
403 {UID: "test-reserved"},
404 },
405 },
406 },
407 wantErr: true,
408 },
409 {
410 description: "pod is not allowed to use resource claim",
411 driverName: driverName,
412 pod: &v1.Pod{
413 ObjectMeta: metav1.ObjectMeta{
414 Name: "test-pod",
415 Namespace: "test-namespace",
416 UID: "test-reserved",
417 },
418 Spec: v1.PodSpec{
419 ResourceClaims: []v1.PodResourceClaim{
420 {
421 Name: "test-pod-claim-2",
422 Source: v1.ClaimSource{
423 ResourceClaimName: func() *string {
424 s := "test-pod-claim-2"
425 return &s
426 }(),
427 },
428 },
429 },
430 },
431 },
432 resourceClaim: &resourcev1alpha2.ResourceClaim{
433 ObjectMeta: metav1.ObjectMeta{
434 Name: "test-pod-claim-2",
435 Namespace: "test-namespace",
436 UID: "test-reserved",
437 },
438 Spec: resourcev1alpha2.ResourceClaimSpec{
439 ResourceClassName: "test-class",
440 },
441 Status: resourcev1alpha2.ResourceClaimStatus{
442 DriverName: driverName,
443 Allocation: &resourcev1alpha2.AllocationResult{
444 ResourceHandles: []resourcev1alpha2.ResourceHandle{
445 {Data: "test-data", DriverName: driverName},
446 },
447 },
448 },
449 },
450 wantErr: true,
451 },
452 {
453 description: "no container actually uses the claim",
454 driverName: driverName,
455 pod: &v1.Pod{
456 ObjectMeta: metav1.ObjectMeta{
457 Name: "test-pod",
458 Namespace: "test-namespace",
459 UID: "test-reserved",
460 },
461 Spec: v1.PodSpec{
462 ResourceClaims: []v1.PodResourceClaim{
463 {
464 Name: "test-pod-claim-3",
465 Source: v1.ClaimSource{ResourceClaimName: func() *string {
466 s := "test-pod-claim-3"
467 return &s
468 }()},
469 },
470 },
471 },
472 },
473 resourceClaim: &resourcev1alpha2.ResourceClaim{
474 ObjectMeta: metav1.ObjectMeta{
475 Name: "test-pod-claim-3",
476 Namespace: "test-namespace",
477 UID: "test-reserved",
478 },
479 Spec: resourcev1alpha2.ResourceClaimSpec{
480 ResourceClassName: "test-class",
481 },
482 Status: resourcev1alpha2.ResourceClaimStatus{
483 DriverName: driverName,
484 Allocation: &resourcev1alpha2.AllocationResult{
485 ResourceHandles: []resourcev1alpha2.ResourceHandle{
486 {Data: "test-data", DriverName: driverName},
487 },
488 },
489 ReservedFor: []resourcev1alpha2.ResourceClaimConsumerReference{
490 {UID: "test-reserved"},
491 },
492 },
493 },
494 wantResourceSkipped: true,
495 },
496 {
497 description: "resource already prepared",
498 driverName: driverName,
499 pod: &v1.Pod{
500 ObjectMeta: metav1.ObjectMeta{
501 Name: "test-pod",
502 Namespace: "test-namespace",
503 UID: "test-reserved",
504 },
505 Spec: v1.PodSpec{
506 ResourceClaims: []v1.PodResourceClaim{
507 {
508 Name: "test-pod-claim-4",
509 Source: v1.ClaimSource{ResourceClaimName: func() *string {
510 s := "test-pod-claim-4"
511 return &s
512 }()},
513 },
514 },
515 Containers: []v1.Container{
516 {
517 Resources: v1.ResourceRequirements{
518 Claims: []v1.ResourceClaim{
519 {
520 Name: "test-pod-claim-4",
521 },
522 },
523 },
524 },
525 },
526 },
527 },
528 claimInfo: &ClaimInfo{
529 ClaimInfoState: state.ClaimInfoState{
530 DriverName: driverName,
531 ClaimName: "test-pod-claim-4",
532 Namespace: "test-namespace",
533 PodUIDs: sets.Set[string]{"test-another-pod-reserved": sets.Empty{}},
534 },
535 prepared: true,
536 },
537 resourceClaim: &resourcev1alpha2.ResourceClaim{
538 ObjectMeta: metav1.ObjectMeta{
539 Name: "test-pod-claim-4",
540 Namespace: "test-namespace",
541 UID: "test-reserved",
542 },
543 Spec: resourcev1alpha2.ResourceClaimSpec{
544 ResourceClassName: "test-class",
545 },
546 Status: resourcev1alpha2.ResourceClaimStatus{
547 DriverName: driverName,
548 Allocation: &resourcev1alpha2.AllocationResult{
549 ResourceHandles: []resourcev1alpha2.ResourceHandle{
550 {Data: "test-data", DriverName: driverName},
551 },
552 },
553 ReservedFor: []resourcev1alpha2.ResourceClaimConsumerReference{
554 {UID: "test-reserved"},
555 },
556 },
557 },
558 wantResourceSkipped: true,
559 },
560 {
561 description: "should timeout",
562 driverName: driverName,
563 pod: &v1.Pod{
564 ObjectMeta: metav1.ObjectMeta{
565 Name: "test-pod",
566 Namespace: "test-namespace",
567 UID: "test-reserved",
568 },
569 Spec: v1.PodSpec{
570 ResourceClaims: []v1.PodResourceClaim{
571 {
572 Name: "test-pod-claim-5",
573 Source: v1.ClaimSource{ResourceClaimName: func() *string {
574 s := "test-pod-claim-5"
575 return &s
576 }()},
577 },
578 },
579 Containers: []v1.Container{
580 {
581 Resources: v1.ResourceRequirements{
582 Claims: []v1.ResourceClaim{
583 {
584 Name: "test-pod-claim-5",
585 },
586 },
587 },
588 },
589 },
590 },
591 },
592 resourceClaim: &resourcev1alpha2.ResourceClaim{
593 ObjectMeta: metav1.ObjectMeta{
594 Name: "test-pod-claim-5",
595 Namespace: "test-namespace",
596 UID: "test-reserved",
597 },
598 Spec: resourcev1alpha2.ResourceClaimSpec{
599 ResourceClassName: "test-class",
600 },
601 Status: resourcev1alpha2.ResourceClaimStatus{
602 DriverName: driverName,
603 Allocation: &resourcev1alpha2.AllocationResult{
604 ResourceHandles: []resourcev1alpha2.ResourceHandle{
605 {Data: "test-data", DriverName: driverName},
606 },
607 },
608 ReservedFor: []resourcev1alpha2.ResourceClaimConsumerReference{
609 {UID: "test-reserved"},
610 },
611 },
612 },
613 wantErr: true,
614 wantTimeout: true,
615 ExpectedPrepareCalls: 1,
616 },
617 {
618 description: "should prepare resource, claim not in cache",
619 driverName: driverName,
620 pod: &v1.Pod{
621 ObjectMeta: metav1.ObjectMeta{
622 Name: "test-pod",
623 Namespace: "test-namespace",
624 UID: "test-reserved",
625 },
626 Spec: v1.PodSpec{
627 ResourceClaims: []v1.PodResourceClaim{
628 {
629 Name: "test-pod-claim-6",
630 Source: v1.ClaimSource{ResourceClaimName: func() *string {
631 s := "test-pod-claim-6"
632 return &s
633 }()},
634 },
635 },
636 Containers: []v1.Container{
637 {
638 Resources: v1.ResourceRequirements{
639 Claims: []v1.ResourceClaim{
640 {
641 Name: "test-pod-claim-6",
642 },
643 },
644 },
645 },
646 },
647 },
648 },
649 resourceClaim: &resourcev1alpha2.ResourceClaim{
650 ObjectMeta: metav1.ObjectMeta{
651 Name: "test-pod-claim-6",
652 Namespace: "test-namespace",
653 UID: "test-reserved",
654 },
655 Spec: resourcev1alpha2.ResourceClaimSpec{
656 ResourceClassName: "test-class",
657 },
658 Status: resourcev1alpha2.ResourceClaimStatus{
659 DriverName: driverName,
660 Allocation: &resourcev1alpha2.AllocationResult{
661 ResourceHandles: []resourcev1alpha2.ResourceHandle{
662 {Data: "test-data"},
663 },
664 },
665 ReservedFor: []resourcev1alpha2.ResourceClaimConsumerReference{
666 {UID: "test-reserved"},
667 },
668 },
669 },
670 ExpectedPrepareCalls: 1,
671 },
672 {
673 description: "should prepare resource. claim in cache, manager did not prepare resource",
674 driverName: driverName,
675 pod: &v1.Pod{
676 ObjectMeta: metav1.ObjectMeta{
677 Name: "test-pod",
678 Namespace: "test-namespace",
679 UID: "test-reserved",
680 },
681 Spec: v1.PodSpec{
682 ResourceClaims: []v1.PodResourceClaim{
683 {
684 Name: "test-pod-claim",
685 Source: v1.ClaimSource{ResourceClaimName: func() *string {
686 s := "test-pod-claim"
687 return &s
688 }()},
689 },
690 },
691 Containers: []v1.Container{
692 {
693 Resources: v1.ResourceRequirements{
694 Claims: []v1.ResourceClaim{
695 {
696 Name: "test-pod-claim",
697 },
698 },
699 },
700 },
701 },
702 },
703 },
704 claimInfo: &ClaimInfo{
705 ClaimInfoState: state.ClaimInfoState{
706 DriverName: driverName,
707 ClassName: "test-class",
708 ClaimName: "test-pod-claim",
709 ClaimUID: "test-reserved",
710 Namespace: "test-namespace",
711 PodUIDs: sets.Set[string]{"test-reserved": sets.Empty{}},
712 CDIDevices: map[string][]string{
713 driverName: {fmt.Sprintf("%s/%s=some-device", driverName, driverClassName)},
714 },
715 ResourceHandles: []resourcev1alpha2.ResourceHandle{{Data: "test-data"}},
716 },
717 annotations: make(map[string][]kubecontainer.Annotation),
718 prepared: false,
719 },
720 resourceClaim: &resourcev1alpha2.ResourceClaim{
721 ObjectMeta: metav1.ObjectMeta{
722 Name: "test-pod-claim",
723 Namespace: "test-namespace",
724 UID: "test-reserved",
725 },
726 Spec: resourcev1alpha2.ResourceClaimSpec{
727 ResourceClassName: "test-class",
728 },
729 Status: resourcev1alpha2.ResourceClaimStatus{
730 DriverName: driverName,
731 Allocation: &resourcev1alpha2.AllocationResult{
732 ResourceHandles: []resourcev1alpha2.ResourceHandle{
733 {Data: "test-data"},
734 },
735 },
736 ReservedFor: []resourcev1alpha2.ResourceClaimConsumerReference{
737 {UID: "test-reserved"},
738 },
739 },
740 },
741 ExpectedPrepareCalls: 1,
742 },
743 } {
744 t.Run(test.description, func(t *testing.T) {
745 cache, err := newClaimInfoCache(t.TempDir(), draManagerStateFileName)
746 if err != nil {
747 t.Fatalf("failed to newClaimInfoCache, err:%v", err)
748 }
749
750 manager := &ManagerImpl{
751 kubeClient: fakeKubeClient,
752 cache: cache,
753 }
754
755 if test.resourceClaim != nil {
756 if _, err := fakeKubeClient.ResourceV1alpha2().ResourceClaims(test.pod.Namespace).Create(context.Background(), test.resourceClaim, metav1.CreateOptions{}); err != nil {
757 t.Fatalf("failed to create ResourceClaim %s: %+v", test.resourceClaim.Name, err)
758 }
759 }
760
761 var pluginClientTimeout *time.Duration
762 if test.wantTimeout {
763 timeout := time.Millisecond * 20
764 pluginClientTimeout = &timeout
765 }
766
767 draServerInfo, err := setupFakeDRADriverGRPCServer(test.wantTimeout, pluginClientTimeout)
768 if err != nil {
769 t.Fatal(err)
770 }
771 defer draServerInfo.teardownFn()
772
773 plg := plugin.NewRegistrationHandler(nil, getFakeNode)
774 if err := plg.RegisterPlugin(test.driverName, draServerInfo.socketName, []string{"1.27"}, pluginClientTimeout); err != nil {
775 t.Fatalf("failed to register plugin %s, err: %v", test.driverName, err)
776 }
777 defer plg.DeRegisterPlugin(test.driverName)
778
779 if test.claimInfo != nil {
780 manager.cache.add(test.claimInfo)
781 }
782
783 err = manager.PrepareResources(test.pod)
784
785 assert.Equal(t, test.ExpectedPrepareCalls, draServerInfo.server.prepareResourceCalls.Load())
786
787 if test.wantErr {
788 assert.Error(t, err)
789 return
790 } else if test.wantResourceSkipped {
791 assert.NoError(t, err)
792 return
793 }
794
795 assert.NoError(t, err)
796
797 claimName, _, err := resourceclaim.Name(test.pod, &test.pod.Spec.ResourceClaims[0])
798 if err != nil {
799 t.Fatal(err)
800 }
801 claimInfo := manager.cache.get(*claimName, test.pod.Namespace)
802 if claimInfo == nil {
803 t.Fatalf("claimInfo not found in cache for claim %s", *claimName)
804 }
805 if claimInfo.DriverName != test.resourceClaim.Status.DriverName {
806 t.Fatalf("driverName mismatch: expected %s, got %s", test.resourceClaim.Status.DriverName, claimInfo.DriverName)
807 }
808 if claimInfo.ClassName != test.resourceClaim.Spec.ResourceClassName {
809 t.Fatalf("resourceClassName mismatch: expected %s, got %s", test.resourceClaim.Spec.ResourceClassName, claimInfo.ClassName)
810 }
811 if len(claimInfo.PodUIDs) != 1 || !claimInfo.PodUIDs.Has(string(test.pod.UID)) {
812 t.Fatalf("podUIDs mismatch: expected [%s], got %v", test.pod.UID, claimInfo.PodUIDs)
813 }
814 expectedResourceClaimDriverName := fmt.Sprintf("%s/%s=claim-%s", driverName, driverClassName, string(test.resourceClaim.Status.ReservedFor[0].UID))
815 if len(claimInfo.CDIDevices[test.resourceClaim.Status.DriverName]) != 1 || claimInfo.CDIDevices[test.resourceClaim.Status.DriverName][0] != expectedResourceClaimDriverName {
816 t.Fatalf("cdiDevices mismatch: expected [%s], got %v", []string{expectedResourceClaimDriverName}, claimInfo.CDIDevices[test.resourceClaim.Status.DriverName])
817 }
818 })
819 }
820 }
821
822 func TestUnprepareResources(t *testing.T) {
823 fakeKubeClient := fake.NewSimpleClientset()
824
825 for _, test := range []struct {
826 description string
827 driverName string
828 pod *v1.Pod
829 claimInfo *ClaimInfo
830 wantErr bool
831 wantTimeout bool
832 wantResourceSkipped bool
833 expectedUnprepareCalls uint32
834 }{
835 {
836 description: "plugin does not exist",
837 driverName: "this-plugin-does-not-exist",
838 pod: &v1.Pod{
839 ObjectMeta: metav1.ObjectMeta{
840 Name: "test-pod",
841 Namespace: "test-namespace",
842 UID: "test-reserved",
843 },
844 Spec: v1.PodSpec{
845 ResourceClaims: []v1.PodResourceClaim{
846 {
847 Name: "another-claim-test",
848 Source: v1.ClaimSource{
849 ResourceClaimName: func() *string {
850 s := "another-claim-test"
851 return &s
852 }(),
853 },
854 },
855 },
856 },
857 },
858 claimInfo: &ClaimInfo{
859 ClaimInfoState: state.ClaimInfoState{
860 DriverName: driverName,
861 ClaimName: "another-claim-test",
862 Namespace: "test-namespace",
863 ResourceHandles: []resourcev1alpha2.ResourceHandle{
864 {
865 DriverName: driverName,
866 Data: "test data",
867 },
868 },
869 },
870 },
871 wantErr: true,
872 },
873 {
874 description: "resource claim referenced by other pod(s)",
875 driverName: driverName,
876 pod: &v1.Pod{
877 ObjectMeta: metav1.ObjectMeta{
878 Name: "test-pod",
879 Namespace: "test-namespace",
880 UID: "test-reserved",
881 },
882 Spec: v1.PodSpec{
883 ResourceClaims: []v1.PodResourceClaim{
884 {
885 Name: "test-pod-claim-1",
886 Source: v1.ClaimSource{ResourceClaimName: func() *string {
887 s := "test-pod-claim-1"
888 return &s
889 }()},
890 },
891 },
892 Containers: []v1.Container{
893 {
894 Resources: v1.ResourceRequirements{
895 Claims: []v1.ResourceClaim{
896 {
897 Name: "test-pod-claim-1",
898 },
899 },
900 },
901 },
902 },
903 },
904 },
905 claimInfo: &ClaimInfo{
906 ClaimInfoState: state.ClaimInfoState{
907 DriverName: driverName,
908 ClaimName: "test-pod-claim-1",
909 Namespace: "test-namespace",
910 PodUIDs: sets.Set[string]{"test-reserved": sets.Empty{}, "test-reserved-2": sets.Empty{}},
911 },
912 },
913 wantResourceSkipped: true,
914 },
915 {
916 description: "should timeout",
917 driverName: driverName,
918 pod: &v1.Pod{
919 ObjectMeta: metav1.ObjectMeta{
920 Name: "test-pod",
921 Namespace: "test-namespace",
922 UID: "test-reserved",
923 },
924 Spec: v1.PodSpec{
925 ResourceClaims: []v1.PodResourceClaim{
926 {
927 Name: "test-pod-claim-2",
928 Source: v1.ClaimSource{ResourceClaimName: func() *string {
929 s := "test-pod-claim-2"
930 return &s
931 }()},
932 },
933 },
934 Containers: []v1.Container{
935 {
936 Resources: v1.ResourceRequirements{
937 Claims: []v1.ResourceClaim{
938 {
939 Name: "test-pod-claim-2",
940 },
941 },
942 },
943 },
944 },
945 },
946 },
947 claimInfo: &ClaimInfo{
948 ClaimInfoState: state.ClaimInfoState{
949 DriverName: driverName,
950 ClaimName: "test-pod-claim-2",
951 Namespace: "test-namespace",
952 ResourceHandles: []resourcev1alpha2.ResourceHandle{
953 {
954 DriverName: driverName,
955 Data: "test data",
956 },
957 },
958 },
959 },
960 wantErr: true,
961 wantTimeout: true,
962 expectedUnprepareCalls: 1,
963 },
964 {
965 description: "should unprepare resource, claim previously prepared by currently running manager",
966 driverName: driverName,
967 pod: &v1.Pod{
968 ObjectMeta: metav1.ObjectMeta{
969 Name: "test-pod",
970 Namespace: "test-namespace",
971 UID: "test-reserved",
972 },
973 Spec: v1.PodSpec{
974 ResourceClaims: []v1.PodResourceClaim{
975 {
976 Name: "test-pod-claim-3",
977 Source: v1.ClaimSource{ResourceClaimName: func() *string {
978 s := "test-pod-claim-3"
979 return &s
980 }()},
981 },
982 },
983 Containers: []v1.Container{
984 {
985 Resources: v1.ResourceRequirements{
986 Claims: []v1.ResourceClaim{
987 {
988 Name: "test-pod-claim-3",
989 },
990 },
991 },
992 },
993 },
994 },
995 },
996 claimInfo: &ClaimInfo{
997 ClaimInfoState: state.ClaimInfoState{
998 DriverName: driverName,
999 ClaimName: "test-pod-claim-3",
1000 Namespace: "test-namespace",
1001 ResourceHandles: []resourcev1alpha2.ResourceHandle{
1002 {
1003 DriverName: driverName,
1004 Data: "test data",
1005 },
1006 },
1007 },
1008 prepared: true,
1009 },
1010 expectedUnprepareCalls: 1,
1011 },
1012 {
1013 description: "should unprepare resource, claim previously was not prepared by currently running manager",
1014 driverName: driverName,
1015 pod: &v1.Pod{
1016 ObjectMeta: metav1.ObjectMeta{
1017 Name: "test-pod",
1018 Namespace: "test-namespace",
1019 UID: "test-reserved",
1020 },
1021 Spec: v1.PodSpec{
1022 ResourceClaims: []v1.PodResourceClaim{
1023 {
1024 Name: "test-pod-claim",
1025 Source: v1.ClaimSource{ResourceClaimName: func() *string {
1026 s := "test-pod-claim"
1027 return &s
1028 }()},
1029 },
1030 },
1031 Containers: []v1.Container{
1032 {
1033 Resources: v1.ResourceRequirements{
1034 Claims: []v1.ResourceClaim{
1035 {
1036 Name: "test-pod-claim",
1037 },
1038 },
1039 },
1040 },
1041 },
1042 },
1043 },
1044 claimInfo: &ClaimInfo{
1045 ClaimInfoState: state.ClaimInfoState{
1046 DriverName: driverName,
1047 ClaimName: "test-pod-claim",
1048 Namespace: "test-namespace",
1049 ResourceHandles: []resourcev1alpha2.ResourceHandle{
1050 {
1051 DriverName: driverName,
1052 Data: "test data",
1053 },
1054 },
1055 },
1056 prepared: false,
1057 },
1058 expectedUnprepareCalls: 1,
1059 },
1060 } {
1061 t.Run(test.description, func(t *testing.T) {
1062 cache, err := newClaimInfoCache(t.TempDir(), draManagerStateFileName)
1063 if err != nil {
1064 t.Fatalf("failed to create a new instance of the claimInfoCache, err: %v", err)
1065 }
1066
1067 var pluginClientTimeout *time.Duration
1068 if test.wantTimeout {
1069 timeout := time.Millisecond * 20
1070 pluginClientTimeout = &timeout
1071 }
1072
1073 draServerInfo, err := setupFakeDRADriverGRPCServer(test.wantTimeout, pluginClientTimeout)
1074 if err != nil {
1075 t.Fatal(err)
1076 }
1077 defer draServerInfo.teardownFn()
1078
1079 plg := plugin.NewRegistrationHandler(nil, getFakeNode)
1080 if err := plg.RegisterPlugin(test.driverName, draServerInfo.socketName, []string{"1.27"}, pluginClientTimeout); err != nil {
1081 t.Fatalf("failed to register plugin %s, err: %v", test.driverName, err)
1082 }
1083 defer plg.DeRegisterPlugin(test.driverName)
1084
1085 manager := &ManagerImpl{
1086 kubeClient: fakeKubeClient,
1087 cache: cache,
1088 }
1089
1090 if test.claimInfo != nil {
1091 manager.cache.add(test.claimInfo)
1092 }
1093
1094 err = manager.UnprepareResources(test.pod)
1095
1096 assert.Equal(t, test.expectedUnprepareCalls, draServerInfo.server.unprepareResourceCalls.Load())
1097
1098 if test.wantErr {
1099 assert.Error(t, err)
1100 return
1101 } else if test.wantResourceSkipped {
1102 assert.NoError(t, err)
1103 return
1104 }
1105
1106 assert.NoError(t, err)
1107
1108 claimName, _, err := resourceclaim.Name(test.pod, &test.pod.Spec.ResourceClaims[0])
1109 if err != nil {
1110 t.Fatal(err)
1111 }
1112 claimInfo := manager.cache.get(*claimName, test.pod.Namespace)
1113 if claimInfo != nil {
1114 t.Fatalf("claimInfo still found in cache after calling UnprepareResources")
1115 }
1116 })
1117 }
1118 }
1119
1120 func TestPodMightNeedToUnprepareResources(t *testing.T) {
1121 fakeKubeClient := fake.NewSimpleClientset()
1122
1123 cache, err := newClaimInfoCache(t.TempDir(), draManagerStateFileName)
1124 if err != nil {
1125 t.Fatalf("failed to newClaimInfoCache, err:%v", err)
1126 }
1127
1128 manager := &ManagerImpl{
1129 kubeClient: fakeKubeClient,
1130 cache: cache,
1131 }
1132
1133 podUID := sets.Set[string]{}
1134 podUID.Insert("test-pod-uid")
1135 manager.cache.add(&ClaimInfo{
1136 ClaimInfoState: state.ClaimInfoState{PodUIDs: podUID, ClaimName: "test-claim", Namespace: "test-namespace"},
1137 })
1138
1139 testClaimInfo := manager.cache.get("test-claim", "test-namespace")
1140 testClaimInfo.addPodReference("test-pod-uid")
1141
1142 manager.PodMightNeedToUnprepareResources("test-pod-uid")
1143 }
1144
1145 func TestGetContainerClaimInfos(t *testing.T) {
1146 cache, err := newClaimInfoCache(t.TempDir(), draManagerStateFileName)
1147 if err != nil {
1148 t.Fatalf("error occur:%v", err)
1149 }
1150 manager := &ManagerImpl{
1151 cache: cache,
1152 }
1153
1154 resourceClaimName := "test-resource-claim-1"
1155 resourceClaimName2 := "test-resource-claim-2"
1156
1157 for i, test := range []struct {
1158 expectedClaimName string
1159 pod *v1.Pod
1160 container *v1.Container
1161 claimInfo *ClaimInfo
1162 }{
1163 {
1164 expectedClaimName: resourceClaimName,
1165 pod: &v1.Pod{
1166 Spec: v1.PodSpec{
1167 ResourceClaims: []v1.PodResourceClaim{
1168 {
1169 Name: "claim1",
1170 Source: v1.ClaimSource{ResourceClaimName: &resourceClaimName},
1171 },
1172 },
1173 },
1174 },
1175 container: &v1.Container{
1176 Resources: v1.ResourceRequirements{
1177 Claims: []v1.ResourceClaim{
1178 {
1179 Name: "claim1",
1180 },
1181 },
1182 },
1183 },
1184 claimInfo: &ClaimInfo{ClaimInfoState: state.ClaimInfoState{ClaimName: resourceClaimName}},
1185 },
1186 {
1187 expectedClaimName: resourceClaimName2,
1188 pod: &v1.Pod{
1189 Spec: v1.PodSpec{
1190 ResourceClaims: []v1.PodResourceClaim{
1191 {
1192 Name: "claim2",
1193 Source: v1.ClaimSource{ResourceClaimName: &resourceClaimName2},
1194 },
1195 },
1196 },
1197 },
1198 container: &v1.Container{
1199 Resources: v1.ResourceRequirements{
1200 Claims: []v1.ResourceClaim{
1201 {
1202 Name: "claim2",
1203 },
1204 },
1205 },
1206 },
1207 claimInfo: &ClaimInfo{ClaimInfoState: state.ClaimInfoState{ClaimName: resourceClaimName2}},
1208 },
1209 } {
1210 t.Run(fmt.Sprintf("test-%d", i), func(t *testing.T) {
1211 manager.cache.add(test.claimInfo)
1212
1213 fakeClaimInfos, err := manager.GetContainerClaimInfos(test.pod, test.container)
1214 assert.NoError(t, err)
1215 assert.Equal(t, 1, len(fakeClaimInfos))
1216 assert.Equal(t, test.expectedClaimName, fakeClaimInfos[0].ClaimInfoState.ClaimName)
1217
1218 manager.cache.delete(test.pod.Spec.ResourceClaims[0].Name, "default")
1219 _, err = manager.GetContainerClaimInfos(test.pod, test.container)
1220 assert.NoError(t, err)
1221 })
1222 }
1223 }
1224
View as plain text