Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions pkg/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
_ "k8s.io/apimachinery/pkg/util/json"
"k8s.io/klog"

"github.com/golang/glog"

Expand Down Expand Up @@ -546,8 +547,12 @@ func (p *csiProvisioner) Provision(options controller.VolumeOptions) (*v1.Persis
rep := &csi.CreateVolumeResponse{}

// Resolve provision secret credentials.
// No PVC is provided when resolving provision/delete secret names, since the PVC may or may not exist at delete time.
provisionerSecretRef, err := getSecretReference(provisionerSecretParams, options.Parameters, pvName, nil)
provisionerSecretRef, err := getSecretReference(provisionerSecretParams, options.Parameters, pvName, &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: options.PVC.Name,
Namespace: options.PVC.Namespace,
},
})
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -769,18 +774,23 @@ func (p *csiProvisioner) Delete(volume *v1.PersistentVolume) error {
if len(storageClassName) != 0 {
if storageClass, err := p.client.StorageV1().StorageClasses().Get(storageClassName, metav1.GetOptions{}); err == nil {
// Resolve provision secret credentials.
// No PVC is provided when resolving provision/delete secret names, since the PVC may or may not exist at delete time.
provisionerSecretRef, err := getSecretReference(provisionerSecretParams, storageClass.Parameters, volume.Name, nil)
provisionerSecretRef, err := getSecretReference(provisionerSecretParams, storageClass.Parameters, volume.Name, &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: volume.Spec.ClaimRef.Name,
Namespace: volume.Spec.ClaimRef.Namespace,
},
})
if err != nil {
return err
}

credentials, err := getCredentials(p.client, provisionerSecretRef)
if err != nil {
return err
// Continue with deletion, as the secret may have already been deleted.
klog.Errorf("Failed to get credentials for volume %s: %s", volume.Name, err.Error())
}
req.Secrets = credentials
}

}
ctx, cancel := context.WithTimeout(context.Background(), p.timeout)
defer cancel()
Expand Down
250 changes: 249 additions & 1 deletion pkg/controller/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"google.golang.org/grpc"
"k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
storagev1 "k8s.io/api/storage/v1"
storage "k8s.io/api/storage/v1beta1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -774,6 +775,20 @@ func TestGetSecretReference(t *testing.T) {
pvc: nil,
expectRef: &v1.SecretReference{Name: "name", Namespace: "ns"},
},
"simple - valid, pvc name and namespace": {
secretParams: provisionerSecretParams,
params: map[string]string{
provisionerSecretNameKey: "param-name",
provisionerSecretNamespaceKey: "param-ns",
},
pvc: &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
Namespace: "ns",
},
},
expectRef: &v1.SecretReference{Name: "param-name", Namespace: "param-ns"},
},
"simple - invalid name": {
secretParams: nodePublishSecretParams,
params: map[string]string{nodePublishSecretNameKey: "bad name", nodePublishSecretNamespaceKey: "ns"},
Expand All @@ -788,7 +803,20 @@ func TestGetSecretReference(t *testing.T) {
expectRef: nil,
expectErr: true,
},
"template - valid": {
"template - PVC name annotations not supported for Provision and Delete": {
secretParams: provisionerSecretParams,
params: map[string]string{
prefixedProvisionerSecretNameKey: "static-${pv.name}-${pvc.namespace}-${pvc.name}-${pvc.annotations['akey']}",
},
pvc: &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
Namespace: "ns",
},
},
expectErr: true,
},
"template - valid nodepublish secret ref": {
secretParams: nodePublishSecretParams,
params: map[string]string{
nodePublishSecretNameKey: "static-${pv.name}-${pvc.namespace}-${pvc.name}-${pvc.annotations['akey']}",
Expand All @@ -804,6 +832,63 @@ func TestGetSecretReference(t *testing.T) {
},
expectRef: &v1.SecretReference{Name: "static-pvname-pvcnamespace-pvcname-avalue", Namespace: "static-pvname-pvcnamespace"},
},
"template - valid provisioner secret ref": {
secretParams: provisionerSecretParams,
params: map[string]string{
provisionerSecretNameKey: "static-provisioner-${pv.name}-${pvc.namespace}-${pvc.name}",
provisionerSecretNamespaceKey: "static-provisioner-${pv.name}-${pvc.namespace}",
},
pvName: "pvname",
pvc: &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "pvcname",
Namespace: "pvcnamespace",
},
},
expectRef: &v1.SecretReference{Name: "static-provisioner-pvname-pvcnamespace-pvcname", Namespace: "static-provisioner-pvname-pvcnamespace"},
},
"template - valid, with pvc.name": {
secretParams: provisionerSecretParams,
params: map[string]string{
provisionerSecretNameKey: "${pvc.name}",
provisionerSecretNamespaceKey: "ns",
},
pvc: &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "pvcname",
Namespace: "pvcns",
},
},
expectRef: &v1.SecretReference{Name: "pvcname", Namespace: "ns"},
},
"template - valid, provisioner with pvc name and namepsace": {
secretParams: provisionerSecretParams,
params: map[string]string{
provisionerSecretNameKey: "${pvc.name}",
provisionerSecretNamespaceKey: "${pvc.namespace}",
},
pvc: &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "pvcname",
Namespace: "pvcns",
},
},
expectRef: &v1.SecretReference{Name: "pvcname", Namespace: "pvcns"},
},
"template - valid, static pvc name and templated namespace": {
secretParams: provisionerSecretParams,
params: map[string]string{
provisionerSecretNameKey: "static-name-1",
provisionerSecretNamespaceKey: "${pvc.namespace}",
},
pvc: &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "name",
Namespace: "ns",
},
},
expectRef: &v1.SecretReference{Name: "static-name-1", Namespace: "ns"},
},
"template - invalid namespace tokens": {
secretParams: nodePublishSecretParams,
params: map[string]string{
Expand Down Expand Up @@ -1903,3 +1988,166 @@ func TestProvisionWithMountOptions(t *testing.T) {
t.Errorf("expected mount options %v; got: %v", expectedOptions, pv.Spec.MountOptions)
}
}

type deleteTestcase struct {
persistentVolume *v1.PersistentVolume
storageClass *storagev1.StorageClass
expectCapabilities bool
mockDelete bool
expectErr bool
}

// TestDelete is a test of the delete operation
func TestDelete(t *testing.T) {
tt := map[string]deleteTestcase{
"fail - nil PV": deleteTestcase{
persistentVolume: nil,
expectErr: true,
},
"fail - nil volume.Spec.CSI": deleteTestcase{
persistentVolume: &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "pv",
Namespace: "ns",
Annotations: map[string]string{
prefixedProvisionerSecretNameKey: "static-${pv.name}-${pvc.namespace}-${pvc.name}-${pvc.annotations['akey']}",
},
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{},
},
},
expectErr: true,
},
"fail - pvc.annotations not supported": deleteTestcase{
persistentVolume: &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "pv",
Namespace: "ns",
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
CSI: &v1.CSIPersistentVolumeSource{
VolumeHandle: "vol-id-1",
},
},
ClaimRef: &v1.ObjectReference{
Name: "sc-name",
Namespace: "ns",
},
StorageClassName: "sc-name",
},
},
storageClass: &storagev1.StorageClass{
ObjectMeta: metav1.ObjectMeta{
Name: "sc-name",
Namespace: "ns",
},
Parameters: map[string]string{
prefixedProvisionerSecretNameKey: "static-${pv.name}-${pvc.namespace}-${pvc.name}-${pvc.annotations['akey']}",
},
},
expectCapabilities: true,
expectErr: true,
},
"simple - valid case": deleteTestcase{
persistentVolume: &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "pv",
Namespace: "ns",
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
CSI: &v1.CSIPersistentVolumeSource{
VolumeHandle: "vol-id-1",
},
},
},
},
storageClass: &storagev1.StorageClass{
ObjectMeta: metav1.ObjectMeta{
Name: "sc-name",
Namespace: "ns",
},
Parameters: map[string]string{
prefixedProvisionerSecretNameKey: "static-${pv.name}-${pvc.namespace}-${pvc.name}",
},
},
expectCapabilities: true,
expectErr: false,
mockDelete: true,
},
"simple - valid case with ClaimRef set": deleteTestcase{
persistentVolume: &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "pv",
Namespace: "ns",
},
Spec: v1.PersistentVolumeSpec{
PersistentVolumeSource: v1.PersistentVolumeSource{
CSI: &v1.CSIPersistentVolumeSource{
VolumeHandle: "vol-id-1",
},
},
ClaimRef: &v1.ObjectReference{
Name: "pvc-name",
Namespace: "ns",
},
},
},
storageClass: &storagev1.StorageClass{
ObjectMeta: metav1.ObjectMeta{
Name: "sc-name",
Namespace: "ns",
},
Parameters: map[string]string{
prefixedProvisionerSecretNameKey: "static-${pv.name}-${pvc.namespace}-${pvc.name}",
},
},
expectCapabilities: true,
expectErr: false,
mockDelete: true,
},
}

