diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 09aa29a3b1..4fcdb9da9e 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -736,11 +736,28 @@ func (p *csiProvisioner) getPVCSource(options controller.ProvisionOptions) (*csi sourcePV, err := p.client.CoreV1().PersistentVolumes().Get(sourcePVC.Spec.VolumeName, metav1.GetOptions{}) if err != nil { - return nil, fmt.Errorf("error getting PV %s from api server: %v", sourcePVC.Spec.VolumeName, err) + klog.Warningf("error getting volume %s for PVC %s/%s: %s", sourcePVC.Spec.VolumeName, sourcePVC.Namespace, sourcePVC.Name, err) + return nil, fmt.Errorf("claim in dataSource not bound or invalid") } if sourcePV.Spec.CSI == nil { - return nil, fmt.Errorf("error getting volume source from persistantVolumeClaim:persistanceVolume %s:%s", sourcePVC.Name, sourcePVC.Spec.VolumeName) + klog.Warningf("error getting volume source from %s for PVC %s/%s", sourcePVC.Spec.VolumeName, sourcePVC.Namespace, sourcePVC.Name) + return nil, fmt.Errorf("claim in dataSource not bound or invalid") + } + + if sourcePV.Spec.CSI.Driver != options.StorageClass.Provisioner { + klog.Warningf("the source volume %s for PVC %s/%s is handled by a different CSI driver than requested by StorageClass %s", sourcePVC.Spec.VolumeName, sourcePVC.Namespace, sourcePVC.Name, *options.PVC.Spec.StorageClassName) + return nil, fmt.Errorf("claim in dataSource not bound or invalid") + } + + if sourcePV.Spec.ClaimRef == nil { + klog.Warningf("the source volume %s for PVC %s/%s is not bound", sourcePVC.Spec.VolumeName, sourcePVC.Namespace, sourcePVC.Name) + return nil, fmt.Errorf("claim in dataSource not bound or invalid") + } + + if sourcePV.Spec.ClaimRef.UID != sourcePVC.UID || sourcePV.Spec.ClaimRef.Namespace != sourcePVC.Namespace || sourcePV.Spec.ClaimRef.Name != sourcePVC.Name { + klog.Warningf("the source volume %s for PVC %s/%s is bound to a different PVC than requested", sourcePVC.Spec.VolumeName, sourcePVC.Namespace, sourcePVC.Name) + return nil, fmt.Errorf("claim in dataSource not bound or invalid") } volumeSource := csi.VolumeContentSource_Volume{ @@ -774,14 +791,31 @@ func (p *csiProvisioner) getSnapshotSource(options controller.ProvisionOptions) snapContentObj, err := p.snapshotClient.VolumesnapshotV1alpha1().VolumeSnapshotContents().Get(snapshotObj.Spec.SnapshotContentName, metav1.GetOptions{}) if err != nil { - return nil, fmt.Errorf("error getting snapshot:snapshotcontent %s:%s from api server: %v", snapshotObj.Name, snapshotObj.Spec.SnapshotContentName, err) + klog.Warningf("error getting snapshotcontent %s for snapshot %s/%s from api server: %s", snapshotObj.Spec.SnapshotContentName, snapshotObj.Namespace, snapshotObj.Name, err) + return nil, fmt.Errorf("snapshot in dataSource not bound or invalid") + } + + if snapContentObj.Spec.VolumeSnapshotRef == nil { + klog.Warningf("snapshotcontent %s for snapshot %s/%s is not bound", snapshotObj.Spec.SnapshotContentName, snapshotObj.Namespace, snapshotObj.Name) + return nil, fmt.Errorf("snapshot in dataSource not bound or invalid") + } + + if snapContentObj.Spec.VolumeSnapshotRef.UID != snapshotObj.UID || snapContentObj.Spec.VolumeSnapshotRef.Namespace != snapshotObj.Namespace || snapContentObj.Spec.VolumeSnapshotRef.Name != snapshotObj.Name { + klog.Warningf("snapshotcontent %s for snapshot %s/%s is bound to a different snapshot", snapshotObj.Spec.SnapshotContentName, snapshotObj.Namespace, snapshotObj.Name) + return nil, fmt.Errorf("snapshot in dataSource not bound or invalid") } - klog.V(5).Infof("VolumeSnapshotContent %+v", snapContentObj) if snapContentObj.Spec.VolumeSnapshotSource.CSI == nil { - return nil, fmt.Errorf("error getting snapshot source from snapshot:snapshotcontent %s:%s", snapshotObj.Name, snapshotObj.Spec.SnapshotContentName) + klog.Warningf("error getting snapshot source from snapshotcontent %s for snapshot %s/%s", snapshotObj.Spec.SnapshotContentName, snapshotObj.Namespace, snapshotObj.Name) + return nil, fmt.Errorf("snapshot in dataSource not bound or invalid") } + if snapContentObj.Spec.VolumeSnapshotSource.CSI.Driver != options.StorageClass.Provisioner { + klog.Warningf("snapshotcontent %s for snapshot %s/%s is handled by a different CSI driver than requested by StorageClass %s", snapshotObj.Spec.SnapshotContentName, snapshotObj.Namespace, snapshotObj.Name, options.StorageClass.Name) + return nil, fmt.Errorf("snapshot in dataSource not bound or invalid") + } + + klog.V(5).Infof("VolumeSnapshotContent %+v", snapContentObj) snapshotSource := csi.VolumeContentSource_Snapshot{ Snapshot: &csi.VolumeContentSource_SnapshotSource{ SnapshotId: snapContentObj.Spec.VolumeSnapshotSource.CSI.SnapshotHandle, diff --git a/pkg/controller/controller_test.go b/pkg/controller/controller_test.go index c70985a7c0..7af9814f9c 100644 --- a/pkg/controller/controller_test.go +++ b/pkg/controller/controller_test.go @@ -1656,19 +1656,24 @@ func TestProvisionFromSnapshot(t *testing.T) { } testcases := map[string]struct { - volOpts controller.ProvisionOptions - restoredVolSizeSmall bool - wrongDataSource bool - snapshotStatusReady bool - expectedPVSpec *pvSpec - expectErr bool - notPopulated bool + volOpts controller.ProvisionOptions + restoredVolSizeSmall bool + wrongDataSource bool + snapshotStatusReady bool + expectedPVSpec *pvSpec + expectErr bool + expectCSICall bool + notPopulated bool + misBoundSnapshotContentUID bool + misBoundSnapshotContentNamespace bool + misBoundSnapshotContentName bool }{ "provision with volume snapshot data source": { volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ ReclaimPolicy: &deletePolicy, Parameters: map[string]string{}, + Provisioner: "test-driver", }, PVName: "test-name", PVC: &v1.PersistentVolumeClaim{ @@ -1677,7 +1682,7 @@ func TestProvisionFromSnapshot(t *testing.T) { Annotations: driverNameAnnotation, }, Spec: v1.PersistentVolumeClaimSpec{ - Selector: nil, + StorageClassName: &snapClassName, Resources: v1.ResourceRequirements{ Requests: v1.ResourceList{ v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes, 10)), @@ -1709,11 +1714,13 @@ func TestProvisionFromSnapshot(t *testing.T) { }, }, }, + expectCSICall: true, }, "fail vol size less than snapshot size": { volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ - Parameters: map[string]string{}, + Parameters: map[string]string{}, + Provisioner: "test-driver", }, PVName: "test-name", PVC: &v1.PersistentVolumeClaim{ @@ -1722,7 +1729,7 @@ func TestProvisionFromSnapshot(t *testing.T) { Annotations: driverNameAnnotation, }, Spec: v1.PersistentVolumeClaimSpec{ - Selector: nil, + StorageClassName: &snapClassName, Resources: v1.ResourceRequirements{ Requests: v1.ResourceList{ v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(100, 10)), @@ -1744,7 +1751,8 @@ func TestProvisionFromSnapshot(t *testing.T) { "fail empty snapshot name": { volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ - Parameters: map[string]string{}, + Parameters: map[string]string{}, + Provisioner: "test-driver", }, PVName: "test-name", PVC: &v1.PersistentVolumeClaim{ @@ -1753,7 +1761,7 @@ func TestProvisionFromSnapshot(t *testing.T) { Annotations: driverNameAnnotation, }, Spec: v1.PersistentVolumeClaimSpec{ - Selector: nil, + StorageClassName: &snapClassName, Resources: v1.ResourceRequirements{ Requests: v1.ResourceList{ v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes, 10)), @@ -1774,7 +1782,8 @@ func TestProvisionFromSnapshot(t *testing.T) { "fail unsupported datasource kind": { volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ - Parameters: map[string]string{}, + Parameters: map[string]string{}, + Provisioner: "test-driver", }, PVName: "test-name", PVC: &v1.PersistentVolumeClaim{ @@ -1783,7 +1792,7 @@ func TestProvisionFromSnapshot(t *testing.T) { Annotations: driverNameAnnotation, }, Spec: v1.PersistentVolumeClaimSpec{ - Selector: nil, + StorageClassName: &snapClassName, Resources: v1.ResourceRequirements{ Requests: v1.ResourceList{ v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes, 10)), @@ -1804,7 +1813,8 @@ func TestProvisionFromSnapshot(t *testing.T) { "fail unsupported apigroup": { volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ - Parameters: map[string]string{}, + Parameters: map[string]string{}, + Provisioner: "test-driver", }, PVName: "test-name", PVC: &v1.PersistentVolumeClaim{ @@ -1813,7 +1823,7 @@ func TestProvisionFromSnapshot(t *testing.T) { Annotations: driverNameAnnotation, }, Spec: v1.PersistentVolumeClaimSpec{ - Selector: nil, + StorageClassName: &snapClassName, Resources: v1.ResourceRequirements{ Requests: v1.ResourceList{ v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes, 10)), @@ -1834,7 +1844,8 @@ func TestProvisionFromSnapshot(t *testing.T) { "fail invalid snapshot status": { volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ - Parameters: map[string]string{}, + Parameters: map[string]string{}, + Provisioner: "test-driver", }, PVName: "test-name", PVC: &v1.PersistentVolumeClaim{ @@ -1843,7 +1854,7 @@ func TestProvisionFromSnapshot(t *testing.T) { Annotations: driverNameAnnotation, }, Spec: v1.PersistentVolumeClaimSpec{ - Selector: nil, + StorageClassName: &snapClassName, Resources: v1.ResourceRequirements{ Requests: v1.ResourceList{ v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(100, 10)), @@ -1864,7 +1875,8 @@ func TestProvisionFromSnapshot(t *testing.T) { "fail not populated volume content source": { volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ - Parameters: map[string]string{}, + Parameters: map[string]string{}, + Provisioner: "test-driver", }, PVName: "test-name", PVC: &v1.PersistentVolumeClaim{ @@ -1873,7 +1885,7 @@ func TestProvisionFromSnapshot(t *testing.T) { Annotations: driverNameAnnotation, }, Spec: v1.PersistentVolumeClaimSpec{ - Selector: nil, + StorageClassName: &snapClassName, Resources: v1.ResourceRequirements{ Requests: v1.ResourceList{ v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes, 10)), @@ -1890,8 +1902,136 @@ func TestProvisionFromSnapshot(t *testing.T) { }, snapshotStatusReady: true, expectErr: true, + expectCSICall: true, notPopulated: true, }, + "fail snapshotContent bound to a different snapshot (by UID)": { + volOpts: controller.ProvisionOptions{ + StorageClass: &storagev1.StorageClass{ + Parameters: map[string]string{}, + Provisioner: "test-driver", + }, + PVName: "test-name", + PVC: &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + UID: "testid", + Annotations: driverNameAnnotation, + }, + Spec: v1.PersistentVolumeClaimSpec{ + StorageClassName: &snapClassName, + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes, 10)), + }, + }, + AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, + DataSource: &v1.TypedLocalObjectReference{ + Name: snapName, + Kind: "VolumeSnapshot", + APIGroup: &apiGrp, + }, + }, + }, + }, + snapshotStatusReady: true, + expectErr: true, + misBoundSnapshotContentUID: true, + }, + "fail snapshotContent bound to a different snapshot (by namespace)": { + volOpts: controller.ProvisionOptions{ + StorageClass: &storagev1.StorageClass{ + Parameters: map[string]string{}, + Provisioner: "test-driver", + }, + PVName: "test-name", + PVC: &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + UID: "testid", + Annotations: driverNameAnnotation, + }, + Spec: v1.PersistentVolumeClaimSpec{ + StorageClassName: &snapClassName, + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes, 10)), + }, + }, + AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, + DataSource: &v1.TypedLocalObjectReference{ + Name: snapName, + Kind: "VolumeSnapshot", + APIGroup: &apiGrp, + }, + }, + }, + }, + snapshotStatusReady: true, + expectErr: true, + misBoundSnapshotContentNamespace: true, + }, + "fail snapshotContent bound to a different snapshot (by name)": { + volOpts: controller.ProvisionOptions{ + StorageClass: &storagev1.StorageClass{ + Parameters: map[string]string{}, + Provisioner: "test-driver", + }, + PVName: "test-name", + PVC: &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + UID: "testid", + Annotations: driverNameAnnotation, + }, + Spec: v1.PersistentVolumeClaimSpec{ + StorageClassName: &snapClassName, + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes, 10)), + }, + }, + AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, + DataSource: &v1.TypedLocalObjectReference{ + Name: snapName, + Kind: "VolumeSnapshot", + APIGroup: &apiGrp, + }, + }, + }, + }, + snapshotStatusReady: true, + expectErr: true, + misBoundSnapshotContentName: true, + }, + "fail snapshotContent uses different driver than StorageClass": { + volOpts: controller.ProvisionOptions{ + StorageClass: &storagev1.StorageClass{ + Parameters: map[string]string{}, + Provisioner: "another-driver", + }, + PVName: "test-name", + PVC: &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + UID: "testid", + Annotations: driverNameAnnotation, + }, + Spec: v1.PersistentVolumeClaimSpec{ + StorageClassName: &snapClassName, + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes, 10)), + }, + }, + AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, + DataSource: &v1.TypedLocalObjectReference{ + Name: snapName, + Kind: "VolumeSnapshot", + APIGroup: &apiGrp, + }, + }, + }, + }, + snapshotStatusReady: true, + expectErr: true, + }, } tmpdir := tempDir(t) @@ -1915,6 +2055,15 @@ func TestProvisionFromSnapshot(t *testing.T) { client.AddReactor("get", "volumesnapshotcontents", func(action k8stesting.Action) (handled bool, ret runtime.Object, err error) { content := newContent("snapcontent-snapuid", snapClassName, "sid", "pv-uid", "volume", "snapuid", snapName, &requestedBytes, &timeNow) + if tc.misBoundSnapshotContentUID { + content.Spec.VolumeSnapshotRef.UID = "another-snapshot-uid" + } + if tc.misBoundSnapshotContentName { + content.Spec.VolumeSnapshotRef.Name = "another-snapshot-name" + } + if tc.misBoundSnapshotContentNamespace { + content.Spec.VolumeSnapshotRef.Namespace = "another-snapshot-namespace" + } return true, content, nil }) @@ -1934,7 +2083,7 @@ func TestProvisionFromSnapshot(t *testing.T) { // early and therefore CreateVolume is not expected to be called. // When the following if condition is met, it is a valid create volume from snapshot // operation and CreateVolume is expected to be called. - if tc.restoredVolSizeSmall == false && tc.wrongDataSource == false && tc.snapshotStatusReady { + if tc.expectCSICall { if tc.notPopulated { out.Volume.ContentSource = nil controllerServer.EXPECT().CreateVolume(gomock.Any(), gomock.Any()).Return(out, nil).Times(1) @@ -2340,6 +2489,11 @@ func TestProvisionFromPVC(t *testing.T) { srcName := "fake-pvc" invalidPVC := "invalid-pv" pvName := "test-testi" + unboundPVName := "unbound-pv" + anotherDriverPVName := "another-class" + wrongPVCName := "pv-bound-to-another-pvc-by-name" + wrongPVCNamespace := "pv-bound-to-another-pvc-by-namespace" + wrongPVCUID := "pv-bound-to-another-pvc-by-UID" deletePolicy := v1.PersistentVolumeReclaimDelete type pvSpec struct { @@ -2352,6 +2506,7 @@ func TestProvisionFromPVC(t *testing.T) { testcases := map[string]struct { volOpts controller.ProvisionOptions + clonePVName string restoredVolSizeSmall bool restoredVolSizeBig bool wrongDataSource bool @@ -2361,12 +2516,14 @@ func TestProvisionFromPVC(t *testing.T) { expectErr bool }{ "provision with pvc data source": { + clonePVName: pvName, volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ ReclaimPolicy: &deletePolicy, Parameters: map[string]string{}, + Provisioner: driverName, }, - PVName: pvName, + PVName: "new-pv-name", PVC: &v1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ UID: "testid", @@ -2408,12 +2565,14 @@ func TestProvisionFromPVC(t *testing.T) { cloneSupport: true, }, "provision with pvc data source no clone capability": { + clonePVName: pvName, volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ ReclaimPolicy: &deletePolicy, Parameters: map[string]string{}, + Provisioner: driverName, }, - PVName: pvName, + PVName: "new-pv-name", PVC: &v1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ UID: "testid", @@ -2441,12 +2600,14 @@ func TestProvisionFromPVC(t *testing.T) { expectErr: true, }, "provision with pvc data source different storage classes": { + clonePVName: pvName, volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ ReclaimPolicy: &deletePolicy, Parameters: map[string]string{}, + Provisioner: driverName, }, - PVName: pvName, + PVName: "new-pv-name", PVC: &v1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ UID: "testid", @@ -2474,12 +2635,14 @@ func TestProvisionFromPVC(t *testing.T) { expectErr: true, }, "provision with pvc data source destination too small": { + clonePVName: pvName, volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ ReclaimPolicy: &deletePolicy, Parameters: map[string]string{}, + Provisioner: driverName, }, - PVName: pvName, + PVName: "new-pv-name", PVC: &v1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ UID: "testid", @@ -2508,12 +2671,14 @@ func TestProvisionFromPVC(t *testing.T) { expectErr: true, }, "provision with pvc data source destination too large": { + clonePVName: pvName, volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ ReclaimPolicy: &deletePolicy, Parameters: map[string]string{}, + Provisioner: driverName, }, - PVName: pvName, + PVName: "new-pv-name", PVC: &v1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ UID: "testid", @@ -2542,12 +2707,14 @@ func TestProvisionFromPVC(t *testing.T) { expectErr: true, }, "provision with pvc data source not found": { + clonePVName: pvName, volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ ReclaimPolicy: &deletePolicy, Parameters: map[string]string{}, + Provisioner: driverName, }, - PVName: pvName, + PVName: "new-pv-name", PVC: &v1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ UID: "testid", @@ -2575,12 +2742,14 @@ func TestProvisionFromPVC(t *testing.T) { expectErr: true, }, "provision with source pvc storageclass nil": { + clonePVName: pvName, volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ ReclaimPolicy: &deletePolicy, Parameters: map[string]string{}, + Provisioner: driverName, }, - PVName: pvName, + PVName: "new-pv-name", PVC: &v1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ UID: "testid", @@ -2608,12 +2777,14 @@ func TestProvisionFromPVC(t *testing.T) { expectErr: true, }, "provision with requested pvc storageclass nil": { + clonePVName: pvName, volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ ReclaimPolicy: &deletePolicy, Parameters: map[string]string{}, + Provisioner: driverName, }, - PVName: pvName, + PVName: "new-pv-name", PVC: &v1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ UID: "testid", @@ -2640,12 +2811,14 @@ func TestProvisionFromPVC(t *testing.T) { expectErr: true, }, "provision with pvc data source when source pv not found": { + clonePVName: "invalid-pv", volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ ReclaimPolicy: &deletePolicy, Parameters: map[string]string{}, + Provisioner: driverName, }, - PVName: "invalid-pv", + PVName: "new-pv-name", PVC: &v1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ UID: "testid", @@ -2672,6 +2845,181 @@ func TestProvisionFromPVC(t *testing.T) { cloneSupport: true, expectErr: true, }, + "provision with PVC using unbound PV": { + clonePVName: unboundPVName, + volOpts: controller.ProvisionOptions{ + StorageClass: &storagev1.StorageClass{ + ReclaimPolicy: &deletePolicy, + Parameters: map[string]string{}, + Provisioner: driverName, + }, + PVName: "new-pv-name", + PVC: &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + UID: "testid", + Annotations: driverNameAnnotation, + }, + Spec: v1.PersistentVolumeClaimSpec{ + StorageClassName: &fakeSc1, + Selector: nil, + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes+1, 10)), + }, + }, + AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, + DataSource: &v1.TypedLocalObjectReference{ + Name: srcName, + Kind: "PersistentVolumeClaim", + }, + }, + }, + }, + pvcStatusReady: true, + expectedPVSpec: nil, + cloneSupport: true, + expectErr: true, + }, + "provision with PVC using PV bound to another PVC (with wrong UID)": { + clonePVName: wrongPVCUID, + volOpts: controller.ProvisionOptions{ + StorageClass: &storagev1.StorageClass{ + ReclaimPolicy: &deletePolicy, + Parameters: map[string]string{}, + Provisioner: driverName, + }, + PVName: "new-pv-name", + PVC: &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + UID: "testid", + Annotations: driverNameAnnotation, + }, + Spec: v1.PersistentVolumeClaimSpec{ + StorageClassName: &fakeSc1, + Selector: nil, + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes+1, 10)), + }, + }, + AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, + DataSource: &v1.TypedLocalObjectReference{ + Name: srcName, + Kind: "PersistentVolumeClaim", + }, + }, + }, + }, + pvcStatusReady: true, + expectedPVSpec: nil, + cloneSupport: true, + expectErr: true, + }, + "provision with PVC using PV bound to another PVC (with wrong namespace)": { + clonePVName: wrongPVCNamespace, + volOpts: controller.ProvisionOptions{ + StorageClass: &storagev1.StorageClass{ + ReclaimPolicy: &deletePolicy, + Parameters: map[string]string{}, + Provisioner: driverName, + }, + PVName: "new-pv-name", + PVC: &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + UID: "testid", + Annotations: driverNameAnnotation, + }, + Spec: v1.PersistentVolumeClaimSpec{ + StorageClassName: &fakeSc1, + Selector: nil, + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes+1, 10)), + }, + }, + AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, + DataSource: &v1.TypedLocalObjectReference{ + Name: srcName, + Kind: "PersistentVolumeClaim", + }, + }, + }, + }, + pvcStatusReady: true, + expectedPVSpec: nil, + cloneSupport: true, + expectErr: true, + }, + "provision with PVC using PV bound to another PVC (with wrong name)": { + clonePVName: wrongPVCName, + volOpts: controller.ProvisionOptions{ + StorageClass: &storagev1.StorageClass{ + ReclaimPolicy: &deletePolicy, + Parameters: map[string]string{}, + Provisioner: driverName, + }, + PVName: "new-pv-name", + PVC: &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + UID: "testid", + Annotations: driverNameAnnotation, + }, + Spec: v1.PersistentVolumeClaimSpec{ + StorageClassName: &fakeSc1, + Selector: nil, + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes+1, 10)), + }, + }, + AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, + DataSource: &v1.TypedLocalObjectReference{ + Name: srcName, + Kind: "PersistentVolumeClaim", + }, + }, + }, + }, + pvcStatusReady: true, + expectedPVSpec: nil, + cloneSupport: true, + expectErr: true, + }, + "provision with PVC bound to PV with wrong provisioner": { + clonePVName: anotherDriverPVName, + volOpts: controller.ProvisionOptions{ + StorageClass: &storagev1.StorageClass{ + ReclaimPolicy: &deletePolicy, + Parameters: map[string]string{}, + Provisioner: driverName, + }, + PVName: "new-pv-name", + PVC: &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + UID: "testid", + Annotations: driverNameAnnotation, + }, + Spec: v1.PersistentVolumeClaimSpec{ + StorageClassName: &fakeSc1, + Selector: nil, + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes+1, 10)), + }, + }, + AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, + DataSource: &v1.TypedLocalObjectReference{ + Name: srcName, + Kind: "PersistentVolumeClaim", + }, + }, + }, + }, + pvcStatusReady: true, + expectedPVSpec: nil, + cloneSupport: true, + expectErr: true, + }, } tmpdir := tempDir(t) @@ -2711,21 +3059,47 @@ func TestProvisionFromPVC(t *testing.T) { }, }, }, + ClaimRef: &v1.ObjectReference{ + Kind: "PersistentVolumeClaim", + Namespace: "testns", + Name: srcName, + UID: types.UID("fake-claim-uid"), + }, + StorageClassName: fakeSc1, }, } + unboundPV := pv.DeepCopy() + unboundPV.Name = unboundPVName + unboundPV.Spec.ClaimRef = nil + + anotherDriverPV := pv.DeepCopy() + anotherDriverPV.Name = anotherDriverPVName + anotherDriverPV.Spec.CSI.Driver = "wrong.com" + + pvBoundToAnotherPVCUID := pv.DeepCopy() + pvBoundToAnotherPVCUID.Name = wrongPVCUID + pvBoundToAnotherPVCUID.Spec.ClaimRef.UID = "another-claim-uid" + + pvBoundToAnotherPVCNamespace := pv.DeepCopy() + pvBoundToAnotherPVCNamespace.Name = wrongPVCNamespace + pvBoundToAnotherPVCNamespace.Spec.ClaimRef.Namespace = "another-claim-namespace" + + pvBoundToAnotherPVCName := pv.DeepCopy() + pvBoundToAnotherPVCName.Name = wrongPVCName + pvBoundToAnotherPVCName.Spec.ClaimRef.Name = "another-claim-name" + // Create a fake claim as our PVC DataSource - claim := fakeClaim(srcName, "fake-claim-uid", "1Gi", pvName, v1.ClaimBound, &fakeSc1) + claim := fakeClaim(srcName, "fake-claim-uid", "1Gi", tc.clonePVName, v1.ClaimBound, &fakeSc1) // Create a fake claim with invalid PV invalidClaim := fakeClaim(invalidPVC, "fake-claim-uid", "1Gi", "pv-not-present", v1.ClaimBound, &fakeSc1) /// Create a fake claim as source PVC storageclass nil scNilClaim := fakeClaim("pvc-sc-nil", "fake-claim-uid", "1Gi", pvName, v1.ClaimBound, nil) - clientSet = fakeclientset.NewSimpleClientset(claim, scNilClaim, pv, invalidClaim) + clientSet = fakeclientset.NewSimpleClientset(claim, scNilClaim, pv, invalidClaim, unboundPV, anotherDriverPV, pvBoundToAnotherPVCUID, pvBoundToAnotherPVCNamespace, pvBoundToAnotherPVCName) pluginCaps, controllerCaps := provisionFromPVCCapabilities() if !tc.cloneSupport { pluginCaps, controllerCaps = provisionCapabilities() - } if !tc.expectErr { volumeSource := csi.VolumeContentSource_Volume{