From 8105a9dcb5b8ba86a95e9100d5fa12721af6d772 Mon Sep 17 00:00:00 2001 From: andyzhangx Date: Sun, 12 Jan 2025 12:16:26 +0000 Subject: [PATCH 1/2] feat: inline volume support fix fix --- charts/latest/csi-driver-smb-v0.0.0.tgz | Bin 5620 -> 5680 bytes .../templates/csi-smb-driver.yaml | 3 + .../templates/rbac-csi-smb.yaml | 25 ++++ deploy/csi-smb-driver.yaml | 3 + .../example/nginx-pod-smb-inline-volume.yaml | 27 +++++ deploy/rbac-csi-smb.yaml | 22 ++++ pkg/smb/nodeserver.go | 40 ++++++- pkg/smb/smb.go | 104 +++++++++++++++++ pkg/smb/smb_test.go | 109 ++++++++++++++++++ pkg/util/util.go | 19 ++- pkg/util/util_test.go | 55 +++++++++ test/e2e/dynamic_provisioning_test.go | 35 ++++++ .../dynamically_provisioned_inline_volume.go | 55 +++++++++ test/e2e/testsuites/specs.go | 9 ++ test/e2e/testsuites/testsuites.go | 26 +++++ 15 files changed, 525 insertions(+), 7 deletions(-) create mode 100644 deploy/example/nginx-pod-smb-inline-volume.yaml create mode 100644 test/e2e/testsuites/dynamically_provisioned_inline_volume.go diff --git a/charts/latest/csi-driver-smb-v0.0.0.tgz b/charts/latest/csi-driver-smb-v0.0.0.tgz index dbd4950fb80ad848d591a288e9babff61ba15ad6..757bd423c3d467ccb18d4c6f0c992afeb528771e 100644 GIT binary patch delta 5671 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 /mnt/smb/outfile; sleep 1; done + volumeMounts: + - name: persistent-storage + mountPath: "/mnt/smb" + readOnly: false + volumes: + - name: persistent-storage + csi: + driver: smb.csi.k8s.io + volumeAttributes: + source: //smb-server.default.svc.cluster.local/share # required + secretName: smbcreds # required, secretNamespace is the same as the pod + mountOptions: "dir_mode=0777,file_mode=0777,cache=strict,actimeo=30,nosharesock" # optional diff --git a/deploy/rbac-csi-smb.yaml b/deploy/rbac-csi-smb.yaml index 248a61c7b28..490f3dcf9eb 100644 --- a/deploy/rbac-csi-smb.yaml +++ b/deploy/rbac-csi-smb.yaml @@ -87,3 +87,25 @@ roleRef: kind: ClusterRole name: smb-external-resizer-role apiGroup: rbac.authorization.k8s.io +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: csi-smb-node-secret-role +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get"] +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: csi-smb-node-secret-binding +subjects: + - kind: ServiceAccount + name: csi-smb-node-sa + namespace: kube-system +roleRef: + kind: ClusterRole + name: csi-smb-node-secret-role + apiGroup: rbac.authorization.k8s.io diff --git a/pkg/smb/nodeserver.go b/pkg/smb/nodeserver.go index a1ca4de8642..e305a8b7855 100644 --- a/pkg/smb/nodeserver.go +++ b/pkg/smb/nodeserver.go @@ -36,13 +36,14 @@ import ( "golang.org/x/net/context" - volumehelper "github.com/kubernetes-csi/csi-driver-smb/pkg/util" + "github.com/kubernetes-csi/csi-driver-smb/pkg/util" azcache "sigs.k8s.io/cloud-provider-azure/pkg/cache" ) // NodePublishVolume mount the volume from staging to target path -func (d *Driver) NodePublishVolume(_ context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) { - if req.GetVolumeCapability() == nil { +func (d *Driver) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) { + volCap := req.GetVolumeCapability() + if volCap == nil { return nil, status.Error(codes.InvalidArgument, "Volume capability missing in request") } volumeID := req.GetVolumeId() @@ -55,6 +56,20 @@ func (d *Driver) NodePublishVolume(_ context.Context, req *csi.NodePublishVolume return nil, status.Error(codes.InvalidArgument, "Target path not provided") } + context := req.GetVolumeContext() + if context != nil && strings.EqualFold(context[ephemeralField], trueValue) { + // ephemeral volume + util.SetKeyValueInMap(context, secretNamespaceField, context[podNamespaceField]) + klog.V(2).Infof("NodePublishVolume: ephemeral volume(%s) mount on %s", volumeID, target) + _, err := d.NodeStageVolume(ctx, &csi.NodeStageVolumeRequest{ + StagingTargetPath: target, + VolumeContext: context, + VolumeCapability: volCap, + VolumeId: volumeID, + }) + return &csi.NodePublishVolumeResponse{}, err + } + source := req.GetStagingTargetPath() if len(source) == 0 { return nil, status.Error(codes.InvalidArgument, "Staging target not provided") @@ -110,7 +125,7 @@ func (d *Driver) NodeUnpublishVolume(_ context.Context, req *csi.NodeUnpublishVo } // NodeStageVolume mount the volume to a staging path -func (d *Driver) NodeStageVolume(_ context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) { +func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) { volumeID := req.GetVolumeId() if len(volumeID) == 0 { return nil, status.Error(codes.InvalidArgument, "Volume ID missing in request") @@ -132,7 +147,7 @@ func (d *Driver) NodeStageVolume(_ context.Context, req *csi.NodeStageVolumeRequ secrets := req.GetSecrets() gidPresent := checkGidPresentInMountFlags(mountFlags) - var source, subDir string + var source, subDir, secretName, secretNamespace string subDirReplaceMap := map[string]string{} for k, v := range context { switch strings.ToLower(k) { @@ -146,6 +161,10 @@ func (d *Driver) NodeStageVolume(_ context.Context, req *csi.NodeStageVolumeRequ subDirReplaceMap[pvcNameMetadata] = v case pvNameKey: subDirReplaceMap[pvNameMetadata] = v + case secretNameField: + secretName = v + case secretNamespaceField: + secretNamespace = v } } @@ -171,6 +190,15 @@ func (d *Driver) NodeStageVolume(_ context.Context, req *csi.NodeStageVolumeRequ } } + if (username == "" || password == "") && (secretName != "" && secretNamespace != "") { + klog.V(2).Infof("NodeStageVolume: getting username and password from secret %s in namespace %s", secretName, secretNamespace) + var err error + username, password, domain, err = d.GetUserNamePasswordFromSecret(ctx, secretName, secretNamespace) + if err != nil { + return nil, status.Error(codes.Internal, fmt.Sprintf("Error getting username and password from secret %s in namespace %s: %v", secretName, secretNamespace, err)) + } + } + // in guest login, username and password options are not needed requireUsernamePwdOption := !hasGuestMountOptions(mountFlags) @@ -236,7 +264,7 @@ func (d *Driver) NodeStageVolume(_ context.Context, req *csi.NodeStageVolumeRequ return Mount(d.mounter, source, targetPath, "cifs", mountOptions, sensitiveMountOptions, volumeID) } timeoutFunc := func() error { return fmt.Errorf("time out") } - if err := volumehelper.WaitUntilTimeout(90*time.Second, execFunc, timeoutFunc); err != nil { + if err := util.WaitUntilTimeout(90*time.Second, execFunc, timeoutFunc); err != nil { return nil, status.Error(codes.Internal, fmt.Sprintf("volume(%s) mount %q on %q failed with %v", volumeID, source, targetPath, err)) } klog.V(2).Infof("volume(%s) mount %q on %q succeeded", volumeID, source, targetPath) diff --git a/pkg/smb/smb.go b/pkg/smb/smb.go index a4e27abf061..d0f2365708b 100644 --- a/pkg/smb/smb.go +++ b/pkg/smb/smb.go @@ -17,12 +17,22 @@ limitations under the License. package smb import ( + "context" + "errors" "fmt" + "net" + "os" + "path/filepath" "strings" "time" "github.com/container-storage-interface/spec/lib/go/csi" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + certutil "k8s.io/client-go/util/cert" "k8s.io/klog/v2" mount "k8s.io/mount-utils" @@ -40,8 +50,12 @@ const ( subDirField = "subdir" domainField = "domain" mountOptionsField = "mountoptions" + secretNameField = "secretname" + secretNamespaceField = "secretnamespace" paramOnDelete = "ondelete" defaultDomainName = "AZURE" + ephemeralField = "csi.storage.k8s.io/ephemeral" + podNamespaceField = "csi.storage.k8s.io/pod.namespace" pvcNameKey = "csi.storage.k8s.io/pvc/name" pvcNamespaceKey = "csi.storage.k8s.io/pvc/namespace" pvNameKey = "csi.storage.k8s.io/pv/name" @@ -56,6 +70,7 @@ const ( dirMode = "dir_mode" defaultFileMode = "0777" defaultDirMode = "0777" + trueValue = "true" ) var supportedOnDeleteValues = []string{"", "delete", retain, archive} @@ -74,6 +89,7 @@ type DriverOptions struct { DefaultOnDeletePolicy string RemoveArchivedVolumePath bool EnableWindowsHostProcess bool + Kubeconfig string } // Driver implements all interfaces of CSI drivers @@ -102,6 +118,8 @@ type Driver struct { defaultOnDeletePolicy string removeArchivedVolumePath bool enableWindowsHostProcess bool + kubeconfig string + kubeClient kubernetes.Interface } // NewDriver Creates a NewCSIDriver object. Assumes vendor version is equal to driver version & @@ -116,6 +134,7 @@ func NewDriver(options *DriverOptions) *Driver { driver.removeArchivedVolumePath = options.RemoveArchivedVolumePath driver.workingMountDir = options.WorkingMountDir driver.enableWindowsHostProcess = options.EnableWindowsHostProcess + driver.kubeconfig = options.Kubeconfig driver.volumeLocks = newVolumeLocks() driver.krb5CacheDirectory = options.Krb5CacheDirectory @@ -138,6 +157,15 @@ func NewDriver(options *DriverOptions) *Driver { if driver.volDeletionCache, err = azcache.NewTimedCache(time.Minute, getter, false); err != nil { klog.Fatalf("%v", err) } + + kubeCfg, err := getKubeConfig(driver.kubeconfig, driver.enableWindowsHostProcess) + if err == nil && kubeCfg != nil { + if driver.kubeClient, err = kubernetes.NewForConfig(kubeCfg); err != nil { + klog.Warningf("NewForConfig failed with error: %v", err) + } + } else { + klog.Warningf("get kubeconfig(%s) failed with error: %v", driver.kubeconfig, err) + } return &driver } @@ -189,6 +217,24 @@ func (d *Driver) Run(endpoint, _ string, testMode bool) { s.Wait() } +// GetUserNamePasswordFromSecret get storage account key from k8s secret +// return +func (d *Driver) GetUserNamePasswordFromSecret(ctx context.Context, secretName, secretNamespace string) (string, string, string, error) { + if d.kubeClient == nil { + return "", "", "", fmt.Errorf("could not username and password from secret(%s): KubeClient is nil", secretName) + } + + secret, err := d.kubeClient.CoreV1().Secrets(secretNamespace).Get(ctx, secretName, metav1.GetOptions{}) + if err != nil { + return "", "", "", fmt.Errorf("could not get secret(%v): %v", secretName, err) + } + + username := strings.TrimSpace(string(secret.Data[usernameField][:])) + password := strings.TrimSpace(string(secret.Data[passwordField][:])) + domain := strings.TrimSpace(string(secret.Data[domainField][:])) + return username, password, domain, nil +} + func IsCorruptedDir(dir string) bool { _, pathErr := mount.PathExists(dir) return pathErr != nil && mount.IsCorruptedMnt(pathErr) @@ -279,3 +325,61 @@ func getRootDir(path string) string { parts := strings.Split(path, "/") return parts[0] } + +func getKubeConfig(kubeconfig string, enableWindowsHostProcess bool) (config *rest.Config, err error) { + if kubeconfig != "" { + if config, err = clientcmd.BuildConfigFromFlags("", kubeconfig); err != nil { + return nil, err + } + } else { + if config, err = inClusterConfig(enableWindowsHostProcess); err != nil { + return nil, err + } + } + return config, err +} + +// inClusterConfig is copied from https://github.com/kubernetes/client-go/blob/b46677097d03b964eab2d67ffbb022403996f4d4/rest/config.go#L507-L541 +// When using Windows HostProcess containers, the path "/var/run/secrets/kubernetes.io/serviceaccount/" is under host, not container. +// Then the token and ca.crt files would be not found. +// An environment variable $CONTAINER_SANDBOX_MOUNT_POINT is set upon container creation and provides the absolute host path to the container volume. +// See https://kubernetes.io/docs/tasks/configure-pod-container/create-hostprocess-pod/#volume-mounts for more details. +func inClusterConfig(enableWindowsHostProcess bool) (*rest.Config, error) { + var ( + tokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token" + rootCAFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" + ) + if enableWindowsHostProcess { + containerSandboxMountPath := os.Getenv("CONTAINER_SANDBOX_MOUNT_POINT") + if len(containerSandboxMountPath) == 0 { + return nil, errors.New("unable to load in-cluster configuration, containerSandboxMountPath must be defined") + } + tokenFile = filepath.Join(containerSandboxMountPath, tokenFile) + rootCAFile = filepath.Join(containerSandboxMountPath, rootCAFile) + } + + host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT") + if len(host) == 0 || len(port) == 0 { + return nil, rest.ErrNotInCluster + } + + token, err := os.ReadFile(tokenFile) + if err != nil { + return nil, err + } + + tlsClientConfig := rest.TLSClientConfig{} + + if _, err := certutil.NewPool(rootCAFile); err != nil { + klog.Errorf("Expected to load root CA config from %s, but got err: %v", rootCAFile, err) + } else { + tlsClientConfig.CAFile = rootCAFile + } + + return &rest.Config{ + Host: "https://" + net.JoinHostPort(host, port), + TLSClientConfig: tlsClientConfig, + BearerToken: string(token), + BearerTokenFile: tokenFile, + }, nil +} diff --git a/pkg/smb/smb_test.go b/pkg/smb/smb_test.go index 090652a7966..21761cbbd49 100644 --- a/pkg/smb/smb_test.go +++ b/pkg/smb/smb_test.go @@ -420,3 +420,112 @@ func TestGetRootPath(t *testing.T) { } } } + +func TestGetKubeConfig(t *testing.T) { + // skip for now as this is very flaky on Windows + //skipIfTestingOnWindows(t) + emptyKubeConfig := "empty-Kube-Config" + validKubeConfig := "valid-Kube-Config" + fakeContent := ` +apiVersion: v1 +clusters: +- cluster: + server: https://localhost:8080 + name: foo-cluster +contexts: +- context: + cluster: foo-cluster + user: foo-user + namespace: bar + name: foo-context +current-context: foo-context +kind: Config +users: +- name: foo-user + user: + exec: + apiVersion: client.authentication.k8s.io/v1beta1 + args: + - arg-1 + - arg-2 + command: foo-command +` + err := createTestFile(emptyKubeConfig) + if err != nil { + t.Error(err) + } + defer func() { + if err := os.Remove(emptyKubeConfig); err != nil { + t.Error(err) + } + }() + + err = createTestFile(validKubeConfig) + if err != nil { + t.Error(err) + } + defer func() { + if err := os.Remove(validKubeConfig); err != nil { + t.Error(err) + } + }() + + if err := os.WriteFile(validKubeConfig, []byte(fakeContent), 0666); err != nil { + t.Error(err) + } + + os.Setenv("CONTAINER_SANDBOX_MOUNT_POINT", "C:\\var\\lib\\kubelet\\pods\\12345678-1234-1234-1234-123456789012") + defer os.Unsetenv("CONTAINER_SANDBOX_MOUNT_POINT") + + tests := []struct { + desc string + kubeconfig string + enableWindowsHostProcess bool + expectError bool + envVariableHasConfig bool + envVariableConfigIsValid bool + }{ + { + desc: "[success] valid kube config passed", + kubeconfig: validKubeConfig, + enableWindowsHostProcess: false, + expectError: false, + envVariableHasConfig: false, + envVariableConfigIsValid: false, + }, + { + desc: "[failure] invalid kube config passed", + kubeconfig: emptyKubeConfig, + enableWindowsHostProcess: false, + expectError: true, + envVariableHasConfig: false, + envVariableConfigIsValid: false, + }, + { + desc: "[failure] empty Kubeconfig under container sandbox mount path", + kubeconfig: "", + enableWindowsHostProcess: true, + expectError: true, + envVariableHasConfig: false, + envVariableConfigIsValid: false, + }, + } + + for _, test := range tests { + _, err := getKubeConfig(test.kubeconfig, test.enableWindowsHostProcess) + receiveError := (err != nil) + if test.expectError != receiveError { + t.Errorf("desc: %s,\n input: %q, GetCloudProvider err: %v, expectErr: %v", test.desc, test.kubeconfig, err, test.expectError) + } + } +} + +func createTestFile(path string) error { + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + + return nil +} diff --git a/pkg/util/util.go b/pkg/util/util.go index 638e8391bf3..5f50d38a91c 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -17,10 +17,12 @@ limitations under the License. package util import ( - "k8s.io/klog/v2" "os" "os/exec" + "strings" "time" + + "k8s.io/klog/v2" ) const MaxPathLengthWindows = 260 @@ -65,3 +67,18 @@ func RunPowershellCmd(command string, envs ...string) ([]byte, error) { klog.V(6).Infof("Executing command: %q", cmd.String()) return cmd.CombinedOutput() } + +// SetKeyValueInMap set key/value pair in map +// key in the map is case insensitive, if key already exists, overwrite existing value +func SetKeyValueInMap(m map[string]string, key, value string) { + if m == nil { + return + } + for k := range m { + if strings.EqualFold(k, key) { + m[k] = value + return + } + } + m[key] = value +} diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go index 718b59a563f..1663e96df42 100644 --- a/pkg/util/util_test.go +++ b/pkg/util/util_test.go @@ -18,6 +18,7 @@ package util import ( "fmt" + "reflect" "testing" "time" ) @@ -73,3 +74,57 @@ func TestWaitUntilTimeout(t *testing.T) { } } } + +func TestSetKeyValueInMap(t *testing.T) { + tests := []struct { + desc string + m map[string]string + key string + value string + expected map[string]string + }{ + { + desc: "nil map", + key: "key", + value: "value", + }, + { + desc: "empty map", + m: map[string]string{}, + key: "key", + value: "value", + expected: map[string]string{"key": "value"}, + }, + { + desc: "non-empty map", + m: map[string]string{"k": "v"}, + key: "key", + value: "value", + expected: map[string]string{ + "k": "v", + "key": "value", + }, + }, + { + desc: "same key already exists", + m: map[string]string{"subDir": "value2"}, + key: "subDir", + value: "value", + expected: map[string]string{"subDir": "value"}, + }, + { + desc: "case insensitive key already exists", + m: map[string]string{"subDir": "value2"}, + key: "subdir", + value: "value", + expected: map[string]string{"subDir": "value"}, + }, + } + + for _, test := range tests { + SetKeyValueInMap(test.m, test.key, test.value) + if !reflect.DeepEqual(test.m, test.expected) { + t.Errorf("test[%s]: unexpected output: %v, expected result: %v", test.desc, test.m, test.expected) + } + } +} diff --git a/test/e2e/dynamic_provisioning_test.go b/test/e2e/dynamic_provisioning_test.go index b269cd55cef..3c88f913020 100644 --- a/test/e2e/dynamic_provisioning_test.go +++ b/test/e2e/dynamic_provisioning_test.go @@ -533,4 +533,39 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { } test.Run(ctx, cs, ns) }) + + ginkgo.It("should create an CSI inline volume", func(ctx ginkgo.SpecContext) { + pods := []testsuites.PodDetails{ + { + Cmd: convertToPowershellCommandIfNecessary("echo 'hello world' > /mnt/test-1/data && grep 'hello world' /mnt/test-1/data"), + Volumes: []testsuites.VolumeDetails{ + { + ClaimSize: "100Gi", + MountOptions: []string{ + "uid=0", + "gid=0", + "mfsymlinks", + "cache=strict", + "nosharesock", + }, + VolumeMount: testsuites.VolumeMountDetails{ + NameGenerate: "test-volume-", + MountPathGenerate: "/mnt/test-", + }, + }, + }, + IsWindows: isWindowsCluster, + WinServerVer: winServerVer, + }, + } + + test := testsuites.DynamicallyProvisionedInlineVolumeTest{ + CSIDriver: testDriver, + Pods: pods, + Source: defaultStorageClassParameters["source"], + SecretName: defaultSmbSecretName, + ReadOnly: false, + } + test.Run(ctx, cs, ns) + }) }) 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 00000000000..33be4ea614d --- /dev/null +++ b/test/e2e/testsuites/dynamically_provisioned_inline_volume.go @@ -0,0 +1,55 @@ +/* +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 ( + "context" + + "github.com/kubernetes-csi/csi-driver-smb/test/e2e/driver" + + "github.com/onsi/ginkgo/v2" + v1 "k8s.io/api/core/v1" + clientset "k8s.io/client-go/kubernetes" +) + +// DynamicallyProvisionedInlineVolumeTest will provision required Source, SecretName +// 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 + Source string + SecretName string + ReadOnly bool +} + +func (t *DynamicallyProvisionedInlineVolumeTest) Run(ctx context.Context, client clientset.Interface, namespace *v1.Namespace) { + for _, pod := range t.Pods { + tpod, cleanup := pod.SetupWithCSIInlineVolumes(client, namespace, t.Source, t.SecretName, 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(ctx) + defer tpod.Cleanup(ctx) + ginkgo.By("checking that the pods command exits with no error") + tpod.WaitForSuccess(ctx) + } +} diff --git a/test/e2e/testsuites/specs.go b/test/e2e/testsuites/specs.go index 30e744e898a..224ca50aef0 100644 --- a/test/e2e/testsuites/specs.go +++ b/test/e2e/testsuites/specs.go @@ -135,6 +135,15 @@ func (pod *PodDetails) SetupWithPreProvisionedVolumes(ctx context.Context, clien return tpod, cleanupFuncs } +func (pod *PodDetails) SetupWithCSIInlineVolumes(client clientset.Interface, namespace *v1.Namespace, source, secretName string, readOnly bool) (*TestPod, []func()) { + tpod := NewTestPod(client, namespace, pod.Cmd, pod.IsWindows, pod.WinServerVer) + cleanupFuncs := make([]func(), 0) + for n, v := range pod.Volumes { + tpod.SetupCSIInlineVolume(fmt.Sprintf("%s%d", v.VolumeMount.NameGenerate, n+1), fmt.Sprintf("%s%d", v.VolumeMount.MountPathGenerate, n+1), source, secretName, readOnly) + } + return tpod, cleanupFuncs +} + func (pod *PodDetails) SetupDeployment(ctx context.Context, client clientset.Interface, namespace *v1.Namespace, csiDriver driver.DynamicPVTestDriver, storageClassParameters map[string]string) (*TestDeployment, []func(ctx context.Context)) { cleanupFuncs := make([]func(ctx context.Context), 0) volume := pod.Volumes[0] diff --git a/test/e2e/testsuites/testsuites.go b/test/e2e/testsuites/testsuites.go index 92e17ff8eca..e5ec7a45a99 100644 --- a/test/e2e/testsuites/testsuites.go +++ b/test/e2e/testsuites/testsuites.go @@ -49,6 +49,7 @@ import ( e2epod "k8s.io/kubernetes/test/e2e/framework/pod" e2epv "k8s.io/kubernetes/test/e2e/framework/pv" imageutils "k8s.io/kubernetes/test/utils/image" + "k8s.io/utils/ptr" ) const ( @@ -637,6 +638,31 @@ func (t *TestPod) SetupRawBlockVolume(pvc *v1.PersistentVolumeClaim, name, devic t.pod.Spec.Volumes = append(t.pod.Spec.Volumes, volume) } +func (t *TestPod) SetupCSIInlineVolume(name, mountPath, source, secretName 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{ + CSI: &v1.CSIVolumeSource{ + Driver: smb.DefaultDriverName, + VolumeAttributes: map[string]string{ + "source": source, + "secretName": secretName, + "mountOptions": "dir_mode=0777,file_mode=0777,cache=strict,actimeo=30,nosharesock", + }, + ReadOnly: ptr.To(readOnly), + }, + }, + } + t.pod.Spec.Volumes = append(t.pod.Spec.Volumes, volume) +} + func (t *TestPod) SetNodeSelector(nodeSelector map[string]string) { t.pod.Spec.NodeSelector = nodeSelector } From 9df3b58e35dd3ce59902f5a6ece1eb17ef2a7952 Mon Sep 17 00:00:00 2001 From: andyzhangx Date: Mon, 13 Jan 2025 02:54:26 +0000 Subject: [PATCH 2/2] feat: add feature flag for inline volume support test: add ut fix fix fix fix fix --- charts/latest/csi-driver-smb-v0.0.0.tgz | Bin 5680 -> 5714 bytes .../templates/csi-smb-driver.yaml | 2 ++ .../templates/rbac-csi-smb.yaml | 3 +- charts/latest/csi-driver-smb/values.yaml | 1 + pkg/smb/nodeserver.go | 18 +++++++--- pkg/smb/nodeserver_test.go | 31 ++++++++++++++++++ test/e2e/dynamic_provisioning_test.go | 10 ++++++ test/e2e/suite_test.go | 3 +- test/e2e/testsuites/testsuites.go | 19 +++++++++++ 9 files changed, 80 insertions(+), 7 deletions(-) diff --git a/charts/latest/csi-driver-smb-v0.0.0.tgz b/charts/latest/csi-driver-smb-v0.0.0.tgz index 757bd423c3d467ccb18d4c6f0c992afeb528771e..d04f15ebea9179d168fd765c488f7b34c82d88cd 100644 GIT binary patch delta 5706 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 delta 5653 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 vejzoOul(5e9a diff --git a/charts/latest/csi-driver-smb/templates/csi-smb-driver.yaml b/charts/latest/csi-driver-smb/templates/csi-smb-driver.yaml index 47a5ab8bbb2..6655aa32500 100755 --- a/charts/latest/csi-driver-smb/templates/csi-smb-driver.yaml +++ b/charts/latest/csi-driver-smb/templates/csi-smb-driver.yaml @@ -8,4 +8,6 @@ spec: podInfoOnMount: true volumeLifecycleModes: - Persistent + {{- if .Values.feature.enableInlineVolume }} - Ephemeral + {{- end }} diff --git a/charts/latest/csi-driver-smb/templates/rbac-csi-smb.yaml b/charts/latest/csi-driver-smb/templates/rbac-csi-smb.yaml index 8de7b91e894..6d89878eff2 100755 --- a/charts/latest/csi-driver-smb/templates/rbac-csi-smb.yaml +++ b/charts/latest/csi-driver-smb/templates/rbac-csi-smb.yaml @@ -98,6 +98,7 @@ roleRef: name: {{ .Values.rbac.name }}-external-resizer-role apiGroup: rbac.authorization.k8s.io --- +{{- if .Values.feature.enableInlineVolume }} kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: @@ -107,7 +108,6 @@ rules: - apiGroups: [""] resources: ["secrets"] verbs: ["get"] - --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 @@ -122,4 +122,5 @@ roleRef: kind: ClusterRole name: csi-{{ .Values.rbac.name }}-node-secret-role apiGroup: rbac.authorization.k8s.io +{{- end }} {{ end }} diff --git a/charts/latest/csi-driver-smb/values.yaml b/charts/latest/csi-driver-smb/values.yaml index 4a7abb3e975..7cda0b0f43b 100755 --- a/charts/latest/csi-driver-smb/values.yaml +++ b/charts/latest/csi-driver-smb/values.yaml @@ -39,6 +39,7 @@ driver: feature: enableGetVolumeStats: true + enableInlineVolume: true controller: name: csi-smb-controller diff --git a/pkg/smb/nodeserver.go b/pkg/smb/nodeserver.go index e305a8b7855..0528003ea01 100644 --- a/pkg/smb/nodeserver.go +++ b/pkg/smb/nodeserver.go @@ -147,7 +147,8 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe secrets := req.GetSecrets() gidPresent := checkGidPresentInMountFlags(mountFlags) - var source, subDir, secretName, secretNamespace string + var source, subDir, secretName, secretNamespace, ephemeralVolMountOptions string + var ephemeralVol bool subDirReplaceMap := map[string]string{} for k, v := range context { switch strings.ToLower(k) { @@ -165,6 +166,10 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe secretName = v case secretNamespaceField: secretNamespace = v + case ephemeralField: + ephemeralVol = strings.EqualFold(v, trueValue) + case mountOptionsField: + ephemeralVolMountOptions = v } } @@ -190,7 +195,13 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe } } - if (username == "" || password == "") && (secretName != "" && secretNamespace != "") { + if ephemeralVol { + mountFlags = strings.Split(ephemeralVolMountOptions, ",") + } + + // in guest login, username and password options are not needed + requireUsernamePwdOption := !hasGuestMountOptions(mountFlags) + if ephemeralVol && requireUsernamePwdOption { klog.V(2).Infof("NodeStageVolume: getting username and password from secret %s in namespace %s", secretName, secretNamespace) var err error username, password, domain, err = d.GetUserNamePasswordFromSecret(ctx, secretName, secretNamespace) @@ -199,9 +210,6 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe } } - // in guest login, username and password options are not needed - requireUsernamePwdOption := !hasGuestMountOptions(mountFlags) - var mountOptions, sensitiveMountOptions []string if runtime.GOOS == "windows" { if domain == "" { diff --git a/pkg/smb/nodeserver_test.go b/pkg/smb/nodeserver_test.go index fa52417194c..4b87fe1a987 100644 --- a/pkg/smb/nodeserver_test.go +++ b/pkg/smb/nodeserver_test.go @@ -413,6 +413,37 @@ func TestNodePublishVolume(t *testing.T) { Readonly: true}, expectedErr: testutil.TestError{}, }, + { + desc: "[Error] failed to create ephemeral Volume", + req: &csi.NodePublishVolumeRequest{VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap}, + VolumeId: "vol_1", + TargetPath: targetTest, + StagingTargetPath: sourceTest, + Readonly: true, + VolumeContext: map[string]string{ephemeralField: "true"}, + }, + expectedErr: testutil.TestError{ + DefaultError: status.Error(codes.InvalidArgument, "source field is missing, current context: map[csi.storage.k8s.io/ephemeral:true secretnamespace:]"), + }, + }, + { + desc: "[error] failed request with ephemeral Volume", + req: &csi.NodePublishVolumeRequest{VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap}, + VolumeId: "vol_1", + TargetPath: targetTest, + StagingTargetPath: sourceTest, + Readonly: true, + VolumeContext: map[string]string{ + ephemeralField: "true", + sourceField: "source", + podNamespaceField: "podnamespace", + }, + }, + skipOnWindows: true, + expectedErr: testutil.TestError{ + DefaultError: status.Error(codes.Internal, "Error getting username and password from secret in namespace podnamespace: could not username and password from secret(): KubeClient is nil"), + }, + }, } // Setup diff --git a/test/e2e/dynamic_provisioning_test.go b/test/e2e/dynamic_provisioning_test.go index 3c88f913020..3be0cc99b38 100644 --- a/test/e2e/dynamic_provisioning_test.go +++ b/test/e2e/dynamic_provisioning_test.go @@ -535,6 +535,16 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { }) ginkgo.It("should create an CSI inline volume", func(ctx ginkgo.SpecContext) { + if winServerVer == "windows-2022" && !isWindowsHostProcessDeployment { + ginkgo.Skip("Skip inline volume test on Windows Server 2022") + } + + secretName := "smbcreds" + ginkgo.By(fmt.Sprintf("creating secret %s in namespace %s", secretName, ns.Name)) + tsecret := testsuites.CopyTestSecret(ctx, cs, "default", ns, defaultSmbSecretName) + tsecret.Create(ctx) + defer tsecret.Cleanup(ctx) + pods := []testsuites.PodDetails{ { Cmd: convertToPowershellCommandIfNecessary("echo 'hello world' > /mnt/test-1/data && grep 'hello world' /mnt/test-1/data"), diff --git a/test/e2e/suite_test.go b/test/e2e/suite_test.go index 17c931cd921..ca299fa8f76 100644 --- a/test/e2e/suite_test.go +++ b/test/e2e/suite_test.go @@ -50,6 +50,7 @@ const ( defaultSmbSource = "//smb-server.default.svc.cluster.local/share" defaultSmbSecretName = "smbcreds" defaultSmbSecretNamespace = "default" + accountNameForTest = "YW5keXNzZGZpbGUK" ) var ( @@ -173,7 +174,7 @@ var _ = ginkgo.BeforeSuite(func() { } if isWindowsHostProcessDeployment { - decodedBytes, err := base64.StdEncoding.DecodeString("YW5keXNzZGZpbGUK") + decodedBytes, err := base64.StdEncoding.DecodeString(accountNameForTest) if err != nil { log.Printf("Error decoding base64 string: %v\n", err) return diff --git a/test/e2e/testsuites/testsuites.go b/test/e2e/testsuites/testsuites.go index e5ec7a45a99..dda6afa81e4 100644 --- a/test/e2e/testsuites/testsuites.go +++ b/test/e2e/testsuites/testsuites.go @@ -717,6 +717,25 @@ func NewTestSecret(c clientset.Interface, ns *v1.Namespace, name string, data ma } } +func CopyTestSecret(ctx context.Context, c clientset.Interface, sourceNamespace string, targetNamespace *v1.Namespace, secretName string) *TestSecret { + secret, err := c.CoreV1().Secrets(sourceNamespace).Get(ctx, secretName, metav1.GetOptions{}) + framework.ExpectNoError(err) + + return &TestSecret{ + client: c, + namespace: targetNamespace, + secret: &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: targetNamespace.Name, + }, + StringData: secret.StringData, + Data: secret.Data, + Type: v1.SecretTypeOpaque, + }, + } +} + func (t *TestSecret) Create(ctx context.Context) { var err error t.secret, err = t.client.CoreV1().Secrets(t.namespace.Name).Create(ctx, t.secret, metav1.CreateOptions{})