Skip to content

Commit

Permalink
[cinder-csi-plugin] ephemeral volume removal (#2602)
Browse files Browse the repository at this point in the history
Remove openstack credits from node plugin

Signed-off-by: Serge Logvinov <[email protected]>
  • Loading branch information
sergelogvinov committed Sep 4, 2024
1 parent fb95c62 commit 0747317
Show file tree
Hide file tree
Showing 13 changed files with 139 additions and 173 deletions.
4 changes: 2 additions & 2 deletions charts/cinder-csi-plugin/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
apiVersion: v1
appVersion: v1.30.0
appVersion: v1.31.0
description: Cinder CSI Chart for OpenStack
name: openstack-cinder-csi
version: 2.30.1-alpha.1
version: 2.31.0-alpha.1
home: https://github.com/kubernetes/cloud-provider-openstack
icon: https://github.com/kubernetes/kubernetes/blob/master/logo/logo.png
maintainers:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ spec:
- "--endpoint=$(CSI_ENDPOINT)"
- "--cloud-config=$(CLOUD_CONFIG)"
- "--cluster=$(CLUSTER_NAME)"
- "--provide-node-service=false"
{{- if .Values.csi.plugin.httpEndpoint.enabled }}
- "--http-endpoint=:{{ .Values.csi.plugin.httpEndpoint.port }}"
{{- end }}
Expand Down
7 changes: 4 additions & 3 deletions charts/cinder-csi-plugin/templates/nodeplugin-daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ spec:
- /bin/cinder-csi-plugin
- "-v={{ .Values.logVerbosityLevel }}"
- "--endpoint=$(CSI_ENDPOINT)"
- "--cloud-config=$(CLOUD_CONFIG)"
- "--provide-controller-service=false"
# - "--cloud-config=$(CLOUD_CONFIG)"
{{- if .Values.csi.plugin.extraArgs }}
{{- with .Values.csi.plugin.extraArgs }}
{{- tpl . $ | trim | nindent 12 }}
Expand All @@ -100,8 +101,8 @@ spec:
env:
- name: CSI_ENDPOINT
value: unix://csi/csi.sock
- name: CLOUD_CONFIG
value: /etc/kubernetes/{{ .Values.secret.filename }}
# - name: CLOUD_CONFIG
# value: /etc/kubernetes/{{ .Values.secret.filename }}
{{- if .Values.csi.plugin.extraEnv }}
{{- toYaml .Values.csi.plugin.extraEnv | nindent 12 }}
{{- end }}
Expand Down
81 changes: 59 additions & 22 deletions cmd/cinder-csi-plugin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,18 @@ import (
)

var (
endpoint string
nodeID string
cloudConfig []string
cloudNames []string
additionalTopologies map[string]string
cluster string
httpEndpoint string
endpoint string
nodeID string
cloudConfig []string
cloudNames []string
additionalTopologies map[string]string
cluster string
httpEndpoint string

searchOrder string
rescanOnResize bool
nodeVolumeAttachLimit int64

provideControllerService bool
provideNodeService bool
)
Expand All @@ -49,6 +54,17 @@ func main() {
Run: func(cmd *cobra.Command, args []string) {
handle()
},
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
f := cmd.Flags()

if provideControllerService {
if configs, err := f.GetStringSlice("cloud-config"); err != nil || len(configs) == 0 {
klog.Fatalf("Unable to mark flag cloud-config to be required")
}
}

return nil
},
Version: version.Version,
}

Expand All @@ -62,10 +78,7 @@ func main() {
klog.Fatalf("Unable to mark flag endpoint to be required: %v", err)
}

cmd.PersistentFlags().StringSliceVar(&cloudConfig, "cloud-config", nil, "CSI driver cloud config. This option can be given multiple times")
if err := cmd.MarkPersistentFlagRequired("cloud-config"); err != nil {
klog.Fatalf("Unable to mark flag cloud-config to be required: %v", err)
}
cmd.Flags().StringSliceVar(&cloudConfig, "cloud-config", nil, "CSI driver cloud config. This option can be given multiple times")

