Skip to content

Commit 6202c6a

Browse files
raminenik8s-ci-robot
authored andcommitted
Add resize support (kubernetes#620)
This commit adds resize code implementation for cinder CSI driver.
1 parent a962245 commit 6202c6a

File tree

16 files changed

+317
-11
lines changed

16 files changed

+317
-11
lines changed

Gopkg.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# This YAML file contains nginx & csi cinder driver objects,
2+
# which are necessary to run nginx with csi cinder driver.
3+
4+
apiVersion: storage.k8s.io/v1
5+
kind: StorageClass
6+
metadata:
7+
name: csi-sc-cinderplugin
8+
provisioner: cinder.csi.openstack.org
9+
allowVolumeExpansion: true
10+
11+
---
12+
apiVersion: v1
13+
kind: PersistentVolumeClaim
14+
metadata:
15+
name: csi-pvc-cinderplugin
16+
spec:
17+
accessModes:
18+
- ReadWriteOnce
19+
resources:
20+
requests:
21+
storage: 1Gi
22+
storageClassName: csi-sc-cinderplugin
23+
24+
---
25+
apiVersion: v1
26+
kind: Pod
27+
metadata:
28+
name: nginx
29+
spec:
30+
containers:
31+
- image: nginx
32+
imagePullPolicy: IfNotPresent
33+
name: nginx
34+
ports:
35+
- containerPort: 80
36+
protocol: TCP
37+
volumeMounts:
38+
- mountPath: /var/lib/www/html
39+
name: csi-data-cinderplugin
40+
volumes:
41+
- name: csi-data-cinderplugin
42+
persistentVolumeClaim:
43+
claimName: csi-pvc-cinderplugin
44+
readOnly: false

manifests/cinder-csi-plugin/cinder-csi-controllerplugin-rbac.yaml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,71 @@ roleRef:
136136
kind: ClusterRole
137137
name: csi-snapshotter-role
138138
apiGroup: rbac.authorization.k8s.io
139+
---
140+
141+
# External Resizer
142+
kind: ClusterRole
143+
apiVersion: rbac.authorization.k8s.io/v1
144+
metadata:
145+
name: csi-resizer-role
146+
rules:
147+
# The following rule should be uncommented for plugins that require secrets
148+
# for provisioning.
149+
# - apiGroups: [""]
150+
# resources: ["secrets"]
151+
# verbs: ["get", "list", "watch"]
152+
- apiGroups: [""]
153+
resources: ["persistentvolumes"]
154+
verbs: ["get", "list", "watch", "update", "patch"]
155+
- apiGroups: [""]
156+
resources: ["persistentvolumeclaims"]
157+
verbs: ["get", "list", "watch"]
158+
- apiGroups: [""]
159+
resources: ["persistentvolumeclaims/status"]
160+
verbs: ["update", "patch"]
161+
- apiGroups: ["storage.k8s.io"]
162+
resources: ["storageclasses"]
163+
verbs: ["get", "list", "watch"]
164+
- apiGroups: [""]
165+
resources: ["events"]
166+
verbs: ["list", "watch", "create", "update", "patch"]
167+
168+
---
169+
kind: ClusterRoleBinding
170+
apiVersion: rbac.authorization.k8s.io/v1
171+
metadata:
172+
name: csi-resizer-binding
173+
subjects:
174+
- kind: ServiceAccount
175+
name: csi-cinder-controller-sa
176+
namespace: kube-system
177+
roleRef:
178+
kind: ClusterRole
179+
name: csi-resizer-role
180+
apiGroup: rbac.authorization.k8s.io
181+
182+
---
183+
kind: Role
184+
apiVersion: rbac.authorization.k8s.io/v1
185+
metadata:
186+
namespace: kube-system
187+
name: external-resizer-cfg
188+
rules:
189+
- apiGroups: ["coordination.k8s.io"]
190+
resources: ["leases"]
191+
verbs: ["get", "watch", "list", "delete", "update", "create"]
192+
193+
---
194+
kind: RoleBinding
195+
apiVersion: rbac.authorization.k8s.io/v1
196+
metadata:
197+
name: csi-resizer-role-cfg
198+
namespace: kube-system
199+
subjects:
200+
- kind: ServiceAccount
201+
name: csi-cinder-controller-sa
202+
namespace: kube-system
203+
roleRef:
204+
kind: Role
205+
name: external-resizer-cfg
206+
apiGroup: rbac.authorization.k8s.io

manifests/cinder-csi-plugin/cinder-csi-controllerplugin.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,18 @@ spec:
6868
volumeMounts:
6969
- mountPath: /var/lib/csi/sockets/pluginproxy/
7070
name: socket-dir
71+
- name: csi-resizer
72+
image: quay.io/k8scsi/csi-resizer:canary
73+
args:
74+
- "--v=5"
75+
- "--csi-address=$(ADDRESS)"
76+
env:
77+
- name: ADDRESS
78+
value: /var/lib/csi/sockets/pluginproxy/csi.sock
79+
imagePullPolicy: "IfNotPresent"
80+
volumeMounts:
81+
- name: socket-dir
82+
mountPath: /var/lib/csi/sockets/pluginproxy/
7183
- name: cinder-csi-plugin
7284
image: docker.io/k8scloudprovider/cinder-csi-plugin:latest
7385
args :

pkg/csi/cinder/controllerserver.go

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,44 @@ func (cs *controllerServer) GetCapacity(ctx context.Context, req *csi.GetCapacit
419419
}
420420

421421
func (cs *controllerServer) ControllerExpandVolume(ctx context.Context, req *csi.ControllerExpandVolumeRequest) (*csi.ControllerExpandVolumeResponse, error) {
422-
return nil, status.Error(codes.Unimplemented, fmt.Sprintf("ControllerExpandVolume is not yet implemented"))
422+
klog.V(4).Infof("ControllerExpandVolume: called with args %+v", *req)
423+
424+
volumeID := req.GetVolumeId()
425+
if len(volumeID) == 0 {
426+
return nil, status.Error(codes.InvalidArgument, "Volume ID not provided")
427+
}
428+
cap := req.GetCapacityRange()
429+
if cap == nil {
430+
return nil, status.Error(codes.InvalidArgument, "Capacity range not provided")
431+
}
432+
433+
volSizeBytes := int64(req.GetCapacityRange().GetRequiredBytes())
434+
volSizeGB := int(util.RoundUpSize(volSizeBytes, 1024*1024*1024))
435+
maxVolSize := cap.GetLimitBytes()
436+
437+
if maxVolSize > 0 && maxVolSize < volSizeBytes {
438+
return nil, status.Error(codes.OutOfRange, "After round-up, volume size exceeds the limit specified")
439+
}
440+
441+
_, err := cs.Cloud.GetVolume(volumeID)
442+
if err != nil {
443+
if cpoerrors.IsNotFound(err) {
444+
return nil, status.Error(codes.NotFound, "Volume not found")
445+
}
446+
return nil, status.Error(codes.Internal, fmt.Sprintf("GetVolume failed with error %v", err))
447+
}
448+
449+
err = cs.Cloud.ExpandVolume(volumeID, volSizeGB)
450+
if err != nil {
451+
return nil, status.Errorf(codes.Internal, fmt.Sprintf("Could not resize volume %q to size %v: %v", volumeID, volSizeGB, err))
452+
}
453+
454+
klog.V(4).Infof("ControllerExpandVolume resized volume %v to size %v", volumeID, volSizeGB)
455+
456+
return &csi.ControllerExpandVolumeResponse{
457+
CapacityBytes: volSizeBytes,
458+
NodeExpansionRequired: true,
459+
}, nil
423460
}
424461

425462
func getAZFromTopology(requirement *csi.TopologyRequirement) string {

pkg/csi/cinder/controllerserver_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,3 +341,36 @@ func TestListSnapshots(t *testing.T) {
341341

342342
assert.NotNil(FakeSnapshotID, actualRes.Entries[0].Snapshot.SnapshotId)
343343
}
344+
345+
func TestControllerExpandVolume(t *testing.T) {
346+
347+
// ExpandVolume(volumeID string, size int)
348+
osmock.On("ExpandVolume", FakeVolName, 5).Return(nil)
349+
350+
// Init assert
351+
assert := assert.New(t)
352+
353+
// Fake request
354+
fakeReq := &csi.ControllerExpandVolumeRequest{
355+
VolumeId: FakeVolName,
356+
CapacityRange: &csi.CapacityRange{
357+
RequiredBytes: 5 * 1024 * 1024 * 1024,
358+
},
359+
}
360+
361+
// Expected Result
362+
expectedRes := &csi.ControllerExpandVolumeResponse{
363+
CapacityBytes: 5 * 1024 * 1024 * 1024,
364+
NodeExpansionRequired: true,
365+
}
366+
367+
// Invoke ControllerExpandVolume
368+
actualRes, err := fakeCs.ControllerExpandVolume(FakeCtx, fakeReq)
369+
if err != nil {
370+
t.Errorf("failed to ExpandVolume: %v", err)
371+
}
372+
373+
// Assert
374+
assert.Equal(expectedRes, actualRes)
375+
376+
}

pkg/csi/cinder/driver.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,14 @@ func NewDriver(nodeID, endpoint, cluster string) *CinderDriver {
7070
csi.ControllerServiceCapability_RPC_PUBLISH_UNPUBLISH_VOLUME,
7171
csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT,
7272
csi.ControllerServiceCapability_RPC_LIST_SNAPSHOTS,
73+
csi.ControllerServiceCapability_RPC_EXPAND_VOLUME,
7374
})
7475
d.AddVolumeCapabilityAccessModes([]csi.VolumeCapability_AccessMode_Mode{csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER})
7576

7677
d.AddNodeServiceCapabilities(
7778
[]csi.NodeServiceCapability_RPC_Type{
7879
csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME,
80+
csi.NodeServiceCapability_RPC_EXPAND_VOLUME,
7981
})
8082

8183
return d

pkg/csi/cinder/identityserver.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func (ids *identityServer) Probe(ctx context.Context, req *csi.ProbeRequest) (*c
5757
}
5858

5959
func (ids *identityServer) GetPluginCapabilities(ctx context.Context, req *csi.GetPluginCapabilitiesRequest) (*csi.GetPluginCapabilitiesResponse, error) {
60-
klog.V(5).Infof("Using default capabilities")
60+
klog.V(5).Infof("GetPluginCapabilities called with req %+v", req)
6161
return &csi.GetPluginCapabilitiesResponse{
6262
Capabilities: []*csi.PluginCapability{
6363
{
@@ -74,6 +74,20 @@ func (ids *identityServer) GetPluginCapabilities(ctx context.Context, req *csi.G
7474
},
7575
},
7676
},
77+
{
78+
Type: &csi.PluginCapability_VolumeExpansion_{
79+
VolumeExpansion: &csi.PluginCapability_VolumeExpansion{
80+
Type: csi.PluginCapability_VolumeExpansion_ONLINE,
81+
},
82+
},
83+
},
84+
{
85+
Type: &csi.PluginCapability_VolumeExpansion_{
86+
VolumeExpansion: &csi.PluginCapability_VolumeExpansion{
87+
Type: csi.PluginCapability_VolumeExpansion_OFFLINE,
88+
},
89+
},
90+
},
7791
},
7892
}, nil
7993
}

