diff --git a/go.mod b/go.mod index 58a9b5893d..dd9396a412 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid 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.9.2-0.20210723114316-fd1f57183b91 github.com/kubernetes-csi/csi-test/v4 v4.0.2 github.com/kubernetes-csi/external-snapshotter/client/v3 v3.0.0 github.com/miekg/dns v1.1.40 // indirect diff --git a/go.sum b/go.sum index 5bffcb68b0..b0f103b6a7 100644 --- a/go.sum +++ b/go.sum @@ -85,7 +85,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -105,7 +104,6 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/container-storage-interface/spec v1.2.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4= github.com/container-storage-interface/spec v1.3.0/go.mod h1:6URME8mwIBbpVyZV93Ce5St17xBiQJQY67NDsuohiy4= github.com/container-storage-interface/spec v1.5.0 h1:lvKxe3uLgqQeVQcrnL2CPQKISoKjTJxojEs9cBk+HXo= github.com/container-storage-interface/spec v1.5.0/go.mod h1:8K96oQNkJ7pFcC2R9Z1ynGGBB1I93kcS6PGg3SsOk8s= @@ -133,7 +131,6 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3 github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -191,7 +188,6 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -337,8 +333,8 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kubernetes-csi/csi-lib-utils v0.9.1 h1:sGq6ifVujfMSkfTsMZip44Ttv8SDXvsBlFk9GdYl/b8= -github.com/kubernetes-csi/csi-lib-utils v0.9.1/go.mod h1:8E2jVUX9j3QgspwHXa6LwyN7IHQDjW9jX3kwoWnSC+M= +github.com/kubernetes-csi/csi-lib-utils v0.9.2-0.20210723114316-fd1f57183b91 h1:VIpVhFtqOf84G2lw21R3luY3yswbp/BnuppzcdGoTMw= +github.com/kubernetes-csi/csi-lib-utils v0.9.2-0.20210723114316-fd1f57183b91/go.mod h1:eC9/eBfNc5tvc77fxQ570nKcs9Xix7ZWzSKZvJNieXM= github.com/kubernetes-csi/csi-test/v4 v4.0.2 h1:MNj94SFHOGK6lOy+yDgxI+zlFWaPcgByqBH3JZZGyZI= github.com/kubernetes-csi/csi-test/v4 v4.0.2/go.mod h1:z3FYigjLFAuzmFzKdHQr8gUPm5Xr4Du2twKcxfys0eI= github.com/kubernetes-csi/external-snapshotter/client/v3 v3.0.0 h1:OYDCOjVcx/5wNzlZ/At8otRibUlw0T6R0xOD31f32bw= @@ -421,7 +417,6 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -862,7 +857,6 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= @@ -930,6 +924,7 @@ k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw= k8s.io/api v0.19.1/go.mod h1:+u/k4/K/7vp4vsfdT7dyl8Oxk1F26Md4g5F26Tu85PU= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8= +k8s.io/api v0.22.0-beta.1/go.mod h1:fi6APFYf34OAndBPyL9KjqGYI5OwoL8wBIKDOqsckDs= k8s.io/api v0.22.0-rc.0 h1:LcnCE0nmb2CVpvmlbHkIzjZUHcVpSoNcn8mJkIo4FoQ= k8s.io/api v0.22.0-rc.0/go.mod h1:EUcKB6RvpW74HMRUSSNwpUzrIHBdGT1FeAvOV+txic0= k8s.io/apiextensions-apiserver v0.20.1 h1:ZrXQeslal+6zKM/HjDXLzThlz/vPSxrfK3OqL8txgVQ= @@ -938,6 +933,7 @@ k8s.io/apimachinery v0.19.0/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlm k8s.io/apimachinery v0.19.1/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.22.0-beta.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apimachinery v0.22.0-rc.0 h1:boMGWXiuYJl4sAEMTEyWJtX4VLEPf0LZ0nUh+vNALIg= k8s.io/apimachinery v0.22.0-rc.0/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= @@ -947,13 +943,14 @@ k8s.io/client-go v0.19.0/go.mod h1:H9E/VT95blcFQnlyShFgnFT9ZnJOAceiUHM3MlRC+mU= k8s.io/client-go v0.19.1/go.mod h1:AZOIVSI9UUtQPeJD3zJFp15CEhSjRgAuQP5PWRJrCIQ= k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.2/go.mod h1:kH5brqWqp7HDxUFKoEgiI4v8G1xzbe9giaCenUWJzgE= +k8s.io/client-go v0.22.0-beta.1/go.mod h1:5KlLMSC7tSopTR11e+r4TQ1PG2G2MTQ3D3riymWJp3k= k8s.io/client-go v0.22.0-rc.0 h1:cxBtvZ9fH5teKp7OG3XX9YPprBqaPsmr9J24E6KqBiI= k8s.io/client-go v0.22.0-rc.0/go.mod h1:BZGppBKJh4UtgDZcIIh6vHJsJ1iZiXS7EwKZYWhyklo= k8s.io/code-generator v0.19.0/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= k8s.io/code-generator v0.20.1/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg= -k8s.io/component-base v0.19.0/go.mod h1:dKsY8BxkA+9dZIAh2aWJLL/UdASFDNtGYTCItL4LM7Y= k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.2/go.mod h1:pzFtCiwe/ASD0iV7ySMu8SYVJjCapNM9bjvk7ptpKh0= +k8s.io/component-base v0.22.0-beta.1/go.mod h1:aSG+PNay+9pkZ26a5+DOLEebeL59ebeAkfNdkMEsi78= k8s.io/component-base v0.22.0-rc.0 h1:OOlymSTskXPEp9ie03pzN5HFzPtTxA7BAO3z/1vXHtk= k8s.io/component-base v0.22.0-rc.0/go.mod h1:DKSub/kewg24bK+3ZJ/csu86fSBYpGdYk837eCTvEKg= k8s.io/component-helpers v0.22.0-rc.0 h1:xxjXY8WkwdBLdYfYUly16CX2eTlj9GgsxljhftQpBd8= diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index dfbb9d071a..dd683bce61 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -26,6 +26,7 @@ import ( "time" "github.com/container-storage-interface/spec/lib/go/csi" + "github.com/kubernetes-csi/csi-lib-utils/accessmodes" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -451,42 +452,53 @@ func getAccessTypeMount(fsType string, mountFlags []string) *csi.VolumeCapabilit } } -func getAccessMode(pvcAccessMode v1.PersistentVolumeAccessMode) *csi.VolumeCapability_AccessMode { - switch pvcAccessMode { - case v1.ReadWriteOnce: - return &csi.VolumeCapability_AccessMode{ - Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, - } - case v1.ReadWriteMany: - return &csi.VolumeCapability_AccessMode{ - Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER, - } - case v1.ReadOnlyMany: - return &csi.VolumeCapability_AccessMode{ - Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY, - } - default: - return nil - } -} - func getVolumeCapability( claim *v1.PersistentVolumeClaim, sc *storagev1.StorageClass, pvcAccessMode v1.PersistentVolumeAccessMode, fsType string, -) *csi.VolumeCapability { + supportsSingleNodeMultiWriter bool, +) (*csi.VolumeCapability, error) { + accessMode, err := accessmodes.ToCSIAccessMode([]v1.PersistentVolumeAccessMode{pvcAccessMode}, supportsSingleNodeMultiWriter) + if err != nil { + return nil, err + } + if util.CheckPersistentVolumeClaimModeBlock(claim) { return &csi.VolumeCapability{ AccessType: getAccessTypeBlock(), - AccessMode: getAccessMode(pvcAccessMode), - } + AccessMode: &csi.VolumeCapability_AccessMode{ + Mode: accessMode, + }, + }, nil } return &csi.VolumeCapability{ AccessType: getAccessTypeMount(fsType, sc.MountOptions), - AccessMode: getAccessMode(pvcAccessMode), + AccessMode: &csi.VolumeCapability_AccessMode{ + Mode: accessMode, + }, + }, nil +} + +func (p *csiProvisioner) getVolumeCapabilities( + claim *v1.PersistentVolumeClaim, + sc *storagev1.StorageClass, + fsType string, +) ([]*csi.VolumeCapability, error) { + supportsSingleNodeMultiWriter := false + if p.controllerCapabilities[csi.ControllerServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER] { + supportsSingleNodeMultiWriter = true } + volumeCaps := make([]*csi.VolumeCapability, 0) + for _, pvcAccessMode := range claim.Spec.AccessModes { + volumeCap, err := getVolumeCapability(claim, sc, pvcAccessMode, fsType, supportsSingleNodeMultiWriter) + if err != nil { + return []*csi.VolumeCapability{}, err + } + volumeCaps = append(volumeCaps, volumeCap) + } + return volumeCaps, nil } type prepareProvisionResult struct { @@ -581,10 +593,9 @@ func (p *csiProvisioner) prepareProvision(ctx context.Context, claim *v1.Persist capacity := claim.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)] volSizeBytes := capacity.Value() - // Get access mode - volumeCaps := make([]*csi.VolumeCapability, 0) - for _, pvcAccessMode := range claim.Spec.AccessModes { - volumeCaps = append(volumeCaps, getVolumeCapability(claim, sc, pvcAccessMode, fsType)) + volumeCaps, err := p.getVolumeCapabilities(claim, sc, fsType) + if err != nil { + return nil, controller.ProvisioningFinished, err } // Create a CSI CreateVolumeRequest and Response diff --git a/pkg/controller/controller_test.go b/pkg/controller/controller_test.go index d0479e222f..751ac0aad7 100644 --- a/pkg/controller/controller_test.go +++ b/pkg/controller/controller_test.go @@ -462,6 +462,15 @@ func provisionCapabilities() (rpc.PluginCapabilitySet, rpc.ControllerCapabilityS } } +func provisionWithSingleNodeMultiWriterCapabilities() (rpc.PluginCapabilitySet, rpc.ControllerCapabilitySet) { + return rpc.PluginCapabilitySet{ + csi.PluginCapability_Service_CONTROLLER_SERVICE: true, + }, rpc.ControllerCapabilitySet{ + csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME: true, + csi.ControllerServiceCapability_RPC_SINGLE_NODE_MULTI_WRITER: true, + } +} + func provisionFromSnapshotCapabilities() (rpc.PluginCapabilitySet, rpc.ControllerCapabilitySet) { return rpc.PluginCapabilitySet{ csi.PluginCapability_Service_CONTROLLER_SERVICE: true, @@ -805,26 +814,27 @@ func TestGetSecretReference(t *testing.T) { } type provisioningTestcase struct { - capacity int64 // if zero, default capacity, otherwise available bytes - volOpts controller.ProvisionOptions - notNilSelector bool - makeVolumeNameErr bool - getSecretRefErr bool - getCredentialsErr bool - volWithLessCap bool - volWithZeroCap bool - expectedPVSpec *pvSpec - clientSetObjects []runtime.Object - createVolumeError error - expectErr bool - expectState controller.ProvisioningState - expectCreateVolDo func(t *testing.T, ctx context.Context, req *csi.CreateVolumeRequest) - withExtraMetadata bool - skipCreateVolume bool - deploymentNode string // fake distributed provisioning with this node as host - immediateBinding bool // enable immediate binding support for distributed provisioning - expectSelectedNode string // a specific selected-node of the PVC in the apiserver after the test, same as before if empty - expectNoProvision bool // if true, then ShouldProvision should return false + capacity int64 // if zero, default capacity, otherwise available bytes + volOpts controller.ProvisionOptions + notNilSelector bool + makeVolumeNameErr bool + getSecretRefErr bool + getCredentialsErr bool + volWithLessCap bool + volWithZeroCap bool + expectedPVSpec *pvSpec + clientSetObjects []runtime.Object + createVolumeError error + expectErr bool + expectState controller.ProvisioningState + expectCreateVolDo func(t *testing.T, ctx context.Context, req *csi.CreateVolumeRequest) + withExtraMetadata bool + skipCreateVolume bool + deploymentNode string // fake distributed provisioning with this node as host + immediateBinding bool // enable immediate binding support for distributed provisioning + expectSelectedNode string // a specific selected-node of the PVC in the apiserver after the test, same as before if empty + expectNoProvision bool // if true, then ShouldProvision should return false + supportsSingleNodeMultiWriter bool // if true, then provision with single node multi writer capabilities } type provisioningFSTypeTestcase struct { @@ -1258,7 +1268,7 @@ func provisionTestcases() (int64, map[string]provisioningTestcase) { }, expectState: controller.ProvisioningFinished, }, - "provision with access mode single writer": { + "provision with access mode single node writer": { volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ ReclaimPolicy: &deletePolicy, @@ -1310,6 +1320,112 @@ func provisionTestcases() (int64, map[string]provisioningTestcase) { }, expectState: controller.ProvisioningFinished, }, + "provision with access mode single node writer and single node multi writer capability": { + supportsSingleNodeMultiWriter: true, + volOpts: controller.ProvisionOptions{ + StorageClass: &storagev1.StorageClass{ + ReclaimPolicy: &deletePolicy, + Parameters: map[string]string{}, + }, + PVName: "test-name", + PVC: &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + UID: "testid", + Annotations: driverNameAnnotation, + }, + Spec: v1.PersistentVolumeClaimSpec{ + Selector: nil, + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes, 10)), + }, + }, + AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, + }, + }, + }, + expectedPVSpec: &pvSpec{ + Name: "test-testi", + ReclaimPolicy: v1.PersistentVolumeReclaimDelete, + AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, + Capacity: v1.ResourceList{ + v1.ResourceName(v1.ResourceStorage): bytesToQuantity(requestedBytes), + }, + CSIPVS: &v1.CSIPersistentVolumeSource{ + Driver: "test-driver", + VolumeHandle: "test-volume-id", + FSType: "ext4", + VolumeAttributes: map[string]string{ + "storage.kubernetes.io/csiProvisionerIdentity": "test-provisioner", + }, + }, + }, + expectCreateVolDo: func(t *testing.T, ctx context.Context, req *csi.CreateVolumeRequest) { + if len(req.GetVolumeCapabilities()) != 1 { + t.Errorf("Incorrect length in volume capabilities") + } + if req.GetVolumeCapabilities()[0].GetAccessMode() == nil { + t.Errorf("Expected access mode to be set") + } + if req.GetVolumeCapabilities()[0].GetAccessMode().GetMode() != csi.VolumeCapability_AccessMode_SINGLE_NODE_MULTI_WRITER { + t.Errorf("Expected single_node_multi_writer") + } + }, + expectState: controller.ProvisioningFinished, + }, + "provision with access mode single node single writer and single node multi writer capability": { + supportsSingleNodeMultiWriter: true, + volOpts: controller.ProvisionOptions{ + StorageClass: &storagev1.StorageClass{ + ReclaimPolicy: &deletePolicy, + Parameters: map[string]string{}, + }, + PVName: "test-name", + PVC: &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + UID: "testid", + Annotations: driverNameAnnotation, + }, + Spec: v1.PersistentVolumeClaimSpec{ + Selector: nil, + Resources: v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes, 10)), + }, + }, + AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOncePod}, + }, + }, + }, + expectedPVSpec: &pvSpec{ + Name: "test-testi", + ReclaimPolicy: v1.PersistentVolumeReclaimDelete, + AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOncePod}, + Capacity: v1.ResourceList{ + v1.ResourceName(v1.ResourceStorage): bytesToQuantity(requestedBytes), + }, + CSIPVS: &v1.CSIPersistentVolumeSource{ + Driver: "test-driver", + VolumeHandle: "test-volume-id", + FSType: "ext4", + VolumeAttributes: map[string]string{ + "storage.kubernetes.io/csiProvisionerIdentity": "test-provisioner", + }, + }, + }, + expectCreateVolDo: func(t *testing.T, ctx context.Context, req *csi.CreateVolumeRequest) { + if len(req.GetVolumeCapabilities()) != 1 { + t.Errorf("Incorrect length in volume capabilities") + } + if req.GetVolumeCapabilities()[0].GetAccessMode() == nil { + t.Errorf("Expected access mode to be set") + } + if req.GetVolumeCapabilities()[0].GetAccessMode().GetMode() != csi.VolumeCapability_AccessMode_SINGLE_NODE_SINGLE_WRITER { + t.Errorf("Expected single_node_multi_writer") + } + }, + expectState: controller.ProvisioningFinished, + }, "provision with multiple access modes": { volOpts: controller.ProvisionOptions{ StorageClass: &storagev1.StorageClass{ @@ -2299,7 +2415,14 @@ func runProvisionTest(t *testing.T, tc provisioningTestcase, requestedBytes int6 } } - pluginCaps, controllerCaps := provisionCapabilities() + var pluginCaps rpc.PluginCapabilitySet + var controllerCaps rpc.ControllerCapabilitySet + if tc.supportsSingleNodeMultiWriter { + pluginCaps, controllerCaps = provisionWithSingleNodeMultiWriterCapabilities() + } else { + pluginCaps, controllerCaps = provisionCapabilities() + } + csiProvisioner := NewCSIProvisioner(clientSet, 5*time.Second, "test-provisioner", "test", 5, csiConn.conn, nil, provisionDriverName, pluginCaps, controllerCaps, supportsMigrationFromInTreePluginName, false, true, csitrans.New(), scInformer.Lister(), csiNodeInformer.Lister(), nodeInformer.Lister(), nil, nil, tc.withExtraMetadata, defaultfsType, nodeDeployment) diff --git a/vendor/github.com/kubernetes-csi/csi-lib-utils/accessmodes/access_modes.go b/vendor/github.com/kubernetes-csi/csi-lib-utils/accessmodes/access_modes.go new file mode 100644 index 0000000000..beb91b2a1d --- /dev/null +++ b/vendor/github.com/kubernetes-csi/csi-lib-utils/accessmodes/access_modes.go @@ -0,0 +1,128 @@ +/* +Copyright 2021 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 accessmodes + +import ( + "fmt" + + "github.com/container-storage-interface/spec/lib/go/csi" + v1 "k8s.io/api/core/v1" +) + +// ToCSIAccessMode maps PersistentVolume access modes in Kubernetes to CSI +// access modes. Which mapping is used depends if the driver supports the +// SINGLE_NODE_MULTI_WRITER capability. +func ToCSIAccessMode(pvAccessModes []v1.PersistentVolumeAccessMode, supportsSingleNodeMultiWriter bool) (csi.VolumeCapability_AccessMode_Mode, error) { + if supportsSingleNodeMultiWriter { + return toSingleNodeMultiWriterCapableCSIAccessMode(pvAccessModes) + } + return toCSIAccessMode(pvAccessModes) +} + +// toCSIAccessMode maps PersistentVolume access modes in Kubernetes to CSI +// access modes. +// +// +------------------+-------------------------+----------------------------------------+ +// | K8s AccessMode | CSI AccessMode | Additional Details | +// +------------------+-------------------------+----------------------------------------+ +// | ReadWriteMany | MULTI_NODE_MULTI_WRITER | | +// | ReadOnlyMany | MULTI_NODE_READER_ONLY | Cannot be combined with ReadWriteOnce | +// | ReadWriteOnce | SINGLE_NODE_WRITER | Cannot be combined with ReadOnlyMany | +// | ReadWriteOncePod | SINGLE_NODE_WRITER | Cannot be combined with any AccessMode | +// +------------------+-------------------------+----------------------------------------+ +func toCSIAccessMode(pvAccessModes []v1.PersistentVolumeAccessMode) (csi.VolumeCapability_AccessMode_Mode, error) { + m := uniqueAccessModes(pvAccessModes) + + switch { + // This mapping exists to enable CSI drivers that lack the + // SINGLE_NODE_MULTI_WRITER capability to work with the + // ReadWriteOncePod access mode. + case m[v1.ReadWriteOncePod]: + if len(m) > 1 { + return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("Kubernetes does not support use of ReadWriteOncePod with other access modes on the same PersistentVolume") + } + return csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, nil + + case m[v1.ReadWriteMany]: + // ReadWriteMany takes precedence, regardless of what other + // modes are set. + return csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER, nil + + case m[v1.ReadOnlyMany] && m[v1.ReadWriteOnce]: + // This is not possible in the CSI spec. + return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("CSI does not support ReadOnlyMany and ReadWriteOnce on the same PersistentVolume") + + case m[v1.ReadOnlyMany]: + return csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY, nil + + case m[v1.ReadWriteOnce]: + return csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, nil + + default: + return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("unsupported AccessMode combination: %+v", pvAccessModes) + } +} + +// toSingleNodeMultiWriterCapableCSIAccessMode maps PersistentVolume access +// modes in Kubernetes to CSI access modes for drivers that support the +// SINGLE_NODE_MULTI_WRITER capability. +// +// +------------------+---------------------------+----------------------------------------+ +// | K8s AccessMode | CSI AccessMode | Additional Details | +// +------------------+---------------------------+----------------------------------------+ +// | ReadWriteMany | MULTI_NODE_MULTI_WRITER | | +// | ReadOnlyMany | MULTI_NODE_READER_ONLY | Cannot be combined with ReadWriteOnce | +// | ReadWriteOnce | SINGLE_NODE_MULTI_WRITER | Cannot be combined with ReadOnlyMany | +// | ReadWriteOncePod | SINGLE_NODE_SINGLE_WRITER | Cannot be combined with any AccessMode | +// +------------------+---------------------------+----------------------------------------+ +func toSingleNodeMultiWriterCapableCSIAccessMode(pvAccessModes []v1.PersistentVolumeAccessMode) (csi.VolumeCapability_AccessMode_Mode, error) { + m := uniqueAccessModes(pvAccessModes) + + switch { + case m[v1.ReadWriteOncePod]: + if len(m) > 1 { + return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("Kubernetes does not support use of ReadWriteOncePod with other access modes on the same PersistentVolume") + } + return csi.VolumeCapability_AccessMode_SINGLE_NODE_SINGLE_WRITER, nil + + case m[v1.ReadWriteMany]: + // ReadWriteMany trumps everything, regardless of what other + // modes are set. + return csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER, nil + + case m[v1.ReadOnlyMany] && m[v1.ReadWriteOnce]: + // This is not possible in the CSI spec. + return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("CSI does not support ReadOnlyMany and ReadWriteOnce on the same PersistentVolume") + + case m[v1.ReadOnlyMany]: + return csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY, nil + + case m[v1.ReadWriteOnce]: + return csi.VolumeCapability_AccessMode_SINGLE_NODE_MULTI_WRITER, nil + + default: + return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("unsupported AccessMode combination: %+v", pvAccessModes) + } +} + +func uniqueAccessModes(pvAccessModes []v1.PersistentVolumeAccessMode) map[v1.PersistentVolumeAccessMode]bool { + m := map[v1.PersistentVolumeAccessMode]bool{} + for _, mode := range pvAccessModes { + m[mode] = true + } + return m +} diff --git a/vendor/github.com/kubernetes-csi/csi-lib-utils/connection/connection.go b/vendor/github.com/kubernetes-csi/csi-lib-utils/connection/connection.go index fbd8d37b5f..ad37321e83 100644 --- a/vendor/github.com/kubernetes-csi/csi-lib-utils/connection/connection.go +++ b/vendor/github.com/kubernetes-csi/csi-lib-utils/connection/connection.go @@ -84,7 +84,8 @@ func ExitOnConnectionLoss() func() bool { if err := ioutil.WriteFile(terminationLogPath, []byte(terminationMsg), 0644); err != nil { klog.Errorf("%s: %s", terminationLogPath, err) } - klog.Fatalf(terminationMsg) + klog.Exit(terminationMsg) + // Not reached. return false } } diff --git a/vendor/modules.txt b/vendor/modules.txt index 97781e927a..5ea78bcf73 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -55,8 +55,9 @@ github.com/imdario/mergo github.com/inconshreveable/mousetrap # github.com/json-iterator/go v1.1.11 github.com/json-iterator/go -# github.com/kubernetes-csi/csi-lib-utils v0.9.1 +# github.com/kubernetes-csi/csi-lib-utils v0.9.2-0.20210723114316-fd1f57183b91 ## explicit +github.com/kubernetes-csi/csi-lib-utils/accessmodes github.com/kubernetes-csi/csi-lib-utils/connection github.com/kubernetes-csi/csi-lib-utils/leaderelection github.com/kubernetes-csi/csi-lib-utils/metrics