cmd.PersistentFlags().StringSliceVar(&cloudNames, "cloud-name", []string{""}, "Cloud name to instruct CSI driver to read additional OpenStack cloud credentials from the configuration subsections. This option can be specified multiple times to manage multiple OpenStack clouds.")
cmd.PersistentFlags().StringToStringVar(&additionalTopologies, "additional-topology", map[string]string{}, "Additional CSI driver topology keys, for example topology.kubernetes.io/region=REGION1. This option can be specified multiple times to add multiple additional topology keys.")
Expand All @@ -76,6 +89,12 @@ func main() {
cmd.PersistentFlags().BoolVar(&provideControllerService, "provide-controller-service", true, "If set to true then the CSI driver does provide the controller service (default: true)")
cmd.PersistentFlags().BoolVar(&provideNodeService, "provide-node-service", true, "If set to true then the CSI driver does provide the node service (default: true)")

cmd.PersistentFlags().StringVar(&searchOrder, "search-order", "configDrive,metadataService", "The search order for metadata service")

// Node specific flags
cmd.PersistentFlags().BoolVar(&rescanOnResize, "rescan-on-resize", false, "If set to true then the CSI driver will rescan the device on volume resize")
cmd.PersistentFlags().Int64Var(&nodeVolumeAttachLimit, "node-volume-attach-limit", 256, "The maximum number of volumes that can be attached to a node")

openstack.AddExtraFlags(pflag.CommandLine)

code := cli.Run(cmd)
Expand All @@ -87,28 +106,46 @@ func handle() {
d := cinder.NewDriver(&cinder.DriverOpts{Endpoint: endpoint, ClusterID: cluster})

openstack.InitOpenStackProvider(cloudConfig, httpEndpoint)
var err error
clouds := make(map[string]openstack.IOpenStack)
for _, cloudName := range cloudNames {
clouds[cloudName], err = openstack.GetOpenStackProvider(cloudName)
if err != nil {
klog.Warningf("Failed to GetOpenStackProvider %s: %v", cloudName, err)
return
}
}

if provideControllerService {
clouds := make(map[string]openstack.IOpenStack, len(cloudNames))
for _, cloudName := range cloudNames {
var err error

clouds[cloudName], err = openstack.GetOpenStackProvider(cloudName)
if err != nil {
klog.Warningf("Failed to GetOpenStackProvider %s: %v", cloudName, err)
return
}
}

d.SetupControllerService(clouds)
}

if provideNodeService {
//Initialize mount
mount := mount.GetMountProvider()

//Backward compatibility, read [BlockStorage] parameters from cloud config
cfg, err := openstack.GetConfigFromFiles(cloudConfig)
if err == nil {
if cfg.Metadata.SearchOrder != "" {
searchOrder = cfg.Metadata.SearchOrder
}

if cfg.BlockStorage.RescanOnResize {
rescanOnResize = cfg.BlockStorage.RescanOnResize
}

if cfg.BlockStorage.NodeVolumeAttachLimit < nodeVolumeAttachLimit {
nodeVolumeAttachLimit = cfg.BlockStorage.NodeVolumeAttachLimit
}
}

//Initialize Metadata
metadata := metadata.GetMetadataProvider(clouds[cloudNames[0]].GetMetadataOpts().SearchOrder)
metadata := metadata.GetMetadataProvider(searchOrder)

d.SetupNodeService(clouds[cloudNames[0]], mount, metadata, additionalTopologies)
d.SetupNodeService(mount, metadata, rescanOnResize, nodeVolumeAttachLimit, additionalTopologies)
}

d.Run()
Expand Down
10 changes: 5 additions & 5 deletions docs/cinder-csi-plugin/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

Dynamic Provisioning uses persistence volume claim (PVC) to request the Kubernetes to create the Cinder volume on behalf of user and consumes the volume from inside container.

For usage, refer [sample app](./examples.md#dynamic-volume-provisioning)
For usage, refer [sample app](./examples.md#dynamic-volume-provisioning)

## Topology

Expand All @@ -35,7 +35,7 @@ This feature enables driver to consider the topology constraints while creating
`topology.cinder.csi.openstack.org/zone` : Availability by Zone
* `allowedTopologies` can be specified in storage class to restrict the topology of provisioned volumes to specific zones and should be used as replacement of `availability` parameter.
* To disable: set `--feature-gates=Topology=false` in external-provisioner (container `csi-provisioner` of `csi-cinder-controllerplugin`).
* If using Helm, it can be disabled by setting `Values.csi.provisioner.topology: "false"`
* If using Helm, it can be disabled by setting `Values.csi.provisioner.topology: "false"`

For usage, refer [sample app](./examples.md#use-topology)

Expand All @@ -51,7 +51,7 @@ For usage, refer [sample app](./examples.md#using-block-volume)

## Volume Expansion

Driver supports both `Offline` and `Online` resize of cinder volumes. Cinder online resize support is available since cinder 3.42 microversion.
Driver supports both `Offline` and `Online` resize of cinder volumes. Cinder online resize support is available since cinder 3.42 microversion.
The same should be supported by underlying OpenStack Cloud to avail the feature.

* As of kubernetes v1.16, Volume Expansion is a beta feature and enabled by default.
Expand All @@ -60,7 +60,7 @@ The same should be supported by underlying OpenStack Cloud to avail the feature.

### Rescan on in-use volume resize

Some hypervizors (like VMware) don't automatically send a new volume size to a Linux kernel, when a volume is in-use. Sending a "1" to `/sys/class/block/XXX/device/rescan` is telling the SCSI block device to refresh it's information about where it's ending boundary is (among other things) to give the kernel information about it's updated size. When a `rescan-on-resize` flag is set in a CSI node driver cloud-config `[BlockStorage]` section, a CSI node driver will rescan block device and verify its size before expanding the filesystem. CSI driver will raise an error, when expected volume size cannot be detected.
Some hypervizors (like VMware) don't automatically send a new volume size to a Linux kernel, when a volume is in-use. Sending a "1" to `/sys/class/block/XXX/device/rescan` is telling the SCSI block device to refresh it's information about where it's ending boundary is (among other things) to give the kernel information about it's updated size. When a `rescan-on-resize` flag is set in a CSI node driver cloud-config `[BlockStorage]` section or through the CLI parameter `--rescan-on-resize`, a CSI node driver will rescan block device and verify its size before expanding the filesystem. CSI driver will raise an error, when expected volume size cannot be detected.

Not all hypervizors have a `/sys/class/block/XXX/device/rescan` location, therefore if you enable this option and your hypervizor doesn't support this, you'll get a warning log on resize event. It is recommended to disable this option in this case.

Expand All @@ -81,7 +81,7 @@ Two different Kubernetes features allow volumes to follow the Pod's lifecycle: C

This feature allows CSI volumes to be directly embedded in the Pod specification instead of a PersistentVolume. Volumes specified in this way are ephemeral and do not persist across Pod restarts.

* As of Kubernetes v1.16 this feature is beta so enabled by default.
* As of Kubernetes v1.16 this feature is beta so enabled by default.
* To enable this feature for CSI Driver, `volumeLifecycleModes` needs to be specified in [CSIDriver](../../manifests/cinder-csi-plugin/csi-cinder-driver.yaml) object. The driver can run in `Persistent` mode, `Ephemeral` or in both modes.
* `podInfoOnMount` must be `true` to use this feature.
* For usage, refer [sample app](./examples.md#deploy-app-using-inline-volumes)
Expand Down
4 changes: 2 additions & 2 deletions docs/cinder-csi-plugin/using-cinder-csi-plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ The following sections are supported in configuration file.
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
These configuration options pertain to block storage and should appear in the `[BlockStorage]` section of the `$CLOUD_CONFIG` file.
These configuration options pertain to block storage and should appear in the `[BlockStorage]` section of the `$CLOUD_CONFIG` file. Some parameters can be passed as command line arguments as well.

* `node-volume-attach-limit`
Optional. To configure maximum volumes that can be attached to the node. Its default value is `256`.
Expand Down Expand Up @@ -270,7 +270,7 @@ helm install --namespace kube-system --name cinder-csi ./charts/cinder-csi-plugi
| VolumeSnapshotClass `parameters` | `type` | Empty String | `snapshot` creates a VolumeSnapshot object linked to a Cinder volume snapshot. `backup` creates a VolumeSnapshot object linked to a cinder volume backup. Defaults to `snapshot` if not defined |
| VolumeSnapshotClass `parameters` | `backup-max-duration-seconds-per-gb` | `20` | Defines the amount of time to wait for a backup to complete in seconds per GB of volume size |
| VolumeSnapshotClass `parameters` | `availability` | Same as volume | String. Backup Availability Zone |
| 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
Expand Down
13 changes: 13 additions & 0 deletions pkg/csi/cinder/controllerserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1061,8 +1061,11 @@ func (cs *controllerServer) ControllerExpandVolume(ctx context.Context, req *csi
func getCreateVolumeResponse(vol *volumes.Volume, ignoreVolumeAZ bool, accessibleTopologyReq *csi.TopologyRequirement) *csi.CreateVolumeResponse {

var volsrc *csi.VolumeContentSource
resizeRequired := false

if vol.SnapshotID != "" {
resizeRequired = true

volsrc = &csi.VolumeContentSource{
Type: &csi.VolumeContentSource_Snapshot{
Snapshot: &csi.VolumeContentSource_SnapshotSource{
Expand All @@ -1073,6 +1076,8 @@ func getCreateVolumeResponse(vol *volumes.Volume, ignoreVolumeAZ bool, accessibl
}

if vol.SourceVolID != "" {
resizeRequired = true

volsrc = &csi.VolumeContentSource{
Type: &csi.VolumeContentSource_Volume{
Volume: &csi.VolumeContentSource_VolumeSource{
Expand All @@ -1083,6 +1088,8 @@ func getCreateVolumeResponse(vol *volumes.Volume, ignoreVolumeAZ bool, accessibl
}

if vol.BackupID != nil && *vol.BackupID != "" {
resizeRequired = true

volsrc = &csi.VolumeContentSource{
Type: &csi.VolumeContentSource_Snapshot{
Snapshot: &csi.VolumeContentSource_SnapshotSource{
Expand All @@ -1107,12 +1114,18 @@ func getCreateVolumeResponse(vol *volumes.Volume, ignoreVolumeAZ bool, accessibl
}
}

volCnx := map[string]string{}
if resizeRequired {
volCnx[ResizeRequired] = "true"
}

resp := &csi.CreateVolumeResponse{
Volume: &csi.Volume{
VolumeId: vol.ID,
CapacityBytes: int64(vol.Size * 1024 * 1024 * 1024),
AccessibleTopology: accessibleTopology,
ContentSource: volsrc,
VolumeContext: volCnx,
},
}

Expand Down
15 changes: 13 additions & 2 deletions pkg/csi/cinder/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ import (
const (
driverName = "cinder.csi.openstack.org"
topologyKey = "topology." + driverName + "/zone"

// MaxVolumesPerNode is the maximum number of volumes that can be attached to a node
MaxVolumesPerNode = 256

// ResizeRequired parameter, if set to true, will trigger a resize on mount operation
ResizeRequired = "resizeRequired"
)

var (
Expand Down Expand Up @@ -177,9 +183,14 @@ func (d *Driver) SetupControllerService(clouds map[string]openstack.IOpenStack)
d.cs = NewControllerServer(d, clouds)
}

func (d *Driver) SetupNodeService(cloud openstack.IOpenStack, mount mount.IMount, metadata metadata.IMetadata, topologies map[string]string) {
func (d *Driver) SetupNodeService(mount mount.IMount, metadata metadata.IMetadata, rescanOnResize bool, nodeVolumeAttachLimit int64, topologies map[string]string) {
klog.Info("Providing node service")
d.ns = NewNodeServer(d, mount, metadata, cloud, topologies)

if nodeVolumeAttachLimit < 0 || nodeVolumeAttachLimit > MaxVolumesPerNode {
nodeVolumeAttachLimit = MaxVolumesPerNode
}

d.ns = NewNodeServer(d, mount, metadata, nodeVolumeAttachLimit, rescanOnResize, topologies)
}

func (d *Driver) Run() {
Expand Down
Loading

0 comments on commit 0747317

Please sign in to comment.