pkg/csi/cinder/mount/mount.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,9 @@ import (
2525
"time"
2626

2727
"k8s.io/apimachinery/pkg/util/wait"
28+
"k8s.io/klog"
2829
"k8s.io/kubernetes/pkg/util/mount"
2930
utilexec "k8s.io/utils/exec"
30-
31-
"k8s.io/klog"
3231
)
3332

3433
const (

pkg/csi/cinder/mount/mount_mock.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ limitations under the License.
1616

1717
package mount
1818

19-
import mock "github.com/stretchr/testify/mock"
20-
import "k8s.io/kubernetes/pkg/util/mount"
19+
import (
20+
mock "github.com/stretchr/testify/mock"
21+
"k8s.io/kubernetes/pkg/util/mount"
22+
)
2123

2224
// MountMock is an autogenerated mock type for the IMount type
2325
// ORIGINALLY GENERATED BY mockery with hand edits
@@ -183,5 +185,17 @@ func (_m *MountMock) UnmountPath(mountPath string) error {
183185

184186
// GetBaseMounter provides a mock function
185187
func (_m *MountMock) GetBaseMounter() *mount.SafeFormatAndMount {
186-
return NewFakeSafeFormatAndMounter()
188+
execCallback := func(cmd string, args ...string) ([]byte, error) {
189+
if cmd == "findmnt" {
190+
return []byte("devicepath"), nil
191+
}
192+
if cmd == "blkid" {
193+
return []byte("UUID=\"1b47881a-1563-4896-a178-eec887b759de\" \n TYPE=\"ext4\""), nil
194+
}
195+
return nil, nil
196+
}
197+
return &mount.SafeFormatAndMount{
198+
Interface: NewFakeMounter(),
199+
Exec: mount.NewFakeExec(execCallback),
200+
}
187201
}

0 commit comments

Comments
 (0)