diff --git a/pkg/azurefile/azurefile.go b/pkg/azurefile/azurefile.go index 4af2c124a2..05d5034a45 100644 --- a/pkg/azurefile/azurefile.go +++ b/pkg/azurefile/azurefile.go @@ -32,6 +32,8 @@ import ( "golang.org/x/net/context" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/klog/v2" @@ -727,3 +729,38 @@ func (d *Driver) useDataPlaneAPI(volumeID, accountName string) bool { } return useDataPlaneAPI } + +func (d *Driver) SetAzureCredentials(accountName, accountKey, secretName, secretNamespace string) (string, error) { + if d.cloud.KubeClient == nil { + klog.Warningf("could not create secret: kubeClient is nil") + return "", nil + } + if accountName == "" || accountKey == "" { + return "", fmt.Errorf("the account info is not enough, accountName(%v), accountKey(%v)", accountName, accountKey) + } + if secretNamespace == "" { + secretNamespace = defaultSecretNamespace + } + if secretName == "" { + secretName = fmt.Sprintf(secretNameTemplate, accountName) + } + secret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: secretNamespace, + Name: secretName, + }, + Data: map[string][]byte{ + defaultSecretAccountName: []byte(accountName), + defaultSecretAccountKey: []byte(accountKey), + }, + Type: "Opaque", + } + _, err := d.cloud.KubeClient.CoreV1().Secrets(secretNamespace).Create(context.TODO(), secret, metav1.CreateOptions{}) + if errors.IsAlreadyExists(err) { + err = nil + } + if err != nil { + return "", fmt.Errorf("couldn't create secret %v", err) + } + return secretName, err +} diff --git a/pkg/azurefile/controllerserver.go b/pkg/azurefile/controllerserver.go index 55d76628e0..0547530c28 100644 --- a/pkg/azurefile/controllerserver.go +++ b/pkg/azurefile/controllerserver.go @@ -352,7 +352,7 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) return nil, fmt.Errorf("failed to GetStorageAccesskey on account(%s) rg(%s), error: %v", accountOptions.Name, accountOptions.ResourceGroup, err) } } - storeSecretName, err := setAzureCredentials(d.cloud.KubeClient, accountName, accountKey, secretName, secretNamespace) + storeSecretName, err := d.SetAzureCredentials(accountName, accountKey, secretName, secretNamespace) if err != nil { return nil, status.Errorf(codes.Internal, "failed to store storage account key: %v", err) } diff --git a/pkg/azurefile/controllerserver_test.go b/pkg/azurefile/controllerserver_test.go index 75323c396b..9ad44f224c 100644 --- a/pkg/azurefile/controllerserver_test.go +++ b/pkg/azurefile/controllerserver_test.go @@ -38,6 +38,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" cloudprovider "k8s.io/cloud-provider" @@ -1930,3 +1931,80 @@ func TestListSnapshots(t *testing.T) { t.Errorf("Unexpected error: %v", err) } } + +func TestSetAzureCredentials(t *testing.T) { + d := NewFakeDriver() + d.cloud = &azure.Cloud{ + Config: azure.Config{ + ResourceGroup: "rg", + Location: "loc", + VnetName: "fake-vnet", + SubnetName: "fake-subnet", + }, + } + fakeClient := fake.NewSimpleClientset() + + tests := []struct { + desc string + kubeClient kubernetes.Interface + accountName string + accountKey string + secretName string + secretNamespace string + expectedName string + expectedErr error + }{ + { + desc: "[failure] accountName is nil", + kubeClient: fakeClient, + expectedErr: fmt.Errorf("the account info is not enough, accountName(), accountKey()"), + }, + { + desc: "[failure] accountKey is nil", + kubeClient: fakeClient, + accountName: "testName", + accountKey: "", + expectedErr: fmt.Errorf("the account info is not enough, accountName(testName), accountKey()"), + }, + { + desc: "[success] kubeClient is nil", + kubeClient: nil, + expectedErr: nil, + }, + { + desc: "[success] normal scenario", + kubeClient: fakeClient, + accountName: "testName", + accountKey: "testKey", + expectedName: "azure-storage-account-testName-secret", + expectedErr: nil, + }, + { + desc: "[success] already exist", + kubeClient: fakeClient, + accountName: "testName", + accountKey: "testKey", + expectedName: "azure-storage-account-testName-secret", + expectedErr: nil, + }, + { + desc: "[success] normal scenario using secretName", + kubeClient: fakeClient, + accountName: "testName", + accountKey: "testKey", + secretName: "secretName", + secretNamespace: "secretNamespace", + expectedName: "secretName", + expectedErr: nil, + }, + } + + for _, test := range tests { + d.cloud.KubeClient = test.kubeClient + result, err := d.SetAzureCredentials(test.accountName, test.accountKey, test.secretName, test.secretNamespace) + if result != test.expectedName || !reflect.DeepEqual(err, test.expectedErr) { + t.Errorf("desc: %s,\n input: accountName(%v), accountKey(%v),\n setAzureCredentials result: %v, expectedName: %v err: %v, expectedErr: %v", + test.desc, test.accountName, test.accountKey, result, test.expectedName, err, test.expectedErr) + } + } +} diff --git a/pkg/azurefile/utils.go b/pkg/azurefile/utils.go index 3a7db20514..aa782aed11 100644 --- a/pkg/azurefile/utils.go +++ b/pkg/azurefile/utils.go @@ -17,16 +17,10 @@ limitations under the License. package azurefile import ( - "context" - "fmt" "strings" "sync" "time" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" "k8s.io/klog/v2" ) @@ -78,41 +72,6 @@ func (lm *lockMap) unlockEntry(entry string) { lm.mutexMap[entry].Unlock() } -func setAzureCredentials(kubeClient kubernetes.Interface, accountName, accountKey, secretName, secretNamespace string) (string, error) { - if kubeClient == nil { - klog.Warningf("could not create secret: kubeClient is nil") - return "", nil - } - if accountName == "" || accountKey == "" { - return "", fmt.Errorf("the account info is not enough, accountName(%v), accountKey(%v)", accountName, accountKey) - } - if secretNamespace == "" { - secretNamespace = defaultSecretNamespace - } - if secretName == "" { - secretName = fmt.Sprintf(secretNameTemplate, accountName) - } - secret := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: secretNamespace, - Name: secretName, - }, - Data: map[string][]byte{ - defaultSecretAccountName: []byte(accountName), - defaultSecretAccountKey: []byte(accountKey), - }, - Type: "Opaque", - } - _, err := kubeClient.CoreV1().Secrets(secretNamespace).Create(context.TODO(), secret, metav1.CreateOptions{}) - if errors.IsAlreadyExists(err) { - err = nil - } - if err != nil { - return "", fmt.Errorf("couldn't create secret %v", err) - } - return secretName, err -} - func isDiskFsType(fsType string) bool { for _, v := range supportedDiskFsTypeList { if fsType == v { diff --git a/pkg/azurefile/utils_test.go b/pkg/azurefile/utils_test.go index 2511f15992..375a5e55fc 100644 --- a/pkg/azurefile/utils_test.go +++ b/pkg/azurefile/utils_test.go @@ -18,13 +18,8 @@ package azurefile import ( "errors" - "fmt" - "reflect" "testing" "time" - - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" ) func TestSimpleLockEntry(t *testing.T) { @@ -99,73 +94,6 @@ func TestUnlockEntryNotExists(t *testing.T) { testLockMap.UnlockEntry("entry1") } -func TestSetAzureCredentials(t *testing.T) { - fakeClient := fake.NewSimpleClientset() - - tests := []struct { - desc string - kubeClient kubernetes.Interface - accountName string - accountKey string - secretName string - secretNamespace string - expectedName string - expectedErr error - }{ - { - desc: "[failure] accountName is nil", - kubeClient: fakeClient, - expectedErr: fmt.Errorf("the account info is not enough, accountName(), accountKey()"), - }, - { - desc: "[failure] accountKey is nil", - kubeClient: fakeClient, - accountName: "testName", - accountKey: "", - expectedErr: fmt.Errorf("the account info is not enough, accountName(testName), accountKey()"), - }, - { - desc: "[success] kubeClient is nil", - kubeClient: nil, - expectedErr: nil, - }, - { - desc: "[success] normal scenario", - kubeClient: fakeClient, - accountName: "testName", - accountKey: "testKey", - expectedName: "azure-storage-account-testName-secret", - expectedErr: nil, - }, - { - desc: "[success] already exist", - kubeClient: fakeClient, - accountName: "testName", - accountKey: "testKey", - expectedName: "azure-storage-account-testName-secret", - expectedErr: nil, - }, - { - desc: "[success] normal scenario using secretName", - kubeClient: fakeClient, - accountName: "testName", - accountKey: "testKey", - secretName: "secretName", - secretNamespace: "secretNamespace", - expectedName: "secretName", - expectedErr: nil, - }, - } - - for _, test := range tests { - result, err := setAzureCredentials(test.kubeClient, test.accountName, test.accountKey, test.secretName, test.secretNamespace) - if result != test.expectedName || !reflect.DeepEqual(err, test.expectedErr) { - t.Errorf("desc: %s,\n input: kubeClient(%v), accountName(%v), accountKey(%v),\n setAzureCredentials result: %v, expectedName: %v err: %v, expectedErr: %v", - test.desc, test.kubeClient, test.accountName, test.accountKey, result, test.expectedName, err, test.expectedErr) - } - } -} - func TestIsDiskFsType(t *testing.T) { tests := []struct { fsType string diff --git a/test/e2e/pre_provisioning_test.go b/test/e2e/pre_provisioning_test.go index 857d12eed3..400e0945dd 100644 --- a/test/e2e/pre_provisioning_test.go +++ b/test/e2e/pre_provisioning_test.go @@ -231,7 +231,7 @@ var _ = ginkgo.Describe("Pre-Provisioned", func() { ginkgo.Fail(fmt.Sprintf("create volume error: %v", err)) } volumeID = resp.Volume.VolumeId - ginkgo.By(fmt.Sprintf("Successfully provisioned BlobFuse volume: %q\n", volumeID)) + ginkgo.By(fmt.Sprintf("Successfully provisioned Azure File volume: %q\n", volumeID)) volumeSize := fmt.Sprintf("%dGi", defaultDiskSize) reclaimPolicy := v1.PersistentVolumeReclaimRetain diff --git a/test/e2e/testsuites/dynamically_provisioned_inline_volume.go b/test/e2e/testsuites/dynamically_provisioned_inline_volume.go new file mode 100644 index 0000000000..e87fd4f01a --- /dev/null +++ b/test/e2e/testsuites/dynamically_provisioned_inline_volume.go @@ -0,0 +1,52 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package testsuites + +import ( + "sigs.k8s.io/azurefile-csi-driver/test/e2e/driver" + + "github.com/onsi/ginkgo" + v1 "k8s.io/api/core/v1" + clientset "k8s.io/client-go/kubernetes" +) + +// DynamicallyProvisionedInlineVolumeTest will provision required SecretName, ShareName +// Waiting for the PV provisioner to create an inline volume +// Testing if the Pod(s) Cmd is run with a 0 exit code +type DynamicallyProvisionedInlineVolumeTest struct { + CSIDriver driver.DynamicPVTestDriver + Pods []PodDetails + SecretName string + ShareName string + ReadOnly bool +} + +func (t *DynamicallyProvisionedInlineVolumeTest) Run(client clientset.Interface, namespace *v1.Namespace) { + for _, pod := range t.Pods { + tpod, cleanup := pod.SetupWithInlineVolumes(client, namespace, t.CSIDriver, t.SecretName, t.ShareName, t.ReadOnly) + // defer must be called here for resources not get removed before using them + for i := range cleanup { + defer cleanup[i]() + } + + ginkgo.By("deploying the pod") + tpod.Create() + defer tpod.Cleanup() + ginkgo.By("checking that the pods command exits with no error") + tpod.WaitForSuccess() + } +} diff --git a/test/e2e/testsuites/specs.go b/test/e2e/testsuites/specs.go index a5a8137a5c..68ed41382f 100644 --- a/test/e2e/testsuites/specs.go +++ b/test/e2e/testsuites/specs.go @@ -134,6 +134,15 @@ func (pod *PodDetails) SetupWithDynamicVolumesWithSubpath(client clientset.Inter return tpod, cleanupFuncs } +func (pod *PodDetails) SetupWithInlineVolumes(client clientset.Interface, namespace *v1.Namespace, csiDriver driver.DynamicPVTestDriver, secretName, shareName string, readOnly bool) (*TestPod, []func()) { + tpod := NewTestPod(client, namespace, pod.Cmd, pod.IsWindows) + cleanupFuncs := make([]func(), 0) + for n, v := range pod.Volumes { + tpod.SetupInlineVolume(fmt.Sprintf("%s%d", v.VolumeMount.NameGenerate, n+1), fmt.Sprintf("%s%d", v.VolumeMount.MountPathGenerate, n+1), secretName, shareName, readOnly) + } + return tpod, cleanupFuncs +} + func (pod *PodDetails) SetupWithPreProvisionedVolumes(client clientset.Interface, namespace *v1.Namespace, csiDriver driver.PreProvisionedVolumeTestDriver) (*TestPod, []func()) { tpod := NewTestPod(client, namespace, pod.Cmd, pod.IsWindows) cleanupFuncs := make([]func(), 0) diff --git a/test/e2e/testsuites/testsuites.go b/test/e2e/testsuites/testsuites.go index f8a008fc3f..6e70a90059 100644 --- a/test/e2e/testsuites/testsuites.go +++ b/test/e2e/testsuites/testsuites.go @@ -782,6 +782,27 @@ func (t *TestPod) SetupVolumeMountWithSubpath(pvc *v1.PersistentVolumeClaim, nam t.pod.Spec.Volumes = append(t.pod.Spec.Volumes, volume) } +func (t *TestPod) SetupInlineVolume(name, mountPath, secretName, shareName string, readOnly bool) { + volumeMount := v1.VolumeMount{ + Name: name, + MountPath: mountPath, + ReadOnly: readOnly, + } + t.pod.Spec.Containers[0].VolumeMounts = append(t.pod.Spec.Containers[0].VolumeMounts, volumeMount) + + volume := v1.Volume{ + Name: name, + VolumeSource: v1.VolumeSource{ + AzureFile: &v1.AzureFileVolumeSource{ + SecretName: secretName, + ShareName: shareName, + ReadOnly: readOnly, + }, + }, + } + t.pod.Spec.Volumes = append(t.pod.Spec.Volumes, volume) +} + func (t *TestPod) SetNodeSelector(nodeSelector map[string]string) { t.pod.Spec.NodeSelector = nodeSelector }