for k, tc := range tt {
runDeleteTest(t, k, tc)
}
}

func runDeleteTest(t *testing.T, k string, tc deleteTestcase) {
t.Logf("Running test: %v", k)

mockController, driver, identityServer, controllerServer, csiConn, err := createMockServer(t)
if err != nil {
t.Fatal(err)
}
defer mockController.Finish()
defer driver.Stop()

var clientSet *fakeclientset.Clientset

if tc.storageClass != nil {
clientSet = fakeclientset.NewSimpleClientset(tc.storageClass)
} else {
clientSet = fakeclientset.NewSimpleClientset()
}

if tc.mockDelete {
controllerServer.EXPECT().DeleteVolume(gomock.Any(), gomock.Any()).Return(&csi.DeleteVolumeResponse{}, nil).Times(1)
}

if tc.expectCapabilities {
provisionMockServerSetupExpectations(identityServer, controllerServer)
}

csiProvisioner := NewCSIProvisioner(clientSet, nil, driver.Address(), 5*time.Second, "test-provisioner", "test", 5, csiConn.conn, nil)

err = csiProvisioner.Delete(tc.persistentVolume)
if tc.expectErr && err == nil {
t.Errorf("test %q: Expected error, got none", k)
}
if !tc.expectErr && err != nil {
t.Errorf("test %q: got error: %v", k, err)
}
}