From ceb019d5e979a25cefa269e97f64687079284cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Fl=C3=B6tzinger?= Date: Wed, 5 Nov 2025 12:31:37 +0100 Subject: [PATCH 01/10] Fix manifest creation caused by missing linebreaks --- .github/workflows/release.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c1125b0..051302c 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -128,15 +128,15 @@ jobs: - name: Create manifest run: | VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,' | sed -e 's/^v//') - echo "---" >> manifest.yaml + printf "---\n" >> manifest.yaml cat deploy/k8s/rbac.yaml >> manifest.yaml - echo "---" >> manifest.yaml + printf "---\n" >> manifest.yaml cat deploy/k8s/csidriver.yaml >> manifest.yaml - echo "---" >> manifest.yaml + printf "---\n" >> manifest.yaml sed -E "s|(image: +${REGISTRY_NAME}/cloudstack-csi-driver)(:[^ ]+)?|\\1:${VERSION}|" deploy/k8s/controller-deployment.yaml >> manifest.yaml - echo "---" >> manifest.yaml + printf "---\n" >> manifest.yaml sed -E "s|(image: +${REGISTRY_NAME}/cloudstack-csi-driver)(:[^ ]+)?|\\1:${VERSION}|" deploy/k8s/node-daemonset.yaml >> manifest.yaml - echo "---" >> manifest.yaml + printf "---\n" >> manifest.yaml cat deploy/k8s/volume-snapshot-class.yaml >> manifest.yaml - name: Create Release From fcba53cf9ee2b40120404eb6d2c116c2bad2887d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Fl=C3=B6tzinger?= Date: Wed, 5 Nov 2025 13:14:51 +0100 Subject: [PATCH 02/10] Add empty option to description for metadataSource as possible entry --- charts/cloudstack-csi/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/cloudstack-csi/values.yaml b/charts/cloudstack-csi/values.yaml index 3cea335..1736b9b 100644 --- a/charts/cloudstack-csi/values.yaml +++ b/charts/cloudstack-csi/values.yaml @@ -280,7 +280,7 @@ node: annotations: {} automountServiceAccountToken: true # Metadata source to try to find instance ID. - # Possible values 'cloud-init' & 'ignition' + # Possible values 'cloud-init' & 'ignition' or '' metadataSource: ignition # The maximum number of volumes that can be attached to a node volumeAttachLimit: From c22f761deeae925b94158ac6afeeae8ab0678a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Fl=C3=B6tzinger?= Date: Wed, 5 Nov 2025 13:15:48 +0100 Subject: [PATCH 03/10] Bump app version to 3.0.0 of csi driver in helm chart --- charts/cloudstack-csi/Chart.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/cloudstack-csi/Chart.yaml b/charts/cloudstack-csi/Chart.yaml index 48ccd0d..2a633d4 100644 --- a/charts/cloudstack-csi/Chart.yaml +++ b/charts/cloudstack-csi/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: cloudstack-csi description: A Helm chart for CloudStack CSI driver type: application -version: 3.0.0 -appVersion: 0.6.1 +version: 3.0.1 +appVersion: 3.0.0 sources: - https://github.com/cloudstack/cloudstack-csi-driver keywords: From f79a93f1c078d7e4bdf90c85c7943607065aac5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Fl=C3=B6tzinger?= Date: Wed, 25 Feb 2026 14:13:39 +0100 Subject: [PATCH 04/10] Fix bug in client to also use project id when getting VM by name --- pkg/cloud/vms.go | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/pkg/cloud/vms.go b/pkg/cloud/vms.go index 2df3c7f..e52f15a 100644 --- a/pkg/cloud/vms.go +++ b/pkg/cloud/vms.go @@ -22,46 +22,44 @@ package cloud import ( "context" + "github.com/apache/cloudstack-go/v2/cloudstack" "k8s.io/klog/v2" ) func (c *client) GetVMByID(ctx context.Context, vmID string) (*VM, error) { logger := klog.FromContext(ctx) - p := c.VirtualMachine.NewListVirtualMachinesParams() - p.SetId(vmID) - if c.projectID != "" { - p.SetProjectid(c.projectID) - } logger.V(2).Info("CloudStack API call", "command", "ListVirtualMachines", "params", map[string]string{ "id": vmID, "projectID": c.projectID, }) - l, err := c.VirtualMachine.ListVirtualMachines(p) - if err != nil { - return nil, err - } - if l.Count == 0 { - return nil, ErrNotFound - } - if l.Count > 1 { - return nil, ErrTooManyResults - } - vm := l.VirtualMachines[0] - logger.V(2).Info("Returning VM", "vmID", vm.Id, "zoneID", vm.Zoneid) - return &VM{ - ID: vm.Id, - ZoneID: vm.Zoneid, - }, nil + return c.getVMByParam(ctx, func(p *cloudstack.ListVirtualMachinesParams) { + p.SetId(vmID) + }) } func (c *client) getVMByName(ctx context.Context, name string) (*VM, error) { logger := klog.FromContext(ctx) - p := c.VirtualMachine.NewListVirtualMachinesParams() - p.SetName(name) logger.V(2).Info("CloudStack API call", "command", "ListVirtualMachines", "params", map[string]string{ - "name": name, + "name": name, + "projectID": c.projectID, + }) + + return c.getVMByParam(ctx, func(p *cloudstack.ListVirtualMachinesParams) { + p.SetName(name) }) +} + +func (c *client) getVMByParam(ctx context.Context, setParams func(p *cloudstack.ListVirtualMachinesParams)) (*VM, error) { + p := c.VirtualMachine.NewListVirtualMachinesParams() + + if c.projectID != "" { + p.SetProjectid(c.projectID) + } + + // set params for virtual machine list + setParams(p) + l, err := c.VirtualMachine.ListVirtualMachines(p) if err != nil { return nil, err @@ -73,6 +71,7 @@ func (c *client) getVMByName(ctx context.Context, name string) (*VM, error) { return nil, ErrTooManyResults } vm := l.VirtualMachines[0] + klog.FromContext(ctx).V(2).Info("Returning VM", "vmID", vm.Id, "zoneID", vm.Zoneid) return &VM{ ID: vm.Id, From 18ef184e4de664e4c58c100866376b21ebccb8a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Fl=C3=B6tzinger?= Date: Wed, 25 Feb 2026 15:18:36 +0100 Subject: [PATCH 05/10] Remove all occurrence of global projectID in cloudstack client because we can set it as default option --- pkg/cloud/cloud.go | 12 ++++++++++-- pkg/cloud/snapshots.go | 20 ++++---------------- pkg/cloud/vms.go | 10 ++-------- pkg/cloud/volumes.go | 11 +---------- 4 files changed, 17 insertions(+), 36 deletions(-) diff --git a/pkg/cloud/cloud.go b/pkg/cloud/cloud.go index 19109a3..a36b1b9 100644 --- a/pkg/cloud/cloud.go +++ b/pkg/cloud/cloud.go @@ -26,6 +26,7 @@ import ( "errors" "github.com/apache/cloudstack-go/v2/cloudstack" + "k8s.io/klog/v2" ) // Interface is the CloudStack client interface. @@ -70,6 +71,7 @@ type Volume struct { DeviceID string } +// Volume represents a CloudStack snapshot. type Snapshot struct { ID string Name string @@ -99,12 +101,18 @@ var ( // client is the implementation of Interface. type client struct { *cloudstack.CloudStackClient - projectID string } // New creates a new cloud connector, given its configuration. func New(config *Config) Interface { csClient := cloudstack.NewAsyncClient(config.APIURL, config.APIKey, config.SecretKey, config.VerifySSL) - return &client{csClient, config.ProjectID} + // Set the project id to every request. + // This is possible because we just could work in one project with the previous implementation. + if config.ProjectID != "" { + csClient.DefaultOptions(cloudstack.WithProject(config.ProjectID)) + klog.Background().V(2).Info("Set projectID to cloud connector", "projectID", config.ProjectID) + } + + return &client{csClient} } diff --git a/pkg/cloud/snapshots.go b/pkg/cloud/snapshots.go index fe2451f..2e001b4 100644 --- a/pkg/cloud/snapshots.go +++ b/pkg/cloud/snapshots.go @@ -34,12 +34,8 @@ func (c *client) GetSnapshotByID(ctx context.Context, snapshotID string) (*Snaps if snapshotID != "" { p.SetId(snapshotID) } - if c.projectID != "" { - p.SetProjectid(c.projectID) - } logger.V(2).Info("CloudStack API call", "command", "ListSnapshots", "params", map[string]string{ - "id": snapshotID, - "projectid": c.projectID, + "id": snapshotID, }) l, err := c.Snapshot.ListSnapshots(p) if err != nil { @@ -112,12 +108,8 @@ func (c *client) GetSnapshotByName(ctx context.Context, name string) (*Snapshot, } p := c.Snapshot.NewListSnapshotsParams() p.SetName(name) - if c.projectID != "" { - p.SetProjectid(c.projectID) - } logger.V(2).Info("CloudStack API call", "command", "ListSnapshots", "params", map[string]string{ - "name": name, - "projectid": c.projectID, + "name": name, }) l, err := c.Snapshot.ListSnapshots(p) if err != nil { @@ -152,13 +144,9 @@ func (c *client) ListSnapshots(ctx context.Context, volumeID, snapshotID string) if volumeID != "" { p.SetVolumeid(volumeID) } - if c.projectID != "" { - p.SetProjectid(c.projectID) - } logger.V(2).Info("CloudStack API call", "command", "ListSnapshots", "params", map[string]string{ - "id": snapshotID, - "volumeid": volumeID, - "projectid": c.projectID, + "id": snapshotID, + "volumeid": volumeID, }) l, err := c.Snapshot.ListSnapshots(p) if err != nil { diff --git a/pkg/cloud/vms.go b/pkg/cloud/vms.go index e52f15a..05596f7 100644 --- a/pkg/cloud/vms.go +++ b/pkg/cloud/vms.go @@ -29,8 +29,7 @@ import ( func (c *client) GetVMByID(ctx context.Context, vmID string) (*VM, error) { logger := klog.FromContext(ctx) logger.V(2).Info("CloudStack API call", "command", "ListVirtualMachines", "params", map[string]string{ - "id": vmID, - "projectID": c.projectID, + "id": vmID, }) return c.getVMByParam(ctx, func(p *cloudstack.ListVirtualMachinesParams) { @@ -41,8 +40,7 @@ func (c *client) GetVMByID(ctx context.Context, vmID string) (*VM, error) { func (c *client) getVMByName(ctx context.Context, name string) (*VM, error) { logger := klog.FromContext(ctx) logger.V(2).Info("CloudStack API call", "command", "ListVirtualMachines", "params", map[string]string{ - "name": name, - "projectID": c.projectID, + "name": name, }) return c.getVMByParam(ctx, func(p *cloudstack.ListVirtualMachinesParams) { @@ -53,10 +51,6 @@ func (c *client) getVMByName(ctx context.Context, name string) (*VM, error) { func (c *client) getVMByParam(ctx context.Context, setParams func(p *cloudstack.ListVirtualMachinesParams)) (*VM, error) { p := c.VirtualMachine.NewListVirtualMachinesParams() - if c.projectID != "" { - p.SetProjectid(c.projectID) - } - // set params for virtual machine list setParams(p) diff --git a/pkg/cloud/volumes.go b/pkg/cloud/volumes.go index caaa7a3..1d69a0b 100644 --- a/pkg/cloud/volumes.go +++ b/pkg/cloud/volumes.go @@ -62,12 +62,8 @@ func (c *client) GetVolumeByID(ctx context.Context, volumeID string) (*Volume, e logger := klog.FromContext(ctx) p := c.Volume.NewListVolumesParams() p.SetId(volumeID) - if c.projectID != "" { - p.SetProjectid(c.projectID) - } logger.V(2).Info("CloudStack API call", "command", "ListVolumes", "params", map[string]string{ - "id": volumeID, - "projectid": c.projectID, + "id": volumeID, }) return c.listVolumes(p) @@ -91,15 +87,11 @@ func (c *client) CreateVolume(ctx context.Context, diskOfferingID, zoneID, name p.SetZoneid(zoneID) p.SetName(name) p.SetSize(sizeInGB) - if c.projectID != "" { - p.SetProjectid(c.projectID) - } logger.V(2).Info("CloudStack API call", "command", "CreateVolume", "params", map[string]string{ "diskofferingid": diskOfferingID, "zoneid": zoneID, "name": name, "size": strconv.FormatInt(sizeInGB, 10), - "projectid": c.projectID, }) vol, err := c.Volume.CreateVolume(p) if err != nil { @@ -199,7 +191,6 @@ func (c *client) CreateVolumeFromSnapshot(ctx context.Context, zoneID, name, pro "name": name, "size": strconv.FormatInt(sizeInGB, 10), "snapshotid": snapshotID, - "projectid": projectID, "zoneid": zoneID, }) // Execute the API call to create volume from snapshot From bfbf5f642c18630ea7557c8974fcb75544351f28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Fl=C3=B6tzinger?= Date: Wed, 25 Feb 2026 15:40:50 +0100 Subject: [PATCH 06/10] Sanitize snapshot functions to require required arguments --- pkg/cloud/snapshots.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/pkg/cloud/snapshots.go b/pkg/cloud/snapshots.go index 2e001b4..7615274 100644 --- a/pkg/cloud/snapshots.go +++ b/pkg/cloud/snapshots.go @@ -31,9 +31,7 @@ import ( func (c *client) GetSnapshotByID(ctx context.Context, snapshotID string) (*Snapshot, error) { logger := klog.FromContext(ctx) p := c.Snapshot.NewListSnapshotsParams() - if snapshotID != "" { - p.SetId(snapshotID) - } + p.SetId(snapshotID) logger.V(2).Info("CloudStack API call", "command", "ListSnapshots", "params", map[string]string{ "id": snapshotID, }) @@ -63,9 +61,7 @@ func (c *client) GetSnapshotByID(ctx context.Context, snapshotID string) (*Snaps func (c *client) CreateSnapshot(ctx context.Context, volumeID, name string) (*Snapshot, error) { logger := klog.FromContext(ctx) p := c.Snapshot.NewCreateSnapshotParams(volumeID) - if name != "" { - p.SetName(name) - } + p.SetName(name) logger.V(2).Info("CloudStack API call", "command", "CreateSnapshot", "params", map[string]string{ "volumeid": volumeID, "name": name, @@ -103,9 +99,6 @@ func (c *client) DeleteSnapshot(_ context.Context, snapshotID string) error { func (c *client) GetSnapshotByName(ctx context.Context, name string) (*Snapshot, error) { logger := klog.FromContext(ctx) - if name == "" { - return nil, ErrNotFound - } p := c.Snapshot.NewListSnapshotsParams() p.SetName(name) logger.V(2).Info("CloudStack API call", "command", "ListSnapshots", "params", map[string]string{ @@ -138,12 +131,16 @@ func (c *client) GetSnapshotByName(ctx context.Context, name string) (*Snapshot, func (c *client) ListSnapshots(ctx context.Context, volumeID, snapshotID string) ([]*Snapshot, error) { logger := klog.FromContext(ctx) p := c.Snapshot.NewListSnapshotsParams() + + // snapshotID is optional: csi.ListSnapshotsRequest if snapshotID != "" { p.SetId(snapshotID) } + // volumeID is optional: csi.ListSnapshotsRequest if volumeID != "" { p.SetVolumeid(volumeID) } + logger.V(2).Info("CloudStack API call", "command", "ListSnapshots", "params", map[string]string{ "id": snapshotID, "volumeid": volumeID, From bfa47131f2122500d93b390d699d588f66e1a787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Fl=C3=B6tzinger?= Date: Thu, 26 Feb 2026 11:51:20 +0100 Subject: [PATCH 07/10] Refactor cloud client to use project id default with other cloudstack methods --- pkg/cloud/cloud.go | 3 ++- pkg/cloud/snapshots.go | 53 ++++++++++++++---------------------------- pkg/cloud/vms.go | 38 ++++++++++-------------------- pkg/cloud/volumes.go | 47 ++++++++++++++++++------------------- 4 files changed, 54 insertions(+), 87 deletions(-) diff --git a/pkg/cloud/cloud.go b/pkg/cloud/cloud.go index a36b1b9..ad42d4e 100644 --- a/pkg/cloud/cloud.go +++ b/pkg/cloud/cloud.go @@ -101,6 +101,7 @@ var ( // client is the implementation of Interface. type client struct { *cloudstack.CloudStackClient + projectID string // Used by some specific cloudstack api calls } // New creates a new cloud connector, given its configuration. @@ -114,5 +115,5 @@ func New(config *Config) Interface { klog.Background().V(2).Info("Set projectID to cloud connector", "projectID", config.ProjectID) } - return &client{csClient} + return &client{csClient, config.ProjectID} } diff --git a/pkg/cloud/snapshots.go b/pkg/cloud/snapshots.go index 7615274..ea1b1b7 100644 --- a/pkg/cloud/snapshots.go +++ b/pkg/cloud/snapshots.go @@ -30,32 +30,23 @@ import ( func (c *client) GetSnapshotByID(ctx context.Context, snapshotID string) (*Snapshot, error) { logger := klog.FromContext(ctx) - p := c.Snapshot.NewListSnapshotsParams() - p.SetId(snapshotID) - logger.V(2).Info("CloudStack API call", "command", "ListSnapshots", "params", map[string]string{ + logger.V(2).Info("CloudStack API call", "command", "GetSnapshotByID", "params", map[string]string{ "id": snapshotID, }) - l, err := c.Snapshot.ListSnapshots(p) + + snapshot, _, err := c.Snapshot.GetSnapshotByID(snapshotID) if err != nil { return nil, err } - if l.Count == 0 { - return nil, ErrNotFound - } - if l.Count > 1 { - return nil, ErrTooManyResults - } - snapshot := l.Snapshots[0] - s := Snapshot{ + + return &Snapshot{ ID: snapshot.Id, Name: snapshot.Name, DomainID: snapshot.Domainid, ProjectID: snapshot.Projectid, ZoneID: snapshot.Zoneid, VolumeID: snapshot.Volumeid, - } - - return &s, nil + }, nil } func (c *client) CreateSnapshot(ctx context.Context, volumeID, name string) (*Snapshot, error) { @@ -72,7 +63,7 @@ func (c *client) CreateSnapshot(ctx context.Context, volumeID, name string) (*Sn return nil, status.Errorf(codes.Internal, "Error %v", err) } - snap := Snapshot{ + return &Snapshot{ ID: snapshot.Id, Name: snapshot.Name, Size: snapshot.Virtualsize, @@ -81,9 +72,7 @@ func (c *client) CreateSnapshot(ctx context.Context, volumeID, name string) (*Sn ZoneID: snapshot.Zoneid, VolumeID: snapshot.Volumeid, CreatedAt: snapshot.Created, - } - - return &snap, nil + }, nil } func (c *client) DeleteSnapshot(_ context.Context, snapshotID string) error { @@ -99,23 +88,15 @@ func (c *client) DeleteSnapshot(_ context.Context, snapshotID string) error { func (c *client) GetSnapshotByName(ctx context.Context, name string) (*Snapshot, error) { logger := klog.FromContext(ctx) - p := c.Snapshot.NewListSnapshotsParams() - p.SetName(name) - logger.V(2).Info("CloudStack API call", "command", "ListSnapshots", "params", map[string]string{ + logger.V(2).Info("CloudStack API call", "command", "GetSnapshotByName", "params", map[string]string{ "name": name, }) - l, err := c.Snapshot.ListSnapshots(p) + snapshot, _, err := c.Snapshot.GetSnapshotByName(name) if err != nil { return nil, err } - if l.Count == 0 { - return nil, ErrNotFound - } - if l.Count > 1 { - return nil, ErrTooManyResults - } - snapshot := l.Snapshots[0] - s := Snapshot{ + + return &Snapshot{ ID: snapshot.Id, Name: snapshot.Name, DomainID: snapshot.Domainid, @@ -123,15 +104,12 @@ func (c *client) GetSnapshotByName(ctx context.Context, name string) (*Snapshot, ZoneID: snapshot.Zoneid, VolumeID: snapshot.Volumeid, CreatedAt: snapshot.Created, - } - - return &s, nil + }, nil } func (c *client) ListSnapshots(ctx context.Context, volumeID, snapshotID string) ([]*Snapshot, error) { logger := klog.FromContext(ctx) p := c.Snapshot.NewListSnapshotsParams() - // snapshotID is optional: csi.ListSnapshotsRequest if snapshotID != "" { p.SetId(snapshotID) @@ -141,6 +119,11 @@ func (c *client) ListSnapshots(ctx context.Context, volumeID, snapshotID string) p.SetVolumeid(volumeID) } + // There is no list function that uses the client default project id + if c.projectID != "" { + p.SetProjectid(c.projectID) + } + logger.V(2).Info("CloudStack API call", "command", "ListSnapshots", "params", map[string]string{ "id": snapshotID, "volumeid": volumeID, diff --git a/pkg/cloud/vms.go b/pkg/cloud/vms.go index 05596f7..52ac6be 100644 --- a/pkg/cloud/vms.go +++ b/pkg/cloud/vms.go @@ -22,50 +22,36 @@ package cloud import ( "context" - "github.com/apache/cloudstack-go/v2/cloudstack" "k8s.io/klog/v2" ) func (c *client) GetVMByID(ctx context.Context, vmID string) (*VM, error) { logger := klog.FromContext(ctx) - logger.V(2).Info("CloudStack API call", "command", "ListVirtualMachines", "params", map[string]string{ + logger.V(2).Info("CloudStack API call", "command", "GetVirtualMachineByID", "params", map[string]string{ "id": vmID, }) - return c.getVMByParam(ctx, func(p *cloudstack.ListVirtualMachinesParams) { - p.SetId(vmID) - }) + vm, _, err := c.VirtualMachine.GetVirtualMachineByID(vmID) + if err != nil { + return nil, err + } + + return &VM{ + ID: vm.Id, + ZoneID: vm.Zoneid, + }, nil } func (c *client) getVMByName(ctx context.Context, name string) (*VM, error) { logger := klog.FromContext(ctx) - logger.V(2).Info("CloudStack API call", "command", "ListVirtualMachines", "params", map[string]string{ + logger.V(2).Info("CloudStack API call", "command", "GetVirtualMachineByName", "params", map[string]string{ "name": name, }) - return c.getVMByParam(ctx, func(p *cloudstack.ListVirtualMachinesParams) { - p.SetName(name) - }) -} - -func (c *client) getVMByParam(ctx context.Context, setParams func(p *cloudstack.ListVirtualMachinesParams)) (*VM, error) { - p := c.VirtualMachine.NewListVirtualMachinesParams() - - // set params for virtual machine list - setParams(p) - - l, err := c.VirtualMachine.ListVirtualMachines(p) + vm, _, err := c.VirtualMachine.GetVirtualMachineByName(name) if err != nil { return nil, err } - if l.Count == 0 { - return nil, ErrNotFound - } - if l.Count > 1 { - return nil, ErrTooManyResults - } - vm := l.VirtualMachines[0] - klog.FromContext(ctx).V(2).Info("Returning VM", "vmID", vm.Id, "zoneID", vm.Zoneid) return &VM{ ID: vm.Id, diff --git a/pkg/cloud/volumes.go b/pkg/cloud/volumes.go index 1d69a0b..de7901e 100644 --- a/pkg/cloud/volumes.go +++ b/pkg/cloud/volumes.go @@ -31,19 +31,8 @@ import ( "github.com/cloudstack/cloudstack-csi-driver/pkg/util" ) -func (c *client) listVolumes(p *cloudstack.ListVolumesParams) (*Volume, error) { - l, err := c.Volume.ListVolumes(p) - if err != nil { - return nil, err - } - if l.Count == 0 { - return nil, ErrNotFound - } - if l.Count > 1 { - return nil, ErrTooManyResults - } - vol := l.Volumes[0] - v := Volume{ +func mapVolume(vol *cloudstack.Volume) *Volume { + return &Volume{ ID: vol.Id, Name: vol.Name, Size: vol.Size, @@ -54,30 +43,36 @@ func (c *client) listVolumes(p *cloudstack.ListVolumesParams) (*Volume, error) { VirtualMachineID: vol.Virtualmachineid, DeviceID: strconv.FormatInt(vol.Deviceid, 10), } - - return &v, nil } func (c *client) GetVolumeByID(ctx context.Context, volumeID string) (*Volume, error) { logger := klog.FromContext(ctx) - p := c.Volume.NewListVolumesParams() - p.SetId(volumeID) - logger.V(2).Info("CloudStack API call", "command", "ListVolumes", "params", map[string]string{ + logger.V(2).Info("CloudStack API call", "command", "GetVolumeByID", "params", map[string]string{ "id": volumeID, }) - return c.listVolumes(p) + volume, _, err := c.Volume.GetVolumeByID(volumeID) + if err != nil { + return nil, err + } + + return mapVolume(volume), nil } func (c *client) GetVolumeByName(ctx context.Context, name string) (*Volume, error) { logger := klog.FromContext(ctx) p := c.Volume.NewListVolumesParams() p.SetName(name) - logger.V(2).Info("CloudStack API call", "command", "ListVolumes", "params", map[string]string{ + logger.V(2).Info("CloudStack API call", "command", "GetVolumeByName", "params", map[string]string{ "name": name, }) - return c.listVolumes(p) + volume, _, err := c.Volume.GetVolumeByName(name) + if err != nil { + return nil, err + } + + return mapVolume(volume), nil } func (c *client) CreateVolume(ctx context.Context, diskOfferingID, zoneID, name string, sizeInGB int64) (string, error) { @@ -87,6 +82,10 @@ func (c *client) CreateVolume(ctx context.Context, diskOfferingID, zoneID, name p.SetZoneid(zoneID) p.SetName(name) p.SetSize(sizeInGB) + // There is no create function that uses the client default project id + if c.projectID != "" { + p.SetProjectid(c.projectID) + } logger.V(2).Info("CloudStack API call", "command", "CreateVolume", "params", map[string]string{ "diskofferingid": diskOfferingID, "zoneid": zoneID, @@ -200,7 +199,7 @@ func (c *client) CreateVolumeFromSnapshot(ctx context.Context, zoneID, name, pro return nil, fmt.Errorf("failed to create volume from snapshot '%s': %w", snapshotID, err) } - v := Volume{ + return &Volume{ ID: vol.Id, Name: vol.Name, Size: vol.Size, @@ -210,7 +209,5 @@ func (c *client) CreateVolumeFromSnapshot(ctx context.Context, zoneID, name, pro ZoneID: vol.Zoneid, VirtualMachineID: vol.Virtualmachineid, DeviceID: strconv.FormatInt(vol.Deviceid, 10), - } - - return &v, nil + }, nil } From f508ea36ac5db99c3be43731ef08e077216a1b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Fl=C3=B6tzinger?= Date: Thu, 26 Feb 2026 13:16:07 +0100 Subject: [PATCH 08/10] Change description for default project id options --- pkg/cloud/cloud.go | 4 ++-- pkg/cloud/snapshots.go | 2 +- pkg/cloud/volumes.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/cloud/cloud.go b/pkg/cloud/cloud.go index ad42d4e..cc90d63 100644 --- a/pkg/cloud/cloud.go +++ b/pkg/cloud/cloud.go @@ -108,8 +108,8 @@ type client struct { func New(config *Config) Interface { csClient := cloudstack.NewAsyncClient(config.APIURL, config.APIKey, config.SecretKey, config.VerifySSL) - // Set the project id to every request. - // This is possible because we just could work in one project with the previous implementation. + // Set the project id to every request that support options. + // This is possible because we also could work in one project only with the previous implementation. if config.ProjectID != "" { csClient.DefaultOptions(cloudstack.WithProject(config.ProjectID)) klog.Background().V(2).Info("Set projectID to cloud connector", "projectID", config.ProjectID) diff --git a/pkg/cloud/snapshots.go b/pkg/cloud/snapshots.go index ea1b1b7..58f4a62 100644 --- a/pkg/cloud/snapshots.go +++ b/pkg/cloud/snapshots.go @@ -119,7 +119,7 @@ func (c *client) ListSnapshots(ctx context.Context, volumeID, snapshotID string) p.SetVolumeid(volumeID) } - // There is no list function that uses the client default project id + // There is no list function that uses the client default project id option if c.projectID != "" { p.SetProjectid(c.projectID) } diff --git a/pkg/cloud/volumes.go b/pkg/cloud/volumes.go index de7901e..621cd37 100644 --- a/pkg/cloud/volumes.go +++ b/pkg/cloud/volumes.go @@ -82,7 +82,7 @@ func (c *client) CreateVolume(ctx context.Context, diskOfferingID, zoneID, name p.SetZoneid(zoneID) p.SetName(name) p.SetSize(sizeInGB) - // There is no create function that uses the client default project id + // There is no create function that uses the client default project id option if c.projectID != "" { p.SetProjectid(c.projectID) } From fa0c9f5176543aded85c2dcfee444cc2674a969e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Fl=C3=B6tzinger?= Date: Wed, 4 Mar 2026 08:52:17 +0100 Subject: [PATCH 09/10] Add back the count checks in vms, volumes, snapshots to not break the CSI driver logic --- pkg/cloud/snapshots.go | 12 ++++++++++-- pkg/cloud/vms.go | 12 ++++++++++-- pkg/cloud/volumes.go | 12 ++++++++++-- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/pkg/cloud/snapshots.go b/pkg/cloud/snapshots.go index 58f4a62..fae632a 100644 --- a/pkg/cloud/snapshots.go +++ b/pkg/cloud/snapshots.go @@ -34,8 +34,12 @@ func (c *client) GetSnapshotByID(ctx context.Context, snapshotID string) (*Snaps "id": snapshotID, }) - snapshot, _, err := c.Snapshot.GetSnapshotByID(snapshotID) + snapshot, count, err := c.Snapshot.GetSnapshotByID(snapshotID) if err != nil { + if count == 0 { + return nil, ErrNotFound + } + return nil, err } @@ -91,8 +95,12 @@ func (c *client) GetSnapshotByName(ctx context.Context, name string) (*Snapshot, logger.V(2).Info("CloudStack API call", "command", "GetSnapshotByName", "params", map[string]string{ "name": name, }) - snapshot, _, err := c.Snapshot.GetSnapshotByName(name) + snapshot, count, err := c.Snapshot.GetSnapshotByName(name) if err != nil { + if count == 0 { + return nil, ErrNotFound + } + return nil, err } diff --git a/pkg/cloud/vms.go b/pkg/cloud/vms.go index 52ac6be..18d5796 100644 --- a/pkg/cloud/vms.go +++ b/pkg/cloud/vms.go @@ -31,8 +31,12 @@ func (c *client) GetVMByID(ctx context.Context, vmID string) (*VM, error) { "id": vmID, }) - vm, _, err := c.VirtualMachine.GetVirtualMachineByID(vmID) + vm, count, err := c.VirtualMachine.GetVirtualMachineByID(vmID) if err != nil { + if count == 0 { + return nil, ErrNotFound + } + return nil, err } @@ -48,8 +52,12 @@ func (c *client) getVMByName(ctx context.Context, name string) (*VM, error) { "name": name, }) - vm, _, err := c.VirtualMachine.GetVirtualMachineByName(name) + vm, count, err := c.VirtualMachine.GetVirtualMachineByName(name) if err != nil { + if count == 0 { + return nil, ErrNotFound + } + return nil, err } diff --git a/pkg/cloud/volumes.go b/pkg/cloud/volumes.go index 621cd37..d9a16ea 100644 --- a/pkg/cloud/volumes.go +++ b/pkg/cloud/volumes.go @@ -51,8 +51,12 @@ func (c *client) GetVolumeByID(ctx context.Context, volumeID string) (*Volume, e "id": volumeID, }) - volume, _, err := c.Volume.GetVolumeByID(volumeID) + volume, count, err := c.Volume.GetVolumeByID(volumeID) if err != nil { + if count == 0 { + return nil, ErrNotFound + } + return nil, err } @@ -67,8 +71,12 @@ func (c *client) GetVolumeByName(ctx context.Context, name string) (*Volume, err "name": name, }) - volume, _, err := c.Volume.GetVolumeByName(name) + volume, count, err := c.Volume.GetVolumeByName(name) if err != nil { + if count == 0 { + return nil, ErrNotFound + } + return nil, err } From cb3aca6a08f5429c3c694a831a05099c9cf4d112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20Fl=C3=B6tzinger?= Date: Wed, 4 Mar 2026 09:45:55 +0100 Subject: [PATCH 10/10] Add ttl for syncer job so helm upgrade won't fail because job template is immutable --- charts/cloudstack-csi/Chart.yaml | 2 +- charts/cloudstack-csi/templates/syncer-job.yaml | 1 + charts/cloudstack-csi/values.yaml | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/charts/cloudstack-csi/Chart.yaml b/charts/cloudstack-csi/Chart.yaml index 2a633d4..c106d92 100644 --- a/charts/cloudstack-csi/Chart.yaml +++ b/charts/cloudstack-csi/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: cloudstack-csi description: A Helm chart for CloudStack CSI driver type: application -version: 3.0.1 +version: 3.0.2 appVersion: 3.0.0 sources: - https://github.com/cloudstack/cloudstack-csi-driver diff --git a/charts/cloudstack-csi/templates/syncer-job.yaml b/charts/cloudstack-csi/templates/syncer-job.yaml index 4c26ec8..9c8f1cf 100644 --- a/charts/cloudstack-csi/templates/syncer-job.yaml +++ b/charts/cloudstack-csi/templates/syncer-job.yaml @@ -14,6 +14,7 @@ metadata: {{- end }} spec: backoffLimit: {{ .Values.syncer.backoffLimit }} + ttlSecondsAfterFinished: {{ .Values.syncer.ttlSecondsAfterFinished }} template: spec: securityContext: {{- toYaml .Values.syncer.podSecurityContext | nindent 8 }} diff --git a/charts/cloudstack-csi/values.yaml b/charts/cloudstack-csi/values.yaml index 1736b9b..0ac9a1e 100644 --- a/charts/cloudstack-csi/values.yaml +++ b/charts/cloudstack-csi/values.yaml @@ -350,6 +350,7 @@ syncer: # Job configurations backoffLimit: 4 + ttlSecondsAfterFinished: 60 restartPolicy: Never # securityContext on the syncer job