From de81f370f3b3e37266f5a305e1fffab24fdc4c65 Mon Sep 17 00:00:00 2001 From: andyzhangx Date: Sun, 8 Dec 2024 08:56:24 +0000 Subject: [PATCH] feat: add volume resize support fix fix fix --- charts/README.md | 4 +- charts/latest/csi-driver-smb-v0.0.0.tgz | Bin 4933 -> 5084 bytes .../templates/csi-smb-controller.yaml | 24 +++++ .../templates/rbac-csi-smb.yaml | 37 ++++++- charts/latest/csi-driver-smb/values.yaml | 12 ++- deploy/csi-smb-controller.yaml | 26 ++++- deploy/rbac-csi-smb.yaml | 35 ++++++- hack/verify-helm-chart.sh | 5 +- pkg/smb/controllerserver.go | 15 ++- pkg/smb/controllerserver_test.go | 53 +++++++++- pkg/smb/smb.go | 1 + test/e2e/dynamic_provisioning_test.go | 51 ++++++--- ...ically_provisioned_resize_volume_tester.go | 99 ++++++++++++++++++ test/external-e2e/testdriver.yaml | 2 + 14 files changed, 335 insertions(+), 29 deletions(-) create mode 100644 test/e2e/testsuites/dynamically_provisioned_resize_volume_tester.go diff --git a/charts/README.md b/charts/README.md index 30630ae2f73..af775ef6ecb 100644 --- a/charts/README.md +++ b/charts/README.md @@ -77,7 +77,7 @@ The following table lists the configurable parameters of the latest SMB CSI Driv | `controller.workingMountDir` | working directory for provisioner to mount smb shares temporarily | `/tmp` | | `controller.runOnMaster` | run controller on master node | `false` | | `controller.runOnControlPlane` | run controller on control plane node | `false` | -| `controller.resources.csiProvisioner.limits.memory` | csi-provisioner memory limits | `100Mi` | +| `controller.resources.csiProvisioner.limits.memory` | csi-provisioner memory limits | `400Mi` | | `controller.resources.csiProvisioner.requests.cpu` | csi-provisioner cpu requests limits | `10m` | | `controller.resources.csiProvisioner.requests.memory` | csi-provisioner memory requests limits | `20Mi` | | `controller.resources.livenessProbe.limits.memory` | liveness-probe memory limits | `300Mi` | @@ -86,7 +86,7 @@ The following table lists the configurable parameters of the latest SMB CSI Driv | `controller.resources.smb.limits.memory` | smb-csi-driver memory limits | `200Mi` | | `controller.resources.smb.requests.cpu` | smb-csi-driver cpu requests limits | `10m` | | `controller.resources.smb.requests.memory` | smb-csi-driver memory requests limits | `20Mi` | -| `controller.resources.csiResizer.limits.memory` | csi-resizer memory limits | `300Mi` | +| `controller.resources.csiResizer.limits.memory` | csi-resizer memory limits | `400Mi` | | `controller.resources.csiResizer.requests.cpu` | csi-resizer cpu requests limits | `10m` | | `controller.resources.csiResizer.requests.memory` | csi-resizer memory requests limits | `20Mi` | | `controller.affinity` | controller pod affinity | `{}` | diff --git a/charts/latest/csi-driver-smb-v0.0.0.tgz b/charts/latest/csi-driver-smb-v0.0.0.tgz index 890822e1e2a866131355ddcc3d3761dbc43d7336..ccb42500c6195d2a756958448946392ae6f38df0 100644 GIT binary patch delta 5071 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 delta 4919 zcmV-76Ugk`C&ea^JAYk!Z`(MwpT7sH{MLX`vo78^|a+j(spYCZETS|hmZ64k%yE$K^&ix8E{7gqweVxbJ>~W zfPS~T)a&(n$A^ddZ?D%Y{_XXT2jBG%Ul01d*RPKc55DX54}W_H{qIn3hi#}$v5=U5 z*V~#`v2$NZ!V&ocxF9Ta(X8LXQIvkASb~r(AB4wAB=xi5`?u(H_zs2-Ro&)wsF%BF(;&oGTa?AQP-*Z+R+u-7Z<|KRvwum8`H zNPs8cwh$U&0e{yJF&A-|5Fz=zbN7?z5Y`oB;t0t&oWW|#(Re~aT~XqRi#!}+KG)x)n9?goi8puAyYU5+R~!U{vW1i> zx#Da_lmP)==G#@`8!bvK=+BNi{Z4NceM)8!f)H1njenqme3Boj{0#9yr+>JbddPe* zhVt4BDX!~+Dw(JuPs--`4Oa8>1c^AipI6M!)J{-ptJVAnJ++@m=(A5E8Or;4hHt;q z?+li+Z3*Br;=vowV{s@IZ;u0(;3COmKrhihrV#4SuaGT^uqiCiCqgM2fvw_ehcahz z2Ms|Y$$!uoQvryK)kE|Nhx%bGKz-+keF{5@9}6XClma)-M+75h@yrp}&cz~EkGH_B z79U}+*kn~&J2K_HT4q+mq;i>7_6~!Se zvM8JU4l|%GI%*;GiSauUPTnhlI3wIe?XC=>wjK(>V(x)3tgDQHYGXj8NWQ6#1X|<| zdVjt5#6IQl?-)e3lo!P=>i2?DwLwy=qVM(0`dczfjcPrM!B#BrcuYbf=Pr79wAwHP z3Z7)#K9YTv>Va4S`jJPd2%k!j@X=XB7cd8=^Ni?p|}j`d^4IYZLgGG;`!ef+UnJu4-uU^Ac9iq+zsuhww3EI7)k zg~22*(T9+N5E_|a$^rXxG{wGB6QpqzBB79&Qt)3POQ(}Sw2q~AqA?N?x@CleU!!jd zrB&%%stSKQb#G^w-%>KVwGaQM#eX4SGZ?;q`yNLT2`6W<60;9sps`w_;Y)3Q!C*46 zsg>MHG>%64>|kA4-v&0*aU~ zS`qW#EJ{_Ap_;`K3i&oDXRX%32%{@b7$J0 z6A+3Nhp~&MQbxkM2!)T7+9tRNxSO_WzEK z4i5MGf6tL_rl2?UhwtAeoBBkciPcdbBFg5VZJahy)+%&bts92CDR_6q+Qx`UEMxHs zMHH|ArLu0zq7_e4B4Bk)zx$>X<0AR@BZPCgP+@Ue17X;sg3iq0`Vq#|Qc&35M}lt)$z|f*6Fp z{%o3N5t&+QPk{!DwFatemnm+Dna>JfmW_+X#G$8fYL$tptP|i6Pr!FZ^Fpnm-Tbbr zXaVuV14^_oJAa6s+Ine#RdX|A`zDR!+G=W4I%3+@la(uKJL4>#BsKrio6*V~xBsV& z+8^7NmKsemPNyylyOADjB-x)hpw%V*LL~GdlxU!b-;h(YSvj#$OsPV19Hg1v6js(u zt*S6I{*vAsPFVZ-dQjR+MW@iB@`lh(Xf2K|qEPSEN3kf|M`vqIx$yydwP(YBy zB@89U5`QLh{Rkn>C?(Rkrt7`1e71~>VY&mi=Ga&44`oKHl$#l)gn~9OS;@>f=o;}{IQh(^ zI+?J!9<~>WZ=Yge0xziDZRb7qVvHB(A#!?vkU0WlAW^PrawG;lK9=&T$-4FBB%Ubv zWPb;l3TWDjEji_;Gi7v@InGf}X>sTyCmxRa^0^(?KJ6qe#eez3DH{X873cp3{lQ@o z{~h)A`0rVg<2VVh(|dgZgt7|QEpS~Gtjh>VXh1_VG}jc&87!v|RRZF&unWxTt=!?M z>c%Dcrhy_CT*{)e8gN$G%@mX6Az4#IR(}h~Dw$<`>H@T<0jFfGSy+BA^8Z3%#^LAa zWtOUW(wS)3qE04|=2k5D3)ONkBfBt8fn^!VWV)ZhGetO4k7kN&D&U%Zo;kNtJ^X2b z1+Dy{#}Y8gXJ=?FDCDD?%er3xhwF6NY`JISr7qIveCk9gt1F9BeHnVoPlLIr-G44h zRlVF6nPpE4Hei`My0w(^<|wvR+3>=OBr6uhNMu%ub~CV^IpUc|ygNYbwX`eIbhfm`)LHP}5WDY)XI`rj!hDh$ax?60XLd4Z zmmWH5BCzjs5aQ(fAKsjuU7rt!KYvyfq=0?k4%nj(t4lP9<5duWd&#k5oVJrVOnnln zqOd(*?MxIZJ^7V|j9>k64IafLFjl!#6T(?>XpBVKuIKWQIfrNt;;Vlh!MwY5*Gt)X z%dMI+gl76wxIX7xnmTk7-MX-A=j_T)M9?m#*|;n}T>^iWlmt;sHI{ z5AqEzTC-fMIAH%Wr1NXW-$W)Qf7iS~?TFRV9 zSC{WDZmQsby%Ut00)2nC)2q?l%nhPy&Ssk_J+EUan5W4Sx1yT8AAjU%^-^kA)9u1N zq5`pq@v=gv=+#`6Xwt7r&uohn@*>V8^B8r}&FNJpukzrtNn=cC%;9FrK};F-%?*!5 zQr7erkVSRW78Rw&wb%}0l8iO|Pcd^z&zo9fwG=0qo&m-4A$TnGg^Lb)xiZ#8q}ly^ zb_AR-zq)cQoXg5@HGigyLvrtSyLCQnsqu_BnGp(;%uSd@+Rk8pIap8xqZ;R8T3E5H zt7NS&vFO`-4`197hkaU-R_^~>`@GE?0al&=9Sn-+e_tQ>2mAg1=SYs@z)82@{Hc)ZvD=SVf@ztUr0#ZzDF#|9IU>3O0j3Jn%cd1-KxKjdZc z%GzhVva!Z2!A(MlRip7#5yUE9wehy3@J>StRaf|`Vp7Q!zUuQ#8wJ2t9*x>2l=xK# zloHD8L@@Q8|9>Zo6csji*Ip)_y0>);CG*}Py;|At89&#aabas|gI#02*Wg5q%afvc z-h7XE`;CNC_f|=o{%`#_QL`QB<*WF_Oz(K7csCos)vvK?pm!D5^LML#D;bnEqN>V< zn)npln`vcU0oK=4`zncaHg(W9!g6}@{SRl?@BTQy{(pFV{>#VV<>_x9uio7J`s1^y z+k#wGJF3Z6U?bbX>|LkZ+qU{o{ds)6y)_<0+}_qMb62$Qw-0a6KVDp(oqxP|^ZvZ5 zWj}Kk6g3Q?F#+md!?>Jd^NjOTcutGUMe+&;lUCcKDjC~6E|&4M`L-^1)JgT6gsmcy zExd*4tbZ7Q&uc*U5wy=2K{McDY3yv12v&W>tyEC8fcPih|B$yB#ezDBrtdezh%b6dj8hvAvzcZQN zk-V=X>)Q--p4M7IztMnK^INihSjJ_!Nw;gikI5_1^ETdu1kx9cmYP)UQ#NnSTFfgQ z&(nt^&yos(D1A9> zm(gECa3A}v9*d`n{FXVH^etQ*-ASBR)o3kzX7g*KcI3CcF`AFJHVvbjtDQRxqFa3M zHAlNkqhV&~i@rIzZ9KfmvEh9*{J-Z0)s}Jb=ZRb!z5BXvIPC-6Us`~>`fhWn*nc*0 z?6r=PY;?tV%UHJ2ti@uzYZvR?+Qs^2_ndbdFW%rDp6_1kj%&jR$@-A1QFx?*IBnj^U|F^rZ2h-oa77`1{|l2m9auc%D?+n$5qsP>ZgX#DC&N;q+$k zFjr-HG-$0^8 zysMg)^;HhsOSO8T+4uM9S1>m26pyKKtd2s2zi<{u!bSgVxBt~Dc<_I=Bh?=vA(Yl3 z+d?zoBVAwuvi%CRDG~aw{(o5~^}!5{`V=LPeC20A6WD;Weydz#_9p6B#Vdb{zKXLg z;ZZC^!wxs>6U-o#8w{cCO*)txb;}b(DalSdv=u>*G47Kvs~lB4wE8~HpjFPkjSW@s z`+tG001~Uz&`*0 diff --git a/charts/latest/csi-driver-smb/templates/csi-smb-controller.yaml b/charts/latest/csi-driver-smb/templates/csi-smb-controller.yaml index b800df63698..4961f82f1d6 100755 --- a/charts/latest/csi-driver-smb/templates/csi-smb-controller.yaml +++ b/charts/latest/csi-driver-smb/templates/csi-smb-controller.yaml @@ -78,6 +78,30 @@ spec: capabilities: drop: - ALL + - name: csi-resizer +{{- if hasPrefix "/" .Values.image.csiResizer.repository }} + image: "{{ .Values.image.baseRepo }}{{ .Values.image.csiResizer.repository }}:{{ .Values.image.csiResizer.tag }}" +{{- else }} + image: "{{ .Values.image.csiResizer.repository }}:{{ .Values.image.csiResizer.tag }}" +{{- end }} + args: + - "-csi-address=$(ADDRESS)" + - "-v=2" + - "-leader-election" + - "--leader-election-namespace={{ .Release.Namespace }}" + - '-handle-volume-inuse-error=false' + env: + - name: ADDRESS + value: /csi/csi.sock + imagePullPolicy: {{ .Values.image.csiResizer.pullPolicy }} + volumeMounts: + - name: socket-dir + mountPath: /csi + resources: {{- toYaml .Values.controller.resources.csiResizer | nindent 12 }} + securityContext: + capabilities: + drop: + - ALL - name: liveness-probe {{- if hasPrefix "/" .Values.image.livenessProbe.repository }} image: "{{ .Values.image.baseRepo }}{{ .Values.image.livenessProbe.repository }}:{{ .Values.image.livenessProbe.tag }}" 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 eec11a4310b..3e13eed752e 100755 --- a/charts/latest/csi-driver-smb/templates/rbac-csi-smb.yaml +++ b/charts/latest/csi-driver-smb/templates/rbac-csi-smb.yaml @@ -48,7 +48,6 @@ rules: resources: ["secrets"] verbs: ["get"] --- - kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: @@ -62,4 +61,40 @@ roleRef: kind: ClusterRole name: {{ .Values.rbac.name }}-external-provisioner-role apiGroup: rbac.authorization.k8s.io +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Values.rbac.name }}-external-resizer-role +{{ include "smb.labels" . | indent 2 }} +rules: + - apiGroups: [""] + resources: ["persistentvolumes"] + verbs: ["get", "list", "watch", "update", "patch"] + - apiGroups: [""] + resources: ["persistentvolumeclaims"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["persistentvolumeclaims/status"] + verbs: ["update", "patch"] + - apiGroups: [""] + resources: ["events"] + verbs: ["list", "watch", "create", "update", "patch"] + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "list", "watch", "create", "update", "patch"] +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Values.rbac.name }}-csi-resizer-role +{{ include "smb.labels" . | indent 2 }} +subjects: + - kind: ServiceAccount + name: {{ .Values.serviceAccount.controller }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: ClusterRole + name: {{ .Values.rbac.name }}-external-resizer-role + apiGroup: rbac.authorization.k8s.io {{ end }} diff --git a/charts/latest/csi-driver-smb/values.yaml b/charts/latest/csi-driver-smb/values.yaml index 6e590df3707..aa828a0acb5 100755 --- a/charts/latest/csi-driver-smb/values.yaml +++ b/charts/latest/csi-driver-smb/values.yaml @@ -8,6 +8,10 @@ image: repository: /csi-provisioner tag: v5.1.0 pullPolicy: IfNotPresent + csiResizer: + repository: registry.k8s.io/sig-storage/csi-resizer + tag: v1.12.0 + pullPolicy: IfNotPresent livenessProbe: repository: /livenessprobe tag: v2.14.0 @@ -50,7 +54,13 @@ controller: resources: csiProvisioner: limits: - memory: 300Mi + memory: 400Mi + requests: + cpu: 10m + memory: 20Mi + csiResizer: + limits: + memory: 400Mi requests: cpu: 10m memory: 20Mi diff --git a/deploy/csi-smb-controller.yaml b/deploy/csi-smb-controller.yaml index e59a050d165..52632a91404 100644 --- a/deploy/csi-smb-controller.yaml +++ b/deploy/csi-smb-controller.yaml @@ -55,7 +55,31 @@ spec: resources: limits: cpu: 1 - memory: 300Mi + memory: 400Mi + requests: + cpu: 10m + memory: 20Mi + securityContext: + capabilities: + drop: + - ALL + - name: csi-resizer + image: registry.k8s.io/sig-storage/csi-resizer:v1.12.0 + args: + - "-csi-address=$(ADDRESS)" + - "-v=2" + - "-leader-election" + - "--leader-election-namespace=kube-system" + - '-handle-volume-inuse-error=false' + env: + - name: ADDRESS + value: /csi/csi.sock + volumeMounts: + - name: socket-dir + mountPath: /csi + resources: + limits: + memory: 400Mi requests: cpu: 10m memory: 20Mi diff --git a/deploy/rbac-csi-smb.yaml b/deploy/rbac-csi-smb.yaml index aa131b9e2d2..248a61c7b28 100644 --- a/deploy/rbac-csi-smb.yaml +++ b/deploy/rbac-csi-smb.yaml @@ -41,7 +41,6 @@ rules: resources: ["secrets"] verbs: ["get"] --- - kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: @@ -54,3 +53,37 @@ roleRef: kind: ClusterRole name: smb-external-provisioner-role apiGroup: rbac.authorization.k8s.io +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: smb-external-resizer-role +rules: + - apiGroups: [""] + resources: ["persistentvolumes"] + verbs: ["get", "list", "watch", "update", "patch"] + - apiGroups: [""] + resources: ["persistentvolumeclaims"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["persistentvolumeclaims/status"] + verbs: ["update", "patch"] + - apiGroups: [""] + resources: ["events"] + verbs: ["list", "watch", "create", "update", "patch"] + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "list", "watch", "create", "update", "patch"] +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: smb-csi-resizer-role +subjects: + - kind: ServiceAccount + name: csi-smb-controller-sa + namespace: kube-system +roleRef: + kind: ClusterRole + name: smb-external-resizer-role + apiGroup: rbac.authorization.k8s.io diff --git a/hack/verify-helm-chart.sh b/hack/verify-helm-chart.sh index fa4b59f40fa..cc61985f924 100755 --- a/hack/verify-helm-chart.sh +++ b/hack/verify-helm-chart.sh @@ -62,8 +62,9 @@ pip install yq --break-system-packages --ignore-installed PyYAML # Extract images from csi-smb-controller.yaml expected_csi_provisioner_image="$(cat ${PKG_ROOT}/deploy/csi-smb-controller.yaml | yq -r .spec.template.spec.containers[0].image | head -n 1)" -expected_liveness_probe_image="$(cat ${PKG_ROOT}/deploy/csi-smb-controller.yaml | yq -r .spec.template.spec.containers[1].image | head -n 1)" -expected_smb_image="$(cat ${PKG_ROOT}/deploy/csi-smb-controller.yaml | yq -r .spec.template.spec.containers[2].image | head -n 1)" +expected_csi_resizer_image="$(cat ${PKG_ROOT}/deploy/csi-smb-controller.yaml | yq -r .spec.template.spec.containers[1].image | head -n 1)" +expected_liveness_probe_image="$(cat ${PKG_ROOT}/deploy/csi-smb-controller.yaml | yq -r .spec.template.spec.containers[2].image | head -n 1)" +expected_smb_image="$(cat ${PKG_ROOT}/deploy/csi-smb-controller.yaml | yq -r .spec.template.spec.containers[3].image | head -n 1)" csi_provisioner_image="$(get_image_from_helm_chart "csiProvisioner")" validate_image "${expected_csi_provisioner_image}" "${csi_provisioner_image}" diff --git a/pkg/smb/controllerserver.go b/pkg/smb/controllerserver.go index 30db2cb4ec3..b16d0b88eaa 100644 --- a/pkg/smb/controllerserver.go +++ b/pkg/smb/controllerserver.go @@ -308,8 +308,19 @@ func (d *Driver) ListVolumes(_ context.Context, _ *csi.ListVolumesRequest) (*csi } // ControllerExpandVolume expand volume -func (d *Driver) ControllerExpandVolume(_ context.Context, _ *csi.ControllerExpandVolumeRequest) (*csi.ControllerExpandVolumeResponse, error) { - return nil, status.Error(codes.Unimplemented, "") +func (d *Driver) ControllerExpandVolume(_ context.Context, req *csi.ControllerExpandVolumeRequest) (*csi.ControllerExpandVolumeResponse, error) { + if len(req.GetVolumeId()) == 0 { + return nil, status.Error(codes.InvalidArgument, "Volume ID missing in request") + } + + if req.GetCapacityRange() == nil { + return nil, status.Error(codes.InvalidArgument, "Capacity Range missing in request") + } + + volSizeBytes := int64(req.GetCapacityRange().GetRequiredBytes()) + klog.V(2).Infof("ControllerExpandVolume(%s) successfully, currentQuota: %d bytes", req.VolumeId, volSizeBytes) + + return &csi.ControllerExpandVolumeResponse{CapacityBytes: req.GetCapacityRange().GetRequiredBytes()}, nil } func (d *Driver) CreateSnapshot(_ context.Context, _ *csi.CreateSnapshotRequest) (*csi.CreateSnapshotResponse, error) { diff --git a/pkg/smb/controllerserver_test.go b/pkg/smb/controllerserver_test.go index 3b2893255ea..f11c55853f1 100644 --- a/pkg/smb/controllerserver_test.go +++ b/pkg/smb/controllerserver_test.go @@ -398,11 +398,54 @@ func TestListVolumes(t *testing.T) { func TestControllerExpandVolume(t *testing.T) { d := NewFakeDriver() - req := csi.ControllerExpandVolumeRequest{} - resp, err := d.ControllerExpandVolume(context.Background(), &req) - assert.Nil(t, resp) - if !reflect.DeepEqual(err, status.Error(codes.Unimplemented, "")) { - t.Errorf("Unexpected error: %v", err) + + testCases := []struct { + name string + testFunc func(t *testing.T) + }{ + { + name: "volume ID missing", + testFunc: func(t *testing.T) { + req := &csi.ControllerExpandVolumeRequest{} + _, err := d.ControllerExpandVolume(context.Background(), req) + expectedErr := status.Error(codes.InvalidArgument, "Volume ID missing in request") + if !reflect.DeepEqual(err, expectedErr) { + t.Errorf("actualErr: (%v), expectedErr: (%v)", err, expectedErr) + } + }, + }, + { + name: "Capacity Range missing", + testFunc: func(t *testing.T) { + req := &csi.ControllerExpandVolumeRequest{ + VolumeId: "unit-test", + } + _, err := d.ControllerExpandVolume(context.Background(), req) + expectedErr := status.Error(codes.InvalidArgument, "Capacity Range missing in request") + if !reflect.DeepEqual(err, expectedErr) { + t.Errorf("actualErr: (%v), expectedErr: (%v)", err, expectedErr) + } + }, + }, + { + name: "Error = nil", + testFunc: func(t *testing.T) { + req := &csi.ControllerExpandVolumeRequest{ + VolumeId: "unit-test", + CapacityRange: &csi.CapacityRange{ + RequiredBytes: 10000, + }, + } + _, err := d.ControllerExpandVolume(context.Background(), req) + if !reflect.DeepEqual(err, nil) { + t.Errorf("actualErr: (%v), expectedErr: (%v)", err, nil) + } + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, tc.testFunc) } } diff --git a/pkg/smb/smb.go b/pkg/smb/smb.go index 338daae96f6..ffb18ca0d8e 100644 --- a/pkg/smb/smb.go +++ b/pkg/smb/smb.go @@ -157,6 +157,7 @@ func (d *Driver) Run(endpoint, _ string, testMode bool) { csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME, csi.ControllerServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER, csi.ControllerServiceCapability_RPC_CLONE_VOLUME, + csi.ControllerServiceCapability_RPC_EXPAND_VOLUME, }) d.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{ diff --git a/test/e2e/dynamic_provisioning_test.go b/test/e2e/dynamic_provisioning_test.go index e37fa6f6cfb..b269cd55cef 100644 --- a/test/e2e/dynamic_provisioning_test.go +++ b/test/e2e/dynamic_provisioning_test.go @@ -54,7 +54,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { }) testDriver = driver.InitSMBDriver() - ginkgo.It("should create a volume after driver restart [smb.csi.k8s.io]", func(ctx ginkgo.SpecContext) { + ginkgo.It("should create a volume after driver restart", func(ctx ginkgo.SpecContext) { ginkgo.Skip("test case is disabled since node logs would be lost after driver restart") pod := testsuites.PodDetails{ Cmd: convertToPowershellCommandIfNecessary("echo 'hello world' >> /mnt/test-1/data && while true; do sleep 3600; done"), @@ -102,7 +102,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { test.Run(ctx, cs, ns) }) - ginkgo.It("should create a volume on demand with mount options [smb.csi.k8s.io] [Windows]", func(ctx ginkgo.SpecContext) { + ginkgo.It("should create a volume on demand with mount options [Windows]", func(ctx ginkgo.SpecContext) { pods := []testsuites.PodDetails{ { Cmd: convertToPowershellCommandIfNecessary("echo 'hello world' > /mnt/test-1/data && grep 'hello world' /mnt/test-1/data"), @@ -137,7 +137,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { test.Run(ctx, cs, ns) }) - ginkgo.It("should create multiple PV objects, bind to PVCs and attach all to different pods on the same node [smb.csi.k8s.io] [Windows]", func(ctx ginkgo.SpecContext) { + ginkgo.It("should create multiple PV objects, bind to PVCs and attach all to different pods on the same node [Windows]", func(ctx ginkgo.SpecContext) { pods := []testsuites.PodDetails{ { Cmd: convertToPowershellCommandIfNecessary("while true; do echo $(date -u) >> /mnt/test-1/data; sleep 100; done"), @@ -178,7 +178,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { }) // Track issue https://github.com/kubernetes/kubernetes/issues/70505 - ginkgo.It("should create a volume on demand and mount it as readOnly in a pod [smb.csi.k8s.io]", func(ctx ginkgo.SpecContext) { + ginkgo.It("should create a volume on demand and mount it as readOnly in a pod", func(ctx ginkgo.SpecContext) { // Windows volume does not support readOnly skipIfTestingInWindowsCluster() pods := []testsuites.PodDetails{ @@ -206,7 +206,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { test.Run(ctx, cs, ns) }) - ginkgo.It("should create a deployment object, write and read to it, delete the pod and write and read to it again [smb.csi.k8s.io] [Windows]", func(ctx ginkgo.SpecContext) { + ginkgo.It("should create a deployment object, write and read to it, delete the pod and write and read to it again [Windows]", func(ctx ginkgo.SpecContext) { skipIfTestingInWindowsCluster() pod := testsuites.PodDetails{ Cmd: convertToPowershellCommandIfNecessary("echo 'hello world' >> /mnt/test-1/data && while true; do sleep 100; done"), @@ -246,7 +246,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { }) // Track issue https://github.com/kubernetes-csi/csi-driver-smb/issues/834 - ginkgo.It(fmt.Sprintf("should delete PV with reclaimPolicy even if it contains read-only subdir %q [smb.csi.k8s.io]", v1.PersistentVolumeReclaimDelete), func(ctx ginkgo.SpecContext) { + ginkgo.It(fmt.Sprintf("should delete PV with reclaimPolicy even if it contains read-only subdir %q", v1.PersistentVolumeReclaimDelete), func(ctx ginkgo.SpecContext) { skipIfTestingInWindowsCluster() reclaimPolicy := v1.PersistentVolumeReclaimDelete pod := testsuites.PodDetails{ @@ -286,7 +286,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { test.Run(ctx, cs, ns) }) - ginkgo.It(fmt.Sprintf("should delete PV with reclaimPolicy %q [smb.csi.k8s.io] [Windows]", v1.PersistentVolumeReclaimDelete), func(ctx ginkgo.SpecContext) { + ginkgo.It(fmt.Sprintf("should delete PV with reclaimPolicy %q [Windows]", v1.PersistentVolumeReclaimDelete), func(ctx ginkgo.SpecContext) { reclaimPolicy := v1.PersistentVolumeReclaimDelete volumes := []testsuites.VolumeDetails{ { @@ -302,7 +302,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { test.Run(ctx, cs, ns) }) - ginkgo.It(fmt.Sprintf("should retain PV with reclaimPolicy %q [smb.csi.k8s.io] [Windows]", v1.PersistentVolumeReclaimRetain), func(ctx ginkgo.SpecContext) { + ginkgo.It(fmt.Sprintf("should retain PV with reclaimPolicy %q [Windows]", v1.PersistentVolumeReclaimRetain), func(ctx ginkgo.SpecContext) { reclaimPolicy := v1.PersistentVolumeReclaimRetain volumes := []testsuites.VolumeDetails{ { @@ -319,7 +319,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { test.Run(ctx, cs, ns) }) - ginkgo.It("should create a pod with multiple volumes [smb.csi.k8s.io] [Windows]", func(ctx ginkgo.SpecContext) { + ginkgo.It("should create a pod with multiple volumes [Windows]", func(ctx ginkgo.SpecContext) { volumes := []testsuites.VolumeDetails{} for i := 1; i <= 6; i++ { volume := testsuites.VolumeDetails{ @@ -348,7 +348,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { test.Run(ctx, cs, ns) }) - ginkgo.It("should create a pod with volume mount subpath [smb.csi.k8s.io] [Windows]", func(ctx ginkgo.SpecContext) { + ginkgo.It("should create a pod with volume mount subpath [Windows]", func(ctx ginkgo.SpecContext) { pods := []testsuites.PodDetails{ { Cmd: convertToPowershellCommandIfNecessary("echo 'hello world' > /mnt/test-1/data && grep 'hello world' /mnt/test-1/data"), @@ -373,7 +373,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { test.Run(ctx, cs, ns) }) - ginkgo.It("should clone a volume from an existing volume [smb.csi.k8s.io]", func(ctx ginkgo.SpecContext) { + ginkgo.It("should clone a volume from an existing volume", func(ctx ginkgo.SpecContext) { skipIfTestingInWindowsCluster() pod := testsuites.PodDetails{ @@ -409,7 +409,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { test.Run(ctx, cs, ns) }) - ginkgo.It("should create a volume on demand with retaining subdir on delete [smb.csi.k8s.io]", func(ctx ginkgo.SpecContext) { + ginkgo.It("should create a volume on demand with retaining subdir on delete", func(ctx ginkgo.SpecContext) { pods := []testsuites.PodDetails{ { Cmd: convertToPowershellCommandIfNecessary("echo 'hello world' > /mnt/test-1/data && grep 'hello world' /mnt/test-1/data"), @@ -443,7 +443,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { test.Run(ctx, cs, ns) }) - ginkgo.It("should create a volume on demand with archive on archive [smb.csi.k8s.io]", func(ctx ginkgo.SpecContext) { + ginkgo.It("should create a volume on demand with archive on archive", func(ctx ginkgo.SpecContext) { pods := []testsuites.PodDetails{ { Cmd: convertToPowershellCommandIfNecessary("echo 'hello world' > /mnt/test-1/data && grep 'hello world' /mnt/test-1/data"), @@ -477,7 +477,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { test.Run(ctx, cs, ns) }) - ginkgo.It("should create a volume on demand with archive on archive subDir [smb.csi.k8s.io]", func(ctx ginkgo.SpecContext) { + ginkgo.It("should create a volume on demand with archive on archive subDir", func(ctx ginkgo.SpecContext) { pods := []testsuites.PodDetails{ { Cmd: convertToPowershellCommandIfNecessary("echo 'hello world' > /mnt/test-1/data && grep 'hello world' /mnt/test-1/data"), @@ -510,4 +510,27 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() { } test.Run(ctx, cs, ns) }) + + ginkgo.It("should create a volume on demand and resize it", func(ctx ginkgo.SpecContext) { + pods := []testsuites.PodDetails{ + { + Cmd: "echo 'hello world' > /mnt/test-1/data && grep 'hello world' /mnt/test-1/data", + Volumes: []testsuites.VolumeDetails{ + { + ClaimSize: "10Gi", + VolumeMount: testsuites.VolumeMountDetails{ + NameGenerate: "test-volume-", + MountPathGenerate: "/mnt/test-", + }, + }, + }, + }, + } + test := testsuites.DynamicallyProvisionedResizeVolumeTest{ + CSIDriver: testDriver, + Pods: pods, + StorageClassParameters: archiveSubDirStorageClassParameters, + } + test.Run(ctx, cs, ns) + }) }) diff --git a/test/e2e/testsuites/dynamically_provisioned_resize_volume_tester.go b/test/e2e/testsuites/dynamically_provisioned_resize_volume_tester.go new file mode 100644 index 00000000000..97e2f9b9c0f --- /dev/null +++ b/test/e2e/testsuites/dynamically_provisioned_resize_volume_tester.go @@ -0,0 +1,99 @@ +/* +Copyright 2024 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" + "fmt" + "strings" + "time" + + "github.com/kubernetes-csi/csi-driver-smb/test/e2e/driver" + "github.com/onsi/ginkgo/v2" + + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/kubernetes/test/e2e/framework" +) + +// DynamicallyProvisionedResizeVolumeTest will provision required StorageClass(es), PVC(s) and Pod(s) +// Waiting for the PV provisioner to resize the PV +// Testing if the PV is resized successfully. +type DynamicallyProvisionedResizeVolumeTest struct { + CSIDriver driver.DynamicPVTestDriver + Pods []PodDetails + StorageClassParameters map[string]string +} + +func (t *DynamicallyProvisionedResizeVolumeTest) Run(ctx context.Context, client clientset.Interface, namespace *v1.Namespace) { + for _, pod := range t.Pods { + tpod, cleanup := pod.SetupWithDynamicVolumes(ctx, client, namespace, t.CSIDriver, t.StorageClassParameters) + // defer must be called here for resources not get removed before using them + for i := range cleanup { + defer cleanup[i](ctx) + } + + 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) + + pvcName := tpod.pod.Spec.Volumes[0].VolumeSource.PersistentVolumeClaim.ClaimName + pvc, err := client.CoreV1().PersistentVolumeClaims(namespace.Name).Get(ctx, pvcName, metav1.GetOptions{}) + if err != nil { + framework.ExpectNoError(err, fmt.Sprintf("fail to get original pvc(%s): %v", pvcName, err)) + } + + originalSize := pvc.Spec.Resources.Requests["storage"] + delta := resource.Quantity{} + delta.Set(1024 * 1024 * 1024) + originalSize.Add(delta) + pvc.Spec.Resources.Requests["storage"] = originalSize + + ginkgo.By("resizing the pvc") + updatedPvc, err := client.CoreV1().PersistentVolumeClaims(namespace.Name).Update(ctx, pvc, metav1.UpdateOptions{}) + if err != nil { + framework.ExpectNoError(err, fmt.Sprintf("fail to resize pvc(%s): %v", pvcName, err)) + } + updatedSize := updatedPvc.Spec.Resources.Requests["storage"] + + ginkgo.By("sleep 30s waiting for resize complete") + time.Sleep(30 * time.Second) + + ginkgo.By("checking the resizing result") + newPvc, err := client.CoreV1().PersistentVolumeClaims(namespace.Name).Get(ctx, tpod.pod.Spec.Volumes[0].VolumeSource.PersistentVolumeClaim.ClaimName, metav1.GetOptions{}) + if err != nil { + framework.ExpectNoError(err, fmt.Sprintf("fail to get new pvc(%s): %v", pvcName, err)) + } + newSize := newPvc.Spec.Resources.Requests["storage"] + if !newSize.Equal(updatedSize) { + framework.Failf("newSize(%+v) is not equal to updatedSize(%+v)", newSize, updatedSize) + } + + ginkgo.By("checking the resizing PV result") + newPv, _ := client.CoreV1().PersistentVolumes().Get(ctx, updatedPvc.Spec.VolumeName, metav1.GetOptions{}) + newPvSize := newPv.Spec.Capacity["storage"] + newPvSizeStr := newPvSize.String() + "Gi" + + if !strings.Contains(newPvSizeStr, newSize.String()) { + framework.Failf("newPVCSize(%+v) is not equal to newPVSize(%+v)", newSize.String(), newPvSizeStr) + } + } +} diff --git a/test/external-e2e/testdriver.yaml b/test/external-e2e/testdriver.yaml index 6029fca0883..5d66de3c623 100644 --- a/test/external-e2e/testdriver.yaml +++ b/test/external-e2e/testdriver.yaml @@ -12,5 +12,7 @@ DriverInfo: multipods: true RWX: true fsGroup: true + controllerExpansion: true + nodeExpansion: true volumeMountGroup: true pvcDataSource: true