Skip to content

Commit 613018d

Browse files
authored
Merge pull request #905 from andyzhangx/inline-volume
feat: inline volume support
2 parents d0687af + 9df3b58 commit 613018d

18 files changed

+599
-8
lines changed
94 Bytes
Binary file not shown.

charts/latest/csi-driver-smb/templates/csi-smb-driver.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,8 @@ metadata:
66
spec:
77
attachRequired: false
88
podInfoOnMount: true
9+
volumeLifecycleModes:
10+
- Persistent
11+
{{- if .Values.feature.enableInlineVolume }}
12+
- Ephemeral
13+
{{- end }}

charts/latest/csi-driver-smb/templates/rbac-csi-smb.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,30 @@ roleRef:
9797
kind: ClusterRole
9898
name: {{ .Values.rbac.name }}-external-resizer-role
9999
apiGroup: rbac.authorization.k8s.io
100+
---
101+
{{- if .Values.feature.enableInlineVolume }}
102+
kind: ClusterRole
103+
apiVersion: rbac.authorization.k8s.io/v1
104+
metadata:
105+
name: csi-{{ .Values.rbac.name }}-node-secret-role
106+
{{ include "smb.labels" . | indent 2 }}
107+
rules:
108+
- apiGroups: [""]
109+
resources: ["secrets"]
110+
verbs: ["get"]
111+
---
112+
kind: ClusterRoleBinding
113+
apiVersion: rbac.authorization.k8s.io/v1
114+
metadata:
115+
name: csi-{{ .Values.rbac.name }}-node-secret-binding
116+
{{ include "smb.labels" . | indent 2 }}
117+
subjects:
118+
- kind: ServiceAccount
119+
name: {{ .Values.serviceAccount.node }}
120+
namespace: {{ .Release.Namespace }}
121+
roleRef:
122+
kind: ClusterRole
123+
name: csi-{{ .Values.rbac.name }}-node-secret-role
124+
apiGroup: rbac.authorization.k8s.io
125+
{{- end }}
100126
{{ end }}

charts/latest/csi-driver-smb/values.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ driver:
3939

4040
feature:
4141
enableGetVolumeStats: true
42+
enableInlineVolume: true
4243

4344
controller:
4445
name: csi-smb-controller

deploy/csi-smb-driver.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ metadata:
66
spec:
77
attachRequired: false
88
podInfoOnMount: true
9+
volumeLifecycleModes:
10+
- Persistent
11+
- Ephemeral
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
kind: Pod
3+
apiVersion: v1
4+
metadata:
5+
name: nginx-smb-inline-volume
6+
spec:
7+
nodeSelector:
8+
"kubernetes.io/os": linux
9+
containers:
10+
- image: mcr.microsoft.com/mirror/docker/library/nginx:1.23
11+
name: nginx-smb
12+
command:
13+
- "/bin/bash"
14+
- "-c"
15+
- set -euo pipefail; while true; do echo $(date) >> /mnt/smb/outfile; sleep 1; done
16+
volumeMounts:
17+
- name: persistent-storage
18+
mountPath: "/mnt/smb"
19+
readOnly: false
20+
volumes:
21+
- name: persistent-storage
22+
csi:
23+
driver: smb.csi.k8s.io
24+
volumeAttributes:
25+
source: //smb-server.default.svc.cluster.local/share # required
26+
secretName: smbcreds # required, secretNamespace is the same as the pod
27+
mountOptions: "dir_mode=0777,file_mode=0777,cache=strict,actimeo=30,nosharesock" # optional

deploy/rbac-csi-smb.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,25 @@ roleRef:
8787
kind: ClusterRole
8888
name: smb-external-resizer-role
8989
apiGroup: rbac.authorization.k8s.io
90+
---
91+
kind: ClusterRole
92+
apiVersion: rbac.authorization.k8s.io/v1
93+
metadata:
94+
name: csi-smb-node-secret-role
95+
rules:
96+
- apiGroups: [""]
97+
resources: ["secrets"]
98+
verbs: ["get"]
99+
---
100+
kind: ClusterRoleBinding
101+
apiVersion: rbac.authorization.k8s.io/v1
102+
metadata:
103+
name: csi-smb-node-secret-binding
104+
subjects:
105+
- kind: ServiceAccount
106+
name: csi-smb-node-sa
107+
namespace: kube-system
108+
roleRef:
109+
kind: ClusterRole
110+
name: csi-smb-node-secret-role
111+
apiGroup: rbac.authorization.k8s.io

pkg/smb/nodeserver.go

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,14 @@ import (
3636

3737
"golang.org/x/net/context"
3838

39-
volumehelper "github.com/kubernetes-csi/csi-driver-smb/pkg/util"
39+
"github.com/kubernetes-csi/csi-driver-smb/pkg/util"
4040
azcache "sigs.k8s.io/cloud-provider-azure/pkg/cache"
4141
)
4242

