Skip to content

Commit

Permalink
Merge pull request #905 from andyzhangx/inline-volume
Browse files Browse the repository at this point in the history
feat: inline volume support
  • Loading branch information
andyzhangx authored Jan 17, 2025
2 parents d0687af + 9df3b58 commit 613018d
Show file tree
Hide file tree
Showing 18 changed files with 599 additions and 8 deletions.
Binary file modified charts/latest/csi-driver-smb-v0.0.0.tgz
Binary file not shown.
5 changes: 5 additions & 0 deletions charts/latest/csi-driver-smb/templates/csi-smb-driver.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ metadata:
spec:
attachRequired: false
podInfoOnMount: true
volumeLifecycleModes:
- Persistent
{{- if .Values.feature.enableInlineVolume }}
- Ephemeral
{{- end }}
26 changes: 26 additions & 0 deletions charts/latest/csi-driver-smb/templates/rbac-csi-smb.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,30 @@ roleRef:
kind: ClusterRole
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:
name: csi-{{ .Values.rbac.name }}-node-secret-role
{{ include "smb.labels" . | indent 2 }}
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: csi-{{ .Values.rbac.name }}-node-secret-binding
{{ include "smb.labels" . | indent 2 }}
subjects:
- kind: ServiceAccount
name: {{ .Values.serviceAccount.node }}
namespace: {{ .Release.Namespace }}
roleRef:
kind: ClusterRole
name: csi-{{ .Values.rbac.name }}-node-secret-role
apiGroup: rbac.authorization.k8s.io
{{- end }}
{{ end }}
1 change: 1 addition & 0 deletions charts/latest/csi-driver-smb/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ driver:

feature:
enableGetVolumeStats: true
enableInlineVolume: true

controller:
name: csi-smb-controller
Expand Down
3 changes: 3 additions & 0 deletions deploy/csi-smb-driver.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ metadata:
spec:
attachRequired: false
podInfoOnMount: true
volumeLifecycleModes:
- Persistent
- Ephemeral
27 changes: 27 additions & 0 deletions deploy/example/nginx-pod-smb-inline-volume.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
kind: Pod
apiVersion: v1
metadata:
name: nginx-smb-inline-volume
spec:
nodeSelector:
"kubernetes.io/os": linux
containers:
- image: mcr.microsoft.com/mirror/docker/library/nginx:1.23
name: nginx-smb
command:
- "/bin/bash"
- "-c"
- set -euo pipefail; while true; do echo $(date) >> /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
22 changes: 22 additions & 0 deletions deploy/rbac-csi-smb.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
48 changes: 42 additions & 6 deletions pkg/smb/nodeserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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")
Expand Down Expand Up @@ -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")
Expand All @@ -132,7 +147,8 @@ func (d *Driver) NodeStageVolume(_ context.Context, req *csi.NodeStageVolumeRequ
secrets := req.GetSecrets()
gidPresent := checkGidPresentInMountFlags(mountFlags)

var source, subDir string
var source, subDir, secretName, secretNamespace, ephemeralVolMountOptions string
var ephemeralVol bool
subDirReplaceMap := map[string]string{}
for k, v := range context {
switch strings.ToLower(k) {
Expand All @@ -146,6 +162,14 @@ 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
case ephemeralField:
ephemeralVol = strings.EqualFold(v, trueValue)
case mountOptionsField:
ephemeralVolMountOptions = v
}
}

Expand All @@ -171,8 +195,20 @@ func (d *Driver) NodeStageVolume(_ context.Context, req *csi.NodeStageVolumeRequ
}
}

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)
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))
}
}

var mountOptions, sensitiveMountOptions []string
if runtime.GOOS == "windows" {
Expand Down Expand Up @@ -236,7 +272,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)
Expand Down
31 changes: 31 additions & 0 deletions pkg/smb/nodeserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 613018d

Please sign in to comment.