Merge pull request #811 from andyzhangx/expand-vol
feat: add volume resize support
This commit is contained in:
commit
f7ce93f341
@ -79,6 +79,9 @@ The following table lists the configurable parameters of the latest NFS CSI Driv
|
||||
| `controller.resources.csiProvisioner.limits.memory` | csi-provisioner memory limits | 100Mi |
|
||||
| `controller.resources.csiProvisioner.requests.cpu` | csi-provisioner cpu requests limits | 10m |
|
||||
| `controller.resources.csiProvisioner.requests.memory` | csi-provisioner memory requests limits | 20Mi |
|
||||
| `controller.resources.csiResizer.limits.memory` | csi-resizer memory limits | 400Mi |
|
||||
| `controller.resources.csiResizer.requests.cpu` | csi-resizer cpu requests | 10m |
|
||||
| `controller.resources.csiResizer.requests.memory` | csi-resizer memory requests | 20Mi |
|
||||
| `controller.resources.livenessProbe.limits.memory` | liveness-probe memory limits | 100Mi |
|
||||
| `controller.resources.livenessProbe.requests.cpu` | liveness-probe cpu requests limits | 10m |
|
||||
| `controller.resources.livenessProbe.requests.memory` | liveness-probe memory requests limits | 20Mi |
|
||||
|
||||
Binary file not shown.
@ -75,6 +75,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: csi-snapshotter
|
||||
{{- if hasPrefix "/" .Values.image.csiSnapshotter.repository }}
|
||||
image: "{{ .Values.image.baseRepo }}{{ .Values.image.csiSnapshotter.repository }}:{{ .Values.image.csiSnapshotter.tag }}"
|
||||
|
||||
@ -57,6 +57,42 @@ rules:
|
||||
resources: ["secrets"]
|
||||
verbs: ["get"]
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: {{ .Values.rbac.name }}-external-resizer-role
|
||||
{{ include "nfs.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 "nfs.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
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
|
||||
@ -9,6 +9,10 @@ image:
|
||||
repository: registry.k8s.io/sig-storage/csi-provisioner
|
||||
tag: v5.1.0
|
||||
pullPolicy: IfNotPresent
|
||||
csiResizer:
|
||||
repository: registry.k8s.io/sig-storage/csi-resizer
|
||||
tag: v1.12.0
|
||||
pullPolicy: IfNotPresent
|
||||
csiSnapshotter:
|
||||
repository: registry.k8s.io/sig-storage/csi-snapshotter
|
||||
tag: v8.1.0
|
||||
@ -81,6 +85,12 @@ controller:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 20Mi
|
||||
csiResizer:
|
||||
limits:
|
||||
memory: 400Mi
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 20Mi
|
||||
csiSnapshotter:
|
||||
limits:
|
||||
memory: 200Mi
|
||||
|
||||
@ -63,6 +63,30 @@ spec:
|
||||
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
|
||||
securityContext:
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
- name: csi-snapshotter
|
||||
image: registry.k8s.io/sig-storage/csi-snapshotter:v8.1.0
|
||||
args:
|
||||
|
||||
@ -64,3 +64,39 @@ roleRef:
|
||||
kind: ClusterRole
|
||||
name: nfs-external-provisioner-role
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
---
|
||||
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: nfs-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: nfs-csi-resizer-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: csi-nfs-controller-sa
|
||||
namespace: kube-system
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: nfs-external-resizer-role
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
||||
@ -62,9 +62,10 @@ pip install yq --ignore-installed PyYAML
|
||||
|
||||
# Extract images from csi-nfs-controller.yaml
|
||||
expected_csi_provisioner_image="$(cat ${PKG_ROOT}/deploy/csi-nfs-controller.yaml | yq -r .spec.template.spec.containers[0].image | head -n 1)"
|
||||
expected_csi_snapshotter_image="$(cat ${PKG_ROOT}/deploy/csi-nfs-controller.yaml | yq -r .spec.template.spec.containers[1].image | head -n 1)"
|
||||
expected_liveness_probe_image="$(cat ${PKG_ROOT}/deploy/csi-nfs-controller.yaml | yq -r .spec.template.spec.containers[2].image | head -n 1)"
|
||||
expected_nfs_image="$(cat ${PKG_ROOT}/deploy/csi-nfs-controller.yaml | yq -r .spec.template.spec.containers[3].image | head -n 1)"
|
||||
expected_csi_resizer_image="$(cat ${PKG_ROOT}/deploy/csi-nfs-controller.yaml | yq -r .spec.template.spec.containers[1].image | head -n 1)"
|
||||
expected_csi_snapshotter_image="$(cat ${PKG_ROOT}/deploy/csi-nfs-controller.yaml | yq -r .spec.template.spec.containers[2].image | head -n 1)"
|
||||
expected_liveness_probe_image="$(cat ${PKG_ROOT}/deploy/csi-nfs-controller.yaml | yq -r .spec.template.spec.containers[3].image | head -n 1)"
|
||||
expected_nfs_image="$(cat ${PKG_ROOT}/deploy/csi-nfs-controller.yaml | yq -r .spec.template.spec.containers[4].image | head -n 1)"
|
||||
|
||||
csi_provisioner_image="$(get_image_from_helm_chart "csiProvisioner")"
|
||||
validate_image "${expected_csi_provisioner_image}" "${csi_provisioner_image}"
|
||||
|
||||
@ -476,8 +476,19 @@ func (cs *ControllerServer) ListSnapshots(_ context.Context, _ *csi.ListSnapshot
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
}
|
||||
|
||||
func (cs *ControllerServer) ControllerExpandVolume(_ context.Context, _ *csi.ControllerExpandVolumeRequest) (*csi.ControllerExpandVolumeResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "")
|
||||
func (cs *ControllerServer) 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
|
||||
}
|
||||
|
||||
// Mount nfs server at base-dir
|
||||
|
||||
@ -372,6 +372,13 @@ func TestControllerGetCapabilities(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: &csi.ControllerServiceCapability_Rpc{
|
||||
Rpc: &csi.ControllerServiceCapability_RPC{
|
||||
Type: csi.ControllerServiceCapability_RPC_EXPAND_VOLUME,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedErr: nil,
|
||||
@ -1057,6 +1064,60 @@ func TestDeleteSnapshot(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestControllerExpandVolume(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
testFunc func(t *testing.T)
|
||||
}{
|
||||
{
|
||||
name: "volume ID missing",
|
||||
testFunc: func(t *testing.T) {
|
||||
d := initTestController(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) {
|
||||
d := initTestController(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) {
|
||||
d := initTestController(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)
|
||||
}
|
||||
}
|
||||
|
||||
func matchCreateSnapshotResponse(e, r *csi.CreateSnapshotResponse) error {
|
||||
if e == nil && r == nil {
|
||||
return nil
|
||||
|
||||
@ -103,6 +103,7 @@ func NewDriver(options *DriverOptions) *Driver {
|
||||
csi.ControllerServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER,
|
||||
csi.ControllerServiceCapability_RPC_CLONE_VOLUME,
|
||||
csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT,
|
||||
csi.ControllerServiceCapability_RPC_EXPAND_VOLUME,
|
||||
})
|
||||
|
||||
n.AddNodeServiceCapabilities([]csi.NodeServiceCapability_RPC_Type{
|
||||
|
||||
@ -52,7 +52,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() {
|
||||
})
|
||||
|
||||
testDriver = driver.InitNFSDriver()
|
||||
ginkgo.It("should create a volume on demand with mount options [nfs.csi.k8s.io]", func(ctx ginkgo.SpecContext) {
|
||||
ginkgo.It("should create a volume on demand with mount options", func(ctx ginkgo.SpecContext) {
|
||||
pods := []testsuites.PodDetails{
|
||||
{
|
||||
Cmd: "echo 'hello world' > /mnt/test-1/data && grep 'hello world' /mnt/test-1/data",
|
||||
@ -75,7 +75,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() {
|
||||
test.Run(ctx, cs, ns)
|
||||
})
|
||||
|
||||
ginkgo.It("should create a volume on demand with zero mountPermissions [nfs.csi.k8s.io]", func(ctx ginkgo.SpecContext) {
|
||||
ginkgo.It("should create a volume on demand with zero mountPermissions", func(ctx ginkgo.SpecContext) {
|
||||
pods := []testsuites.PodDetails{
|
||||
{
|
||||
Cmd: "echo 'hello world' > /mnt/test-1/data && grep 'hello world' /mnt/test-1/data",
|
||||
@ -98,7 +98,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 [nfs.csi.k8s.io]", func(ctx ginkgo.SpecContext) {
|
||||
ginkgo.It("should create multiple PV objects, bind to PVCs and attach all to different pods on the same node", func(ctx ginkgo.SpecContext) {
|
||||
pods := []testsuites.PodDetails{
|
||||
{
|
||||
Cmd: "while true; do echo $(date -u) >> /mnt/test-1/data; sleep 100; done",
|
||||
@ -135,7 +135,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 [nfs.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) {
|
||||
pods := []testsuites.PodDetails{
|
||||
{
|
||||
Cmd: "touch /mnt/test-1/data",
|
||||
@ -159,7 +159,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 [nfs.csi.k8s.io]", 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", func(ctx ginkgo.SpecContext) {
|
||||
pod := testsuites.PodDetails{
|
||||
Cmd: "echo 'hello world' >> /mnt/test-1/data && while true; do sleep 100; done",
|
||||
Volumes: []testsuites.VolumeDetails{
|
||||
@ -188,7 +188,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() {
|
||||
test.Run(ctx, cs, ns)
|
||||
})
|
||||
|
||||
ginkgo.It("[subDir]should create a deployment object, write and read to it, delete the pod and write and read to it again [nfs.csi.k8s.io]", func(ctx ginkgo.SpecContext) {
|
||||
ginkgo.It("[subDir]should create a deployment object, write and read to it, delete the pod and write and read to it again", func(ctx ginkgo.SpecContext) {
|
||||
pod := testsuites.PodDetails{
|
||||
Cmd: "echo 'hello world' >> /mnt/test-1/data && while true; do sleep 100; done",
|
||||
Volumes: []testsuites.VolumeDetails{
|
||||
@ -217,7 +217,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() {
|
||||
test.Run(ctx, cs, ns)
|
||||
})
|
||||
|
||||
ginkgo.It(fmt.Sprintf("should delete PV with reclaimPolicy %q [nfs.csi.k8s.io]", v1.PersistentVolumeReclaimDelete), func(ctx ginkgo.SpecContext) {
|
||||
ginkgo.It(fmt.Sprintf("should delete PV with reclaimPolicy %q", v1.PersistentVolumeReclaimDelete), func(ctx ginkgo.SpecContext) {
|
||||
reclaimPolicy := v1.PersistentVolumeReclaimDelete
|
||||
volumes := []testsuites.VolumeDetails{
|
||||
{
|
||||
@ -234,7 +234,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() {
|
||||
test.Run(ctx, cs, ns)
|
||||
})
|
||||
|
||||
ginkgo.It(fmt.Sprintf("should retain PV with reclaimPolicy %q [nfs.csi.k8s.io]", v1.PersistentVolumeReclaimRetain), func(ctx ginkgo.SpecContext) {
|
||||
ginkgo.It(fmt.Sprintf("should retain PV with reclaimPolicy %q", v1.PersistentVolumeReclaimRetain), func(ctx ginkgo.SpecContext) {
|
||||
reclaimPolicy := v1.PersistentVolumeReclaimRetain
|
||||
volumes := []testsuites.VolumeDetails{
|
||||
{
|
||||
@ -251,7 +251,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() {
|
||||
test.Run(ctx, cs, ns)
|
||||
})
|
||||
|
||||
ginkgo.It("should create a pod with multiple volumes [nfs.csi.k8s.io]", func(ctx ginkgo.SpecContext) {
|
||||
ginkgo.It("should create a pod with multiple volumes", func(ctx ginkgo.SpecContext) {
|
||||
volumes := []testsuites.VolumeDetails{}
|
||||
for i := 1; i <= 6; i++ {
|
||||
volume := testsuites.VolumeDetails{
|
||||
@ -278,7 +278,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() {
|
||||
test.Run(ctx, cs, ns)
|
||||
})
|
||||
|
||||
ginkgo.It("should create a pod with volume mount subpath [nfs.csi.k8s.io]", func(ctx ginkgo.SpecContext) {
|
||||
ginkgo.It("should create a pod with volume mount subpath", func(ctx ginkgo.SpecContext) {
|
||||
pods := []testsuites.PodDetails{
|
||||
{
|
||||
Cmd: convertToPowershellCommandIfNecessary("echo 'hello world' > /mnt/test-1/data && grep 'hello world' /mnt/test-1/data"),
|
||||
@ -301,7 +301,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() {
|
||||
test.Run(ctx, cs, ns)
|
||||
})
|
||||
|
||||
ginkgo.It("should create a CSI inline volume [nfs.csi.k8s.io]", func(ctx ginkgo.SpecContext) {
|
||||
ginkgo.It("should create a 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"),
|
||||
@ -328,7 +328,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 [nfs.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: "echo 'hello world' > /mnt/test-1/data && grep 'hello world' /mnt/test-1/data",
|
||||
@ -351,7 +351,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() {
|
||||
test.Run(ctx, cs, ns)
|
||||
})
|
||||
|
||||
ginkgo.It("should create a volume on demand with archive on delete [nfs.csi.k8s.io]", func(ctx ginkgo.SpecContext) {
|
||||
ginkgo.It("should create a volume on demand with archive on delete", func(ctx ginkgo.SpecContext) {
|
||||
pods := []testsuites.PodDetails{
|
||||
{
|
||||
Cmd: "echo 'hello world' > /mnt/test-1/data && grep 'hello world' /mnt/test-1/data",
|
||||
@ -374,7 +374,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() {
|
||||
test.Run(ctx, cs, ns)
|
||||
})
|
||||
|
||||
ginkgo.It("should create a volume on demand with archive subdir on delete [nfs.csi.k8s.io]", func(ctx ginkgo.SpecContext) {
|
||||
ginkgo.It("should create a volume on demand with archive subdir on delete", func(ctx ginkgo.SpecContext) {
|
||||
pods := []testsuites.PodDetails{
|
||||
{
|
||||
Cmd: "echo 'hello world' > /mnt/test-1/data && grep 'hello world' /mnt/test-1/data",
|
||||
@ -396,4 +396,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)
|
||||
})
|
||||
})
|
||||
|
||||
@ -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-nfs/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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -16,6 +16,8 @@ DriverInfo:
|
||||
fsGroup: true
|
||||
pvcDataSource: true
|
||||
snapshotDataSource: true
|
||||
controllerExpansion: true
|
||||
nodeExpansion: true
|
||||
InlineVolumes:
|
||||
- Attributes:
|
||||
server: nfs-server.default.svc.cluster.local
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user