package k8s import ( "context" "testing" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" k8sfake "k8s.io/client-go/kubernetes/fake" k8stesting "k8s.io/client-go/testing" ) func TestResolvePVCVolumeCoversSuccessAndFailures(t *testing.T) { pvcBound := &corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{Name: "data", Namespace: "apps"}, Spec: corev1.PersistentVolumeClaimSpec{VolumeName: "pv-data"}, } pvBound := &corev1.PersistentVolume{ ObjectMeta: metav1.ObjectMeta{Name: "pv-data"}, } pvcNoVolume := &corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{Name: "pending", Namespace: "apps"}, } client := &Client{Clientset: k8sfake.NewSimpleClientset(pvcBound, pvcNoVolume, pvBound)} volumeName, pvc, pv, err := client.ResolvePVCVolume(context.Background(), "apps", "data") if err != nil || volumeName != "pv-data" || pvc.Name != "data" || pv.Name != "pv-data" { t.Fatalf("expected resolved pvc volume, got volume=%q pvc=%v pv=%v err=%v", volumeName, pvc, pv, err) } if _, _, _, err := client.ResolvePVCVolume(context.Background(), "apps", "pending"); err == nil { t.Fatalf("expected unbound pvc error") } if _, _, _, err := client.ResolvePVCVolume(context.Background(), "apps", "missing"); err == nil { t.Fatalf("expected missing pvc error") } clientset := k8sfake.NewSimpleClientset(pvcBound) clientset.PrependReactor("get", "persistentvolumes", func(action k8stesting.Action) (bool, runtime.Object, error) { return true, nil, apierrors.NewNotFound(schema.GroupResource{Resource: "persistentvolumes"}, "pv-data") }) client = &Client{Clientset: clientset} if _, _, _, err := client.ResolvePVCVolume(context.Background(), "apps", "data"); err == nil { t.Fatalf("expected missing pv error") } } func TestListBoundPVCsAndExistsCoversFilteringSortingAndCapacityFallback(t *testing.T) { storageClass := "fast" client := &Client{Clientset: k8sfake.NewSimpleClientset( &corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{Name: "zeta", Namespace: "ops"}, Spec: corev1.PersistentVolumeClaimSpec{ VolumeName: "pv-zeta", StorageClassName: &storageClass, AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteMany}, Resources: corev1.VolumeResourceRequirements{ Requests: corev1.ResourceList{corev1.ResourceStorage: resource.MustParse("2Gi")}, }, }, Status: corev1.PersistentVolumeClaimStatus{Phase: corev1.ClaimBound}, }, &corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{Name: "alpha", Namespace: "apps"}, Spec: corev1.PersistentVolumeClaimSpec{ VolumeName: "pv-alpha", AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}, }, Status: corev1.PersistentVolumeClaimStatus{ Phase: corev1.ClaimBound, Capacity: corev1.ResourceList{corev1.ResourceStorage: resource.MustParse("5Gi")}, }, }, &corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{Name: "skip-unbound", Namespace: "apps"}, Status: corev1.PersistentVolumeClaimStatus{Phase: corev1.ClaimPending}, }, &corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{Name: "skip-novol", Namespace: "apps"}, Status: corev1.PersistentVolumeClaimStatus{Phase: corev1.ClaimBound}, }, )} items, err := client.ListBoundPVCs(context.Background()) if err != nil { t.Fatalf("list bound pvcs: %v", err) } if len(items) != 2 { t.Fatalf("expected two bound pvc summaries, got %#v", items) } if items[0].Namespace != "apps" || items[0].Name != "alpha" || items[1].Namespace != "ops" || items[1].Name != "zeta" { t.Fatalf("expected sorted pvc summaries, got %#v", items) } if items[0].Capacity != "5Gi" || items[1].Capacity != "2Gi" { t.Fatalf("expected status/spec capacity fallback, got %#v", items) } if len(items[0].AccessModes) != 1 || items[0].AccessModes[0] != string(corev1.ReadWriteOnce) { t.Fatalf("expected access modes to be captured, got %#v", items[0]) } exists, err := client.PersistentVolumeClaimExists(context.Background(), "apps", "alpha") if err != nil || !exists { t.Fatalf("expected pvc to exist, got %v %v", exists, err) } exists, err = client.PersistentVolumeClaimExists(context.Background(), "apps", "missing") if err != nil || exists { t.Fatalf("expected pvc to be missing, got %v %v", exists, err) } clientset := k8sfake.NewSimpleClientset() clientset.PrependReactor("list", "persistentvolumeclaims", func(action k8stesting.Action) (bool, runtime.Object, error) { return true, nil, apierrors.NewForbidden(schema.GroupResource{Resource: "persistentvolumeclaims"}, "", nil) }) client = &Client{Clientset: clientset} if _, err := client.ListBoundPVCs(context.Background()); err == nil { t.Fatalf("expected wrapped pvc list error") } } func TestPersistentVolumeClaimExistsWrapsUnexpectedErrors(t *testing.T) { clientset := k8sfake.NewSimpleClientset() clientset.PrependReactor("get", "persistentvolumeclaims", func(action k8stesting.Action) (bool, runtime.Object, error) { return true, nil, apierrors.NewForbidden(schema.GroupResource{Resource: "persistentvolumeclaims"}, "data", nil) }) client := &Client{Clientset: clientset} if _, err := client.PersistentVolumeClaimExists(context.Background(), "apps", "data"); err == nil { t.Fatalf("expected wrapped pvc get error") } }