Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/container-storage-interface/spec v1.5.0
github.com/google/gofuzz v1.2.0 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/kubernetes-csi/csi-lib-utils v0.9.1
github.com/kubernetes-csi/csi-lib-utils v0.10.0
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84 // indirect
golang.org/x/term v0.0.0-20210317153231-de623e64d2a6 // indirect
google.golang.org/appengine v1.6.7 // indirect
Expand Down
48 changes: 2 additions & 46 deletions go.sum

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions pkg/controller/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ func TestController(t *testing.T) {
disableVolumeInUseErrorHandler: true,
},
} {
client := csi.NewMockClient("mock", test.NodeResize, true, true)
client := csi.NewMockClient("mock", test.NodeResize, true, true, true)
driverName, _ := client.GetDriverName(context.TODO())

var expectedCap resource.Quantity
Expand Down Expand Up @@ -343,7 +343,7 @@ func TestResizePVC(t *testing.T) {
expectFailure: true,
},
} {
client := csi.NewMockClient("mock", test.NodeResize, true, true)
client := csi.NewMockClient("mock", test.NodeResize, true, true, true)
if test.expansionFailure {
client.SetExpansionFailed()
}
Expand Down
12 changes: 12 additions & 0 deletions pkg/csi/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ type Client interface {
// in NodeGetCapabilities() gRPC call.
SupportsNodeResize(ctx context.Context) (bool, error)

// SupportsControllerResize returns whether the CSI driver reports
// SINGLE_NODE_MULTI_WRITER in ControllerGetCapabilities() gRPC call.
SupportsControllerSingleNodeMultiWriter(ctx context.Context) (bool, error)

// Expand expands the volume to a new size at least as big as requestBytes.
// It returns the new size and whether the volume need expand operation on the node.
Expand(ctx context.Context, volumeID string, requestBytes int64, secrets map[string]string, capability *csi.VolumeCapability) (int64, bool, error)
Expand Down Expand Up @@ -119,6 +123,14 @@ func (c *client) SupportsNodeResize(ctx context.Context) (bool, error) {
return false, nil
}

func (c *client) SupportsControllerSingleNodeMultiWriter(ctx context.Context) (bool, error) {
caps, err := csirpc.GetControllerCapabilities(ctx, c.conn)
if err != nil {
return false, fmt.Errorf("error getting controller capabilities: %v", err)
}
return caps[csi.ControllerServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER], nil
}

func (c *client) Expand(
ctx context.Context,
volumeID string,
Expand Down
37 changes: 22 additions & 15 deletions pkg/csi/mock_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,29 @@ func NewMockClient(
name string,
supportsNodeResize bool,
supportsControllerResize bool,
supportsPluginControllerService bool) *MockClient {
supportsPluginControllerService bool,
supportsControllerSingleNodeMultiWriter bool) *MockClient {
return &MockClient{
name: name,
supportsNodeResize: supportsNodeResize,
supportsControllerResize: supportsControllerResize,
expandCalled: 0,
supportsPluginControllerService: supportsPluginControllerService,
name: name,
supportsNodeResize: supportsNodeResize,
supportsControllerResize: supportsControllerResize,
expandCalled: 0,
supportsPluginControllerService: supportsPluginControllerService,
supportsControllerSingleNodeMultiWriter: supportsControllerSingleNodeMultiWriter,
}
}

type MockClient struct {
name string
supportsNodeResize bool
supportsControllerResize bool
supportsPluginControllerService bool
expandCalled int
expansionFailed bool
checkMigratedLabel bool
usedSecrets map[string]string
usedCapability *csi.VolumeCapability
name string
supportsNodeResize bool
supportsControllerResize bool
supportsPluginControllerService bool
supportsControllerSingleNodeMultiWriter bool
expandCalled int
expansionFailed bool
checkMigratedLabel bool
usedSecrets map[string]string
usedCapability *csi.VolumeCapability
}

func (c *MockClient) GetDriverName(context.Context) (string, error) {
Expand All @@ -50,6 +53,10 @@ func (c *MockClient) SupportsNodeResize(context.Context) (bool, error) {
return c.supportsNodeResize, nil
}

func (c *MockClient) SupportsControllerSingleNodeMultiWriter(context.Context) (bool, error) {
return c.supportsControllerSingleNodeMultiWriter, nil
}

func (c *MockClient) SetExpansionFailed() {
c.expansionFailed = true
}
Expand Down
61 changes: 35 additions & 26 deletions pkg/resizer/csi_resizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"time"

csilib "github.com/container-storage-interface/spec/lib/go/csi"
"github.com/kubernetes-csi/csi-lib-utils/accessmodes"
"github.com/kubernetes-csi/csi-lib-utils/connection"
"github.com/kubernetes-csi/external-resizer/pkg/csi"
"github.com/kubernetes-csi/external-resizer/pkg/util"
Expand Down Expand Up @@ -74,6 +75,11 @@ func NewResizerFromClient(
return nil, resizeNotSupportErr
}

_, err = supportsControllerSingleNodeMultiWriter(csiClient, timeout)
if err != nil {
return nil, fmt.Errorf("failed to check if plugin supports the SINGLE_NODE_MULTI_WRITER capability: %v", err)
}

return &csiResizer{
name: driverName,
client: csiClient,
Expand Down Expand Up @@ -165,7 +171,7 @@ func (r *csiResizer) Resize(pv *v1.PersistentVolume, requestSize resource.Quanti
}
}

capability, err := GetVolumeCapabilities(pvSpec)
capability, err := r.getVolumeCapabilities(pvSpec)
if err != nil {
return oldSize, false, fmt.Errorf("failed to get capabilities of volume %s with %v", pv.Name, err)
}
Expand All @@ -182,13 +188,17 @@ func (r *csiResizer) Resize(pv *v1.PersistentVolume, requestSize resource.Quanti
return *resource.NewQuantity(newSizeBytes, resource.BinarySI), nodeResizeRequired, err
}

// GetVolumeCapabilities returns volumecapability from PV spec
func GetVolumeCapabilities(pvSpec v1.PersistentVolumeSpec) (*csilib.VolumeCapability, error) {
m := map[v1.PersistentVolumeAccessMode]bool{}
for _, mode := range pvSpec.AccessModes {
m[mode] = true
func (r *csiResizer) getVolumeCapabilities(pvSpec v1.PersistentVolumeSpec) (*csilib.VolumeCapability, error) {
supported, err := supportsControllerSingleNodeMultiWriter(r.client, r.timeout)
if err != nil {
return nil, err
}
return GetVolumeCapabilities(pvSpec, supported)
}

// GetVolumeCapabilities returns a VolumeCapability from the PV spec. Which access mode will be set depends if the driver supports the
// SINGLE_NODE_MULTI_WRITER capability.
func GetVolumeCapabilities(pvSpec v1.PersistentVolumeSpec, singleNodeMultiWriterCapable bool) (*csilib.VolumeCapability, error) {
if pvSpec.CSI == nil {
return nil, errors.New("CSI volume source was nil")
}
Expand Down Expand Up @@ -216,27 +226,12 @@ func GetVolumeCapabilities(pvSpec v1.PersistentVolumeSpec) (*csilib.VolumeCapabi
}
}

// Translate array of modes into single VolumeCapability
switch {
case m[v1.ReadWriteMany]:
// ReadWriteMany trumps everything, regardless what other modes are set
cap.AccessMode.Mode = csilib.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER

case m[v1.ReadOnlyMany] && m[v1.ReadWriteOnce]:
// This is no way how to translate this to CSI...
return nil, fmt.Errorf("CSI does not support ReadOnlyMany and ReadWriteOnce on the same PersistentVolume")

case m[v1.ReadOnlyMany]:
// There is only ReadOnlyMany set
cap.AccessMode.Mode = csilib.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY

case m[v1.ReadWriteOnce]:
// There is only ReadWriteOnce set
cap.AccessMode.Mode = csilib.VolumeCapability_AccessMode_SINGLE_NODE_WRITER

default:
return nil, fmt.Errorf("unsupported AccessMode combination: %+v", pvSpec.AccessModes)
am, err := accessmodes.ToCSIAccessMode(pvSpec.AccessModes, singleNodeMultiWriterCapable)
if err != nil {
return nil, err
}

cap.AccessMode.Mode = am
return cap, nil
}

Expand All @@ -258,6 +253,12 @@ func supportsNodeResize(client csi.Client, timeout time.Duration) (bool, error)
return client.SupportsNodeResize(ctx)
}

func supportsControllerSingleNodeMultiWriter(client csi.Client, timeout time.Duration) (bool, error) {
ctx, cancel := timeoutCtx(timeout)
defer cancel()
return client.SupportsControllerSingleNodeMultiWriter(ctx)
}

func timeoutCtx(timeout time.Duration) (context.Context, context.CancelFunc) {
return context.WithTimeout(context.Background(), timeout)
}
Expand All @@ -278,3 +279,11 @@ func getCredentials(k8sClient kubernetes.Interface, ref *v1.SecretReference) (ma
}
return credentials, nil
}

func uniqueAccessModes(pvSpec v1.PersistentVolumeSpec) map[v1.PersistentVolumeAccessMode]bool {
m := map[v1.PersistentVolumeAccessMode]bool{}
for _, mode := range pvSpec.AccessModes {
m[mode] = true
}
return m
}
Loading