From 86b647f8da3b576ae46459d6ac2c94afba00deac Mon Sep 17 00:00:00 2001 From: Tobias Wolf Date: Wed, 24 Jan 2024 10:39:36 +0100 Subject: [PATCH] cinder-csi: Add basic support for an `encrypted` storage class parameter --- .../using-cinder-csi-plugin.md | 15 ++++--- pkg/csi/cinder/controllerserver.go | 6 ++- pkg/csi/cinder/nodeserver.go | 2 +- pkg/csi/cinder/openstack/openstack.go | 2 +- pkg/csi/cinder/openstack/openstack_mock.go | 12 +++--- pkg/csi/cinder/openstack/openstack_volumes.go | 43 ++++++++++++++++++- tests/sanity/cinder/fakecloud.go | 3 +- 7 files changed, 64 insertions(+), 19 deletions(-) diff --git a/docs/cinder-csi-plugin/using-cinder-csi-plugin.md b/docs/cinder-csi-plugin/using-cinder-csi-plugin.md index 4bcb1eaeed..ddf46868ac 100644 --- a/docs/cinder-csi-plugin/using-cinder-csi-plugin.md +++ b/docs/cinder-csi-plugin/using-cinder-csi-plugin.md @@ -114,7 +114,7 @@ Implementation of `cinder-csi-plugin` relies on following OpenStack services. For Driver configuration, parameters must be passed via configuration file specified in `$CLOUD_CONFIG` environment variable. The following sections are supported in configuration file. -### Global +### Global For Cinder CSI Plugin to authenticate with OpenStack Keystone, required parameters needs to be passed in `[Global]` section of the file. For all supported parameters, please refer [Global](../openstack-cloud-controller-manager/using-openstack-cloud-controller-manager.md#global) section. ### Block Storage @@ -196,7 +196,7 @@ cinder.csi.openstack.org true true false < mountPath: /etc/cacert readOnly: true - volumes: + volumes: .... - name: cacert hostPath: @@ -253,8 +253,9 @@ helm install --namespace kube-system --name cinder-csi ./charts/cinder-csi-plugi |------------------------- |-----------------------|-----------------|-----------------| | StorageClass `parameters` | `availability` | `nova` | String. Volume Availability Zone | | StorageClass `parameters` | `type` | Empty String | String. Name/ID of Volume type. Corresponding volume type should exist in cinder | +| StorageClass `parameters` | `encrypted` | Empty String | String. True if Volume Encryption is requested | | VolumeSnapshotClass `parameters` | `force-create` | `false` | Enable to support creating snapshot for a volume in in-use status | -| Inline Volume `volumeAttributes` | `capacity` | `1Gi` | volume size for creating inline volumes| +| Inline Volume `volumeAttributes` | `capacity` | `1Gi` | volume size for creating inline volumes| | Inline Volume `VolumeAttributes` | `type` | Empty String | Name/ID of Volume type. Corresponding volume type should exist in cinder | ## Local Development @@ -266,14 +267,14 @@ To build the plugin, run ``` $ export ARCH=amd64 # Defaults to amd64 $ make build-cmd-cinder-csi-plugin -``` +``` To build cinder-csi-plugin image ``` $ export ARCH=amd64 # Defaults to amd64 $ make build-local-image-cinder-csi-plugin -``` +``` ### Testing @@ -284,7 +285,7 @@ To run all unit tests: $ make test ``` #### Sanity Tests -Sanity tests ensures the CSI spec conformance of the driver. For more info, refer [Sanity check](https://github.com/kubernetes-csi/csi-test/tree/master/pkg/sanity) +Sanity tests ensures the CSI spec conformance of the driver. For more info, refer [Sanity check](https://github.com/kubernetes-csi/csi-test/tree/master/pkg/sanity) Run sanity tests for cinder CSI driver using: @@ -298,5 +299,5 @@ Optionally, to test the driver csc tool could be used. please refer, [usage guid Starting from Kubernetes 1.21, OpenStack Cinder CSI migration is supported as beta feature and is `ON` by default. Cinder CSI driver must be installed on clusters on OpenStack for Cinder volumes to work. If you have persistence volumes that are created with in-tree `kubernetes.io/cinder` plugin, you could migrate to use `cinder.csi.openstack.org` Container Storage Interface (CSI) Driver. -* The CSI Migration feature for Cinder, when enabled, shims all plugin operations from the existing in-tree plugin to the `cinder.csi.openstack.org` CSI Driver. +* The CSI Migration feature for Cinder, when enabled, shims all plugin operations from the existing in-tree plugin to the `cinder.csi.openstack.org` CSI Driver. * For more info, please refer [Migrate to CCM with CSI Migration](../openstack-cloud-controller-manager/migrate-to-ccm-with-csimigration.md#migrate-from-in-tree-cloud-provider-to-openstack-cloud-controller-manager-and-enable-csimigration) guide diff --git a/pkg/csi/cinder/controllerserver.go b/pkg/csi/cinder/controllerserver.go index 110d3e3ec8..1e561cd3c8 100644 --- a/pkg/csi/cinder/controllerserver.go +++ b/pkg/csi/cinder/controllerserver.go @@ -134,12 +134,14 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol } } - vol, err := cloud.CreateVolume(volName, volSizeGB, volType, volAvailability, snapshotID, sourcevolID, &properties) + // Volume Encryption + encrypted := (req.GetParameters()["encrypted"] == "true") + + vol, err := cloud.CreateVolume(volName, volSizeGB, volType, encrypted, volAvailability, snapshotID, sourcevolID, &properties) if err != nil { klog.Errorf("Failed to CreateVolume: %v", err) return nil, status.Errorf(codes.Internal, "CreateVolume failed with error %v", err) - } klog.V(4).Infof("CreateVolume: Successfully created volume %s in Availability Zone: %s of size %d GiB", vol.ID, vol.AvailabilityZone, vol.Size) diff --git a/pkg/csi/cinder/nodeserver.go b/pkg/csi/cinder/nodeserver.go index 23058fcef9..50bfa2c5d7 100644 --- a/pkg/csi/cinder/nodeserver.go +++ b/pkg/csi/cinder/nodeserver.go @@ -150,7 +150,7 @@ func nodePublishEphemeral(req *csi.NodePublishVolumeRequest, ns *nodeServer) (*c volumeType = "" } - evol, err := ns.Cloud.CreateVolume(volName, size, volumeType, volAvailability, "", "", &properties) + evol, err := ns.Cloud.CreateVolume(volName, size, volumeType, false, volAvailability, "", "", &properties) if err != nil { klog.V(3).Infof("Failed to Create Ephemeral Volume: %v", err) diff --git a/pkg/csi/cinder/openstack/openstack.go b/pkg/csi/cinder/openstack/openstack.go index e3a76a167f..5172ae4b70 100644 --- a/pkg/csi/cinder/openstack/openstack.go +++ b/pkg/csi/cinder/openstack/openstack.go @@ -44,7 +44,7 @@ func AddExtraFlags(fs *pflag.FlagSet) { } type IOpenStack interface { - CreateVolume(name string, size int, vtype, availability string, snapshotID string, sourcevolID string, tags *map[string]string) (*volumes.Volume, error) + CreateVolume(name string, size int, vtype string, encrypted bool, availability string, snapshotID string, sourcevolID string, tags *map[string]string) (*volumes.Volume, error) DeleteVolume(volumeID string) error AttachVolume(instanceID, volumeID string) (string, error) ListVolumes(limit int, startingToken string) ([]volumes.Volume, string, error) diff --git a/pkg/csi/cinder/openstack/openstack_mock.go b/pkg/csi/cinder/openstack/openstack_mock.go index 5c52cf0fbf..f050d287b4 100644 --- a/pkg/csi/cinder/openstack/openstack_mock.go +++ b/pkg/csi/cinder/openstack/openstack_mock.go @@ -71,20 +71,20 @@ func (_m *OpenStackMock) AttachVolume(instanceID string, volumeID string) (strin return r0, r1 } -// CreateVolume provides a mock function with given fields: name, size, vtype, availability, tags -func (_m *OpenStackMock) CreateVolume(name string, size int, vtype string, availability string, snapshotID string, sourceVolID string, tags *map[string]string) (*volumes.Volume, error) { +// CreateVolume provides a mock function with given fields: name, size, vtype, encrypted, availability, tags +func (_m *OpenStackMock) CreateVolume(name string, size int, vtype string, encrypted bool, availability string, snapshotID string, sourceVolID string, tags *map[string]string) (*volumes.Volume, error) { ret := _m.Called(name, size, vtype, availability, snapshotID, sourceVolID, tags) var r0 *volumes.Volume - if rf, ok := ret.Get(0).(func(string, int, string, string, string, string, *map[string]string) *volumes.Volume); ok { - r0 = rf(name, size, vtype, availability, snapshotID, sourceVolID, tags) + if rf, ok := ret.Get(0).(func(string, int, string, bool, string, string, string, *map[string]string) *volumes.Volume); ok { + r0 = rf(name, size, vtype, encrypted, availability, snapshotID, sourceVolID, tags) } else { r0 = ret.Get(0).(*volumes.Volume) } var r1 error - if rf, ok := ret.Get(1).(func(string, int, string, string, string, string, *map[string]string) error); ok { - r1 = rf(name, size, vtype, availability, snapshotID, sourceVolID, tags) + if rf, ok := ret.Get(1).(func(string, int, string, bool, string, string, string, *map[string]string) error); ok { + r1 = rf(name, size, vtype, encrypted, availability, snapshotID, sourceVolID, tags) } else { r1 = ret.Error(1) } diff --git a/pkg/csi/cinder/openstack/openstack_volumes.go b/pkg/csi/cinder/openstack/openstack_volumes.go index 9ef45b455a..dd34b3a3b2 100644 --- a/pkg/csi/cinder/openstack/openstack_volumes.go +++ b/pkg/csi/cinder/openstack/openstack_volumes.go @@ -24,6 +24,7 @@ import ( "github.com/gophercloud/gophercloud/openstack" volumeexpand "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions" "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes" + "github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach" "github.com/gophercloud/gophercloud/pagination" "k8s.io/apimachinery/pkg/util/wait" @@ -51,7 +52,20 @@ const ( var volumeErrorStates = [...]string{"error", "error_extending", "error_deleting"} // CreateVolume creates a volume of given size -func (os *OpenStack) CreateVolume(name string, size int, vtype, availability string, snapshotID string, sourcevolID string, tags *map[string]string) (*volumes.Volume, error) { +func (os *OpenStack) CreateVolume(name string, size int, vtype string, encrypted bool, availability string, snapshotID string, sourcevolID string, tags *map[string]string) (*volumes.Volume, error) { + if encrypted { + volType, err := os.getVolumeTypeByName(vtype) + if err != nil { + return nil, err + } else if nil == volType { + return nil, fmt.Errorf("Failed to find the VolumeType %q given", vtype) + } + + _, err = volumetypes.GetEncryption(os.blockstorage, volType.ID).Extract() + if err != nil { + return nil, err + } + } opts := &volumes.CreateOpts{ Name: name, @@ -412,6 +426,33 @@ func (os *OpenStack) diskIsUsed(volumeID string) (bool, error) { return false, nil } +// getVolumeTypeByName is a wrapper around ListVolumeTypes that creates a Name filter to act as a GetByUniqueName +// Returns the Volume Type referenced with the specified name +func (os *OpenStack) getVolumeTypeByName(n string) (*volumetypes.VolumeType, error) { + var matchingVolType *volumetypes.VolumeType + + opts := volumetypes.ListOpts{} + volumetypes.List(os.blockstorage, opts).EachPage(func(page pagination.Page) (bool, error) { + var err error + + volTypes, err := volumetypes.ExtractVolumeTypes(page) + if err != nil { + return false, err + } + + for _, volType := range volTypes { + if n == volType.Name { + matchingVolType = &volType + return false, nil + } + } + + return true, nil + }) + + return matchingVolType, nil +} + // GetBlockStorageOpts returns OpenStack block storage options func (os *OpenStack) GetBlockStorageOpts() BlockStorageOpts { return os.bsOpts diff --git a/tests/sanity/cinder/fakecloud.go b/tests/sanity/cinder/fakecloud.go index d1bbed949f..2b715e9fe0 100644 --- a/tests/sanity/cinder/fakecloud.go +++ b/tests/sanity/cinder/fakecloud.go @@ -32,7 +32,7 @@ func getfakecloud() *cloud { var _ openstack.IOpenStack = &cloud{} // Fake Cloud -func (cloud *cloud) CreateVolume(name string, size int, vtype, availability string, snapshotID string, sourceVolID string, tags *map[string]string) (*volumes.Volume, error) { +func (cloud *cloud) CreateVolume(name string, size int, vtype string, encrypted bool, availability string, snapshotID string, sourceVolID string, tags *map[string]string) (*volumes.Volume, error) { vol := &volumes.Volume{ ID: randString(10), @@ -43,6 +43,7 @@ func (cloud *cloud) CreateVolume(name string, size int, vtype, availability stri AvailabilityZone: availability, SnapshotID: snapshotID, SourceVolID: sourceVolID, + Encrypted: encrypted, } cloud.volumes[vol.ID] = vol