Skip to content

Commit 199a1b8

Browse files
authored
Merge pull request #70 from gnufied/add-extra-fields
Add volume capability to ControllerExpandVolume CSI call
2 parents 5e75c86 + 3b462ac commit 199a1b8

File tree

10 files changed

+1080
-818
lines changed

10 files changed

+1080
-818
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ This information reflects the head of this branch.
1616

1717
| Compatible with CSI Version | Container Image | Recommended K8s Version |
1818
| ------------------------------------------------------------------------------------------ | -------------------------------| --------------- |
19-
| [CSI Spec v1.1.0](https://github.com/container-storage-interface/spec/releases/tag/v1.1.0) | quay.io/k8scsi/csi-resizer | 1.16 |
19+
| [CSI Spec v1.2.0](https://github.com/container-storage-interface/spec/releases/tag/v1.2.0) | quay.io/k8scsi/csi-resizer | 1.16 |
20+
2021

2122

2223
## Feature status

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/kubernetes-csi/external-resizer
33
go 1.12
44

55
require (
6-
github.com/container-storage-interface/spec v1.1.0
6+
github.com/container-storage-interface/spec v1.2.0
77
github.com/imdario/mergo v0.3.7 // indirect
88
github.com/kubernetes-csi/csi-lib-utils v0.7.0
99
google.golang.org/grpc v1.26.0

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
2424
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
2525
github.com/container-storage-interface/spec v1.1.0 h1:qPsTqtR1VUPvMPeK0UnCZMtXaKGyyLPG8gj/wG6VqMs=
2626
github.com/container-storage-interface/spec v1.1.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4=
27+
github.com/container-storage-interface/spec v1.2.0 h1:bD9KIVgaVKKkQ/UbVUY9kCaH/CJbhNxe0eeB4JeJV2s=
28+
github.com/container-storage-interface/spec v1.2.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4=
2729
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2830
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2931
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=

pkg/controller/controller_test.go

+33-9
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@ import (
2222
)
2323

2424
func TestController(t *testing.T) {
25+
blockVolumeMode := v1.PersistentVolumeBlock
26+
fsVolumeMode := v1.PersistentVolumeFilesystem
2527
for _, test := range []struct {
2628
Name string
2729
PVC *v1.PersistentVolumeClaim
2830
PV *v1.PersistentVolume
2931

30-
CreateObjects bool
31-
NodeResize bool
32-
CallCSIExpand bool
32+
CreateObjects bool
33+
NodeResize bool
34+
CallCSIExpand bool
35+
expectBlockVolume bool
3336
}{
3437
{
3538
Name: "Invalid key",
@@ -56,36 +59,45 @@ func TestController(t *testing.T) {
5659
{
5760
Name: "pv claimref does not have pvc UID",
5861
PVC: createPVC(2, 1),
59-
PV: createPV(1, "testPVC" /*pvcName*/, "test" /*pvcNamespace*/, "foobaz" /*pvcUID*/),
62+
PV: createPV(1, "testPVC" /*pvcName*/, "test" /*pvcNamespace*/, "foobaz" /*pvcUID*/, &fsVolumeMode),
6063
CallCSIExpand: false,
6164
},
6265
{
6366
Name: "pv claimref does not have PVC namespace",
6467
PVC: createPVC(2, 1),
65-
PV: createPV(1, "testPVC" /*pvcName*/, "test1" /*pvcNamespace*/, "foobar" /*pvcUID*/),
68+
PV: createPV(1, "testPVC" /*pvcName*/, "test1" /*pvcNamespace*/, "foobar" /*pvcUID*/, &fsVolumeMode),
6669
CallCSIExpand: false,
6770
},
6871
{
6972
Name: "pv claimref is nil",
7073
PVC: createPVC(2, 1),
71-
PV: createPV(1, "" /*pvcName*/, "test1" /*pvcNamespace*/, "foobar" /*pvcUID*/),
74+
PV: createPV(1, "" /*pvcName*/, "test1" /*pvcNamespace*/, "foobar" /*pvcUID*/, &fsVolumeMode),
7275
CallCSIExpand: false,
7376
},
7477
{
7578
Name: "Resize PVC, no FS resize",
7679
PVC: createPVC(2, 1),
77-
PV: createPV(1, "testPVC", "test", "foobar"),
80+
PV: createPV(1, "testPVC", "test", "foobar", &fsVolumeMode),
7881
CreateObjects: true,
7982
CallCSIExpand: true,
8083
},
8184
{
8285
Name: "Resize PVC with FS resize",
8386
PVC: createPVC(2, 1),
84-
PV: createPV(1, "testPVC", "test", "foobar"),
87+
PV: createPV(1, "testPVC", "test", "foobar", &fsVolumeMode),
8588
CreateObjects: true,
8689
NodeResize: true,
8790
CallCSIExpand: true,
8891
},
92+
{
93+
Name: "Block Resize PVC with FS resize",
94+
PVC: createPVC(2, 1),
95+
PV: createPV(1, "testPVC", "test", "foobar", &blockVolumeMode),
96+
CreateObjects: true,
97+
NodeResize: true,
98+
CallCSIExpand: true,
99+
expectBlockVolume: true,
100+
},
89101
} {
90102
client := csi.NewMockClient("mock", test.NodeResize, true, true)
91103
driverName, _ := client.GetDriverName(context.TODO())
@@ -138,6 +150,16 @@ func TestController(t *testing.T) {
138150
if !test.CallCSIExpand && expandCallCount > 0 {
139151
t.Fatalf("for %s: expected no csi expand call, received csi expansion request", test.Name)
140152
}
153+
154+
usedCapability := client.GetCapability()
155+
156+
if test.CallCSIExpand && test.expectBlockVolume && usedCapability.GetBlock() == nil {
157+
t.Errorf("For %s: expected block accesstype got: %v", test.Name, usedCapability)
158+
}
159+
160+
if test.CallCSIExpand && !test.expectBlockVolume && usedCapability.GetMount() == nil {
161+
t.Errorf("For %s: expected mount accesstype got: %v", test.Name, usedCapability)
162+
}
141163
}
142164
}
143165

@@ -181,14 +203,15 @@ func createPVC(requestGB, capacityGB int) *v1.PersistentVolumeClaim {
181203
}
182204
}
183205

184-
func createPV(capacityGB int, pvcName, pvcNamespace string, pvcUID types.UID) *v1.PersistentVolume {
206+
func createPV(capacityGB int, pvcName, pvcNamespace string, pvcUID types.UID, volumeMode *v1.PersistentVolumeMode) *v1.PersistentVolume {
185207
capacity := quantityGB(capacityGB)
186208

187209
pv := &v1.PersistentVolume{
188210
ObjectMeta: metav1.ObjectMeta{
189211
Name: "testPV",
190212
},
191213
Spec: v1.PersistentVolumeSpec{
214+
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
192215
Capacity: map[v1.ResourceName]resource.Quantity{
193216
v1.ResourceStorage: capacity,
194217
},
@@ -198,6 +221,7 @@ func createPV(capacityGB int, pvcName, pvcNamespace string, pvcUID types.UID) *v
198221
VolumeHandle: "foo",
199222
},
200223
},
224+
VolumeMode: volumeMode,
201225
},
202226
}
203227
if len(pvcName) > 0 {

pkg/csi/client.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ type Client interface {
4848

4949
// Expand expands the volume to a new size at least as big as requestBytes.
5050
// It returns the new size and whether the volume need expand operation on the node.
51-
Expand(ctx context.Context, volumeID string, requestBytes int64, secrets map[string]string) (int64, bool, error)
51+
Expand(ctx context.Context, volumeID string, requestBytes int64, secrets map[string]string, capability *csi.VolumeCapability) (int64, bool, error)
5252
}
5353

5454
// New creates a new CSI client.
@@ -120,11 +120,13 @@ func (c *client) Expand(
120120
ctx context.Context,
121121
volumeID string,
122122
requestBytes int64,
123-
secrets map[string]string) (int64, bool, error) {
123+
secrets map[string]string,
124+
capability *csi.VolumeCapability) (int64, bool, error) {
124125
req := &csi.ControllerExpandVolumeRequest{
125-
Secrets: secrets,
126-
VolumeId: volumeID,
127-
CapacityRange: &csi.CapacityRange{RequiredBytes: requestBytes},
126+
Secrets: secrets,
127+
VolumeId: volumeID,
128+
CapacityRange: &csi.CapacityRange{RequiredBytes: requestBytes},
129+
VolumeCapability: capability,
128130
}
129131
resp, err := c.ctrlClient.ControllerExpandVolume(ctx, req)
130132
if err != nil {

pkg/csi/mock_client.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package csi
22

3-
import "context"
3+
import (
4+
"context"
5+
6+
"github.com/container-storage-interface/spec/lib/go/csi"
7+
)
48

59
func NewMockClient(
610
name string,
@@ -23,6 +27,7 @@ type MockClient struct {
2327
supportsPluginControllerService bool
2428
expandCalled int
2529
usedSecrets map[string]string
30+
usedCapability *csi.VolumeCapability
2631
}
2732

2833
func (c *MockClient) GetDriverName(context.Context) (string, error) {
@@ -45,17 +50,23 @@ func (c *MockClient) Expand(
4550
ctx context.Context,
4651
volumeID string,
4752
requestBytes int64,
48-
secrets map[string]string) (int64, bool, error) {
53+
secrets map[string]string,
54+
capability *csi.VolumeCapability) (int64, bool, error) {
4955
// TODO: Determine whether the operation succeeds or fails by parameters.
5056
c.expandCalled++
5157
c.usedSecrets = secrets
58+
c.usedCapability = capability
5259
return requestBytes, c.supportsNodeResize, nil
5360
}
5461

5562
func (c *MockClient) GetExpandCount() int {
5663
return c.expandCalled
5764
}
5865

66+
func (c *MockClient) GetCapability() *csi.VolumeCapability {
67+
return c.usedCapability
68+
}
69+
5970
// GetSecrets returns secrets used for volume expansion
6071
func (c *MockClient) GetSecrets() map[string]string {
6172
return c.usedSecrets

pkg/resizer/csi_resizer.go

+68-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"fmt"
2323
"time"
2424

25+
csilib "github.com/container-storage-interface/spec/lib/go/csi"
2526
"github.com/kubernetes-csi/csi-lib-utils/metrics"
2627
"github.com/kubernetes-csi/external-resizer/pkg/csi"
2728
"github.com/kubernetes-csi/external-resizer/pkg/util"
@@ -157,10 +158,12 @@ func (r *csiResizer) Resize(pv *v1.PersistentVolume, requestSize resource.Quanti
157158

158159
var volumeID string
159160
var source *v1.CSIPersistentVolumeSource
161+
var pvSpec v1.PersistentVolumeSpec
160162
if pv.Spec.CSI != nil {
161163
// handle CSI volume
162164
source = pv.Spec.CSI
163165
volumeID = source.VolumeHandle
166+
pvSpec = pv.Spec
164167
} else {
165168
if csitranslationlib.IsMigratedCSIDriverByName(r.name) {
166169
// handle migrated in-tree volume
@@ -169,6 +172,7 @@ func (r *csiResizer) Resize(pv *v1.PersistentVolume, requestSize resource.Quanti
169172
return oldSize, false, fmt.Errorf("failed to translate persistent volume: %v", err)
170173
}
171174
source = csiPV.Spec.CSI
175+
pvSpec = csiPV.Spec
172176
volumeID = source.VolumeHandle
173177
} else {
174178
// non-migrated in-tree volume
@@ -190,16 +194,79 @@ func (r *csiResizer) Resize(pv *v1.PersistentVolume, requestSize resource.Quanti
190194
}
191195
}
192196

197+
capability, err := GetVolumeCapabilities(pvSpec)
198+
if err != nil {
199+
return oldSize, false, fmt.Errorf("failed to get capabilities of volume %s with %v", pv.Name, err)
200+
}
201+
193202
ctx, cancel := timeoutCtx(r.timeout)
194203
defer cancel()
195-
newSizeBytes, nodeResizeRequired, err := r.client.Expand(ctx, volumeID, requestSize.Value(), secrets)
204+
newSizeBytes, nodeResizeRequired, err := r.client.Expand(ctx, volumeID, requestSize.Value(), secrets, capability)
196205
if err != nil {
197206
return oldSize, nodeResizeRequired, err
198207
}
199208

200209
return *resource.NewQuantity(newSizeBytes, resource.BinarySI), nodeResizeRequired, err
201210
}
202211

212+
// GetVolumeCapabilities returns volumecapability from PV spec
213+
func GetVolumeCapabilities(pvSpec v1.PersistentVolumeSpec) (*csilib.VolumeCapability, error) {
214+
m := map[v1.PersistentVolumeAccessMode]bool{}
215+
for _, mode := range pvSpec.AccessModes {
216+
m[mode] = true
217+
}
218+
219+
if pvSpec.CSI == nil {
220+
return nil, errors.New("CSI volume source was nil")
221+
}
222+
223+
var cap *csilib.VolumeCapability
224+
if pvSpec.VolumeMode != nil && *pvSpec.VolumeMode == v1.PersistentVolumeBlock {
225+
cap = &csilib.VolumeCapability{
226+
AccessType: &csilib.VolumeCapability_Block{
227+
Block: &csilib.VolumeCapability_BlockVolume{},
228+
},
229+
AccessMode: &csilib.VolumeCapability_AccessMode{},
230+
}
231+
232+
} else {
233+
fsType := pvSpec.CSI.FSType
234+
235+
cap = &csilib.VolumeCapability{
236+
AccessType: &csilib.VolumeCapability_Mount{
237+
Mount: &csilib.VolumeCapability_MountVolume{
238+
FsType: fsType,
239+
MountFlags: pvSpec.MountOptions,
240+
},
241+
},
242+
AccessMode: &csilib.VolumeCapability_AccessMode{},
243+
}
244+
}
245+
246+
// Translate array of modes into single VolumeCapability
247+
switch {
248+
case m[v1.ReadWriteMany]:
249+
// ReadWriteMany trumps everything, regardless what other modes are set
250+
cap.AccessMode.Mode = csilib.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER
251+
252+
case m[v1.ReadOnlyMany] && m[v1.ReadWriteOnce]:
253+
// This is no way how to translate this to CSI...
254+
return nil, fmt.Errorf("CSI does not support ReadOnlyMany and ReadWriteOnce on the same PersistentVolume")
255+
256+
case m[v1.ReadOnlyMany]:
257+
// There is only ReadOnlyMany set
258+
cap.AccessMode.Mode = csilib.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY
259+
260+
case m[v1.ReadWriteOnce]:
261+
// There is only ReadWriteOnce set
262+
cap.AccessMode.Mode = csilib.VolumeCapability_AccessMode_SINGLE_NODE_WRITER
263+
264+
default:
265+
return nil, fmt.Errorf("unsupported AccessMode combination: %+v", pvSpec.AccessModes)
266+
}
267+
return cap, nil
268+
}
269+
203270
func getDriverName(client csi.Client, timeout time.Duration) (string, error) {
204271
ctx, cancel := timeoutCtx(timeout)
205272
defer cancel()

0 commit comments

Comments
 (0)