4343
// NodePublishVolume mount the volume from staging to target path
44-
func (d *Driver) NodePublishVolume(_ context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
45-
if req.GetVolumeCapability() == nil {
44+
func (d *Driver) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
45+
volCap := req.GetVolumeCapability()
46+
if volCap == nil {
4647
return nil, status.Error(codes.InvalidArgument, "Volume capability missing in request")
4748
}
4849
volumeID := req.GetVolumeId()
@@ -55,6 +56,20 @@ func (d *Driver) NodePublishVolume(_ context.Context, req *csi.NodePublishVolume
5556
return nil, status.Error(codes.InvalidArgument, "Target path not provided")
5657
}
5758

59+
context := req.GetVolumeContext()
60+
if context != nil && strings.EqualFold(context[ephemeralField], trueValue) {
61+
// ephemeral volume
62+
util.SetKeyValueInMap(context, secretNamespaceField, context[podNamespaceField])
63+
klog.V(2).Infof("NodePublishVolume: ephemeral volume(%s) mount on %s", volumeID, target)
64+
_, err := d.NodeStageVolume(ctx, &csi.NodeStageVolumeRequest{
65+
StagingTargetPath: target,
66+
VolumeContext: context,
67+
VolumeCapability: volCap,
68+
VolumeId: volumeID,
69+
})
70+
return &csi.NodePublishVolumeResponse{}, err
71+
}
72+
5873
source := req.GetStagingTargetPath()
5974
if len(source) == 0 {
6075
return nil, status.Error(codes.InvalidArgument, "Staging target not provided")
@@ -110,7 +125,7 @@ func (d *Driver) NodeUnpublishVolume(_ context.Context, req *csi.NodeUnpublishVo
110125
}
111126

112127
// NodeStageVolume mount the volume to a staging path
113-
func (d *Driver) NodeStageVolume(_ context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {
128+
func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {
114129
volumeID := req.GetVolumeId()
115130
if len(volumeID) == 0 {
116131
return nil, status.Error(codes.InvalidArgument, "Volume ID missing in request")
@@ -132,7 +147,8 @@ func (d *Driver) NodeStageVolume(_ context.Context, req *csi.NodeStageVolumeRequ
132147
secrets := req.GetSecrets()
133148
gidPresent := checkGidPresentInMountFlags(mountFlags)
134149

135-
var source, subDir string
150+
var source, subDir, secretName, secretNamespace, ephemeralVolMountOptions string
151+
var ephemeralVol bool
136152
subDirReplaceMap := map[string]string{}
137153
for k, v := range context {
138154
switch strings.ToLower(k) {
@@ -146,6 +162,14 @@ func (d *Driver) NodeStageVolume(_ context.Context, req *csi.NodeStageVolumeRequ
146162
subDirReplaceMap[pvcNameMetadata] = v
147163
case pvNameKey:
148164
subDirReplaceMap[pvNameMetadata] = v
165+
case secretNameField:
166+
secretName = v
167+
case secretNamespaceField:
168+
secretNamespace = v
169+
case ephemeralField:
170+
ephemeralVol = strings.EqualFold(v, trueValue)
171+
case mountOptionsField:
172+
ephemeralVolMountOptions = v
149173
}
150174
}
151175

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

198+
if ephemeralVol {
199+
mountFlags = strings.Split(ephemeralVolMountOptions, ",")
200+
}
201+
174202
// in guest login, username and password options are not needed
175203
requireUsernamePwdOption := !hasGuestMountOptions(mountFlags)
204+
if ephemeralVol && requireUsernamePwdOption {
205+
klog.V(2).Infof("NodeStageVolume: getting username and password from secret %s in namespace %s", secretName, secretNamespace)
206+
var err error
207+
username, password, domain, err = d.GetUserNamePasswordFromSecret(ctx, secretName, secretNamespace)
208+
if err != nil {
209+
return nil, status.Error(codes.Internal, fmt.Sprintf("Error getting username and password from secret %s in namespace %s: %v", secretName, secretNamespace, err))
210+
}
211+
}
176212

177213
var mountOptions, sensitiveMountOptions []string
178214
if runtime.GOOS == "windows" {
@@ -236,7 +272,7 @@ func (d *Driver) NodeStageVolume(_ context.Context, req *csi.NodeStageVolumeRequ
236272
return Mount(d.mounter, source, targetPath, "cifs", mountOptions, sensitiveMountOptions, volumeID)
237273
}
238274
timeoutFunc := func() error { return fmt.Errorf("time out") }
239-
if err := volumehelper.WaitUntilTimeout(90*time.Second, execFunc, timeoutFunc); err != nil {
275+
if err := util.WaitUntilTimeout(90*time.Second, execFunc, timeoutFunc); err != nil {
240276
return nil, status.Error(codes.Internal, fmt.Sprintf("volume(%s) mount %q on %q failed with %v", volumeID, source, targetPath, err))
241277
}
242278
klog.V(2).Infof("volume(%s) mount %q on %q succeeded", volumeID, source, targetPath)

pkg/smb/nodeserver_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,37 @@ func TestNodePublishVolume(t *testing.T) {
413413
Readonly: true},
414414
expectedErr: testutil.TestError{},
415415
},
416+
{
417+
desc: "[Error] failed to create ephemeral Volume",
418+
req: &csi.NodePublishVolumeRequest{VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
419+
VolumeId: "vol_1",
420+
TargetPath: targetTest,
421+
StagingTargetPath: sourceTest,
422+
Readonly: true,
423+
VolumeContext: map[string]string{ephemeralField: "true"},
424+
},
425+
expectedErr: testutil.TestError{
426+
DefaultError: status.Error(codes.InvalidArgument, "source field is missing, current context: map[csi.storage.k8s.io/ephemeral:true secretnamespace:]"),
427+
},
428+
},
429+
{
430+
desc: "[error] failed request with ephemeral Volume",
431+
req: &csi.NodePublishVolumeRequest{VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
432+
VolumeId: "vol_1",
433+
TargetPath: targetTest,
434+
StagingTargetPath: sourceTest,
435+
Readonly: true,
436+
VolumeContext: map[string]string{
437+
ephemeralField: "true",
438+
sourceField: "source",
439+
podNamespaceField: "podnamespace",
440+
},
441+
},
442+
skipOnWindows: true,
443+
expectedErr: testutil.TestError{
444+
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"),
445+
},
446+
},
416447
}
417448

418449
// Setup

0 commit comments

Comments
 (0)