From f9cc1699d0d2bbd6f3648a647babee0aecae8ae3 Mon Sep 17 00:00:00 2001 From: Alex Morris Date: Wed, 8 Oct 2025 15:40:11 +1300 Subject: [PATCH 1/4] Add support for setting volume size and type --- README.md | 2 ++ internal/openstackclient/openstackclient.go | 21 +++++++++-- provider.go | 40 +++++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0add6a9..112fd5b 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,8 @@ The following parameters are supported: | `boot_time` | string | Optional. Maximum wait time for instance to boot up. During that time plugin check Cloud-Init signatures. | | `use_ignition` | string | Enable Fedora CoreOS / Flatcar Linux Ignition support | | `server_spec` | object | Server spec used to create instances. See: [Compute API](https://docs.openstack.org/api-ref/compute/#create-server) | +| `volume_type` | string | Optional. Volume type name, must be used in conjunction with `volume_size` | +| `volume_size` | int | Optional. Size of disk volume in gigabytes, must be used in conjunction with `volume_type` | ### Default connector config diff --git a/internal/openstackclient/openstackclient.go b/internal/openstackclient/openstackclient.go index 5b7e53d..c1a8a6f 100644 --- a/internal/openstackclient/openstackclient.go +++ b/internal/openstackclient/openstackclient.go @@ -11,6 +11,7 @@ import ( "github.com/gophercloud/gophercloud/v2" "github.com/gophercloud/gophercloud/v2/openstack" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" "github.com/gophercloud/gophercloud/v2/openstack/config" "github.com/gophercloud/gophercloud/v2/openstack/config/clouds" "github.com/gophercloud/gophercloud/v2/openstack/image/v2/images" @@ -81,11 +82,14 @@ type Client interface { ListServers(ctx context.Context) ([]servers.Server, error) CreateServer(ctx context.Context, spec servers.CreateOptsBuilder, hintOpts servers.SchedulerHintOptsBuilder) (*servers.Server, error) DeleteServer(ctx context.Context, serverId string) error + CreateVolume(ctx context.Context, opts volumes.CreateOpts) (*volumes.Volume, error) + GetVolume(ctx context.Context, volumeId string) (*volumes.Volume, error) } type client struct { - compute *gophercloud.ServiceClient - image *gophercloud.ServiceClient + compute *gophercloud.ServiceClient + image *gophercloud.ServiceClient + blockStorage *gophercloud.ServiceClient } func New(ctx context.Context, authConfig AuthConfig, cloudOpts *CloudOpts) (Client, error) { @@ -118,10 +122,15 @@ func New(ctx context.Context, authConfig AuthConfig, cloudOpts *CloudOpts) (Clie if err != nil { return nil, err } + blockStorageClient, err := openstack.NewBlockStorageV3(providerClient, endpointOps) + if err != nil { + return nil, err + } return &client{ compute: computeClient, image: imageClient, + blockStorage: blockStorageClient, }, nil } @@ -315,3 +324,11 @@ func (c *client) CreateServer(ctx context.Context, spec servers.CreateOptsBuilde func (c *client) DeleteServer(ctx context.Context, serverId string) error { return servers.Delete(ctx, c.compute, serverId).ExtractErr() } + +func (c *client) CreateVolume(ctx context.Context, opts volumes.CreateOpts) (*volumes.Volume, error) { + return volumes.Create(ctx, c.blockStorage, opts, nil).Extract() +} + +func (c *client) GetVolume(ctx context.Context, volumeId string) (*volumes.Volume, error) { + return volumes.Get(ctx, c.blockStorage, volumeId).Extract() +} diff --git a/provider.go b/provider.go index 532b23b..33ed3ec 100644 --- a/provider.go +++ b/provider.go @@ -9,6 +9,7 @@ import ( "time" "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" + "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" "github.com/hashicorp/go-hclog" "github.com/jinzhu/copier" "github.com/sardinasystems/fleeting-plugin-openstack/internal/openstackclient" @@ -29,6 +30,8 @@ type InstanceGroup struct { UseIgnition bool `json:"use_ignition"` // Configure keys via Ignition (Fedora CoreOS / Flatcar) BootTimeS string `json:"boot_time"` // optional: wait some time before report machine as available BootTime time.Duration + VolumeType string `json:"volume_type"` + VolumeSize int `json:"volume_size"` client openstackclient.Client settings provider.Settings @@ -242,6 +245,43 @@ func (g *InstanceGroup) createInstance(ctx context.Context) (string, error) { } } + if g.VolumeSize != 0 && g.VolumeType != "" { + volumeOpts := volumes.CreateOpts{ + Name: spec.Name, + Size: g.VolumeSize, + VolumeType: g.VolumeType, + ImageID: spec.ImageRef, + } + volume, err := g.client.CreateVolume(ctx, volumeOpts) + if err != nil { + return "", err + } + + for { + volume, err := g.client.GetVolume(ctx, volume.ID) + + if err == nil && volume.Status == "available" { + break + } + select { + case <-ctx.Done(): + return "", errors.New("timeout waiting for volume to reach available status") + default: + } + time.Sleep(time.Second) + } + + spec.BlockDevice = []servers.BlockDevice{ + { + BootIndex: 0, + DeleteOnTermination: true, + DestinationType: servers.DestinationVolume, + SourceType: servers.SourceVolume, + UUID: volume.ID, + }, + } + } + if spec.ImageName != "" { imageRef, imgProps, err := g.client.GetImageByName(ctx, spec.ImageName) if err != nil { From ba38b5a542b4c7ab7990a34953a086433d0de5e3 Mon Sep 17 00:00:00 2001 From: Alex Morris Date: Thu, 9 Oct 2025 10:59:39 +1300 Subject: [PATCH 2/4] Run go fmt --- provider.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/provider.go b/provider.go index 33ed3ec..bd2b0bb 100644 --- a/provider.go +++ b/provider.go @@ -8,8 +8,8 @@ import ( "sync/atomic" "time" - "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" "github.com/gophercloud/gophercloud/v2/openstack/blockstorage/v2/volumes" + "github.com/gophercloud/gophercloud/v2/openstack/compute/v2/servers" "github.com/hashicorp/go-hclog" "github.com/jinzhu/copier" "github.com/sardinasystems/fleeting-plugin-openstack/internal/openstackclient" @@ -30,8 +30,8 @@ type InstanceGroup struct { UseIgnition bool `json:"use_ignition"` // Configure keys via Ignition (Fedora CoreOS / Flatcar) BootTimeS string `json:"boot_time"` // optional: wait some time before report machine as available BootTime time.Duration - VolumeType string `json:"volume_type"` - VolumeSize int `json:"volume_size"` + VolumeType string `json:"volume_type"` + VolumeSize int `json:"volume_size"` client openstackclient.Client settings provider.Settings @@ -247,10 +247,10 @@ func (g *InstanceGroup) createInstance(ctx context.Context) (string, error) { if g.VolumeSize != 0 && g.VolumeType != "" { volumeOpts := volumes.CreateOpts{ - Name: spec.Name, - Size: g.VolumeSize, + Name: spec.Name, + Size: g.VolumeSize, VolumeType: g.VolumeType, - ImageID: spec.ImageRef, + ImageID: spec.ImageRef, } volume, err := g.client.CreateVolume(ctx, volumeOpts) if err != nil { @@ -273,11 +273,11 @@ func (g *InstanceGroup) createInstance(ctx context.Context) (string, error) { spec.BlockDevice = []servers.BlockDevice{ { - BootIndex: 0, + BootIndex: 0, DeleteOnTermination: true, - DestinationType: servers.DestinationVolume, - SourceType: servers.SourceVolume, - UUID: volume.ID, + DestinationType: servers.DestinationVolume, + SourceType: servers.SourceVolume, + UUID: volume.ID, }, } } From 5bc955eeedbbb12f2d560c0ee889e71046bf7d6a Mon Sep 17 00:00:00 2001 From: Alex Morris Date: Thu, 9 Oct 2025 11:05:18 +1300 Subject: [PATCH 3/4] use WaitForStatus() --- internal/openstackclient/openstackclient.go | 5 +++++ provider.go | 15 +++------------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/internal/openstackclient/openstackclient.go b/internal/openstackclient/openstackclient.go index c1a8a6f..d59b94e 100644 --- a/internal/openstackclient/openstackclient.go +++ b/internal/openstackclient/openstackclient.go @@ -84,6 +84,7 @@ type Client interface { DeleteServer(ctx context.Context, serverId string) error CreateVolume(ctx context.Context, opts volumes.CreateOpts) (*volumes.Volume, error) GetVolume(ctx context.Context, volumeId string) (*volumes.Volume, error) + WaitForStatus(ctx context.Context, volumeId string, status string) error } type client struct { @@ -332,3 +333,7 @@ func (c *client) CreateVolume(ctx context.Context, opts volumes.CreateOpts) (*vo func (c *client) GetVolume(ctx context.Context, volumeId string) (*volumes.Volume, error) { return volumes.Get(ctx, c.blockStorage, volumeId).Extract() } + +func (c *client) WaitForStatus(ctx context.Context, volumeId string, status string) error { + return volumes.WaitForStatus(ctx, c.blockStorage, volumeId, status) +} diff --git a/provider.go b/provider.go index bd2b0bb..53b4d02 100644 --- a/provider.go +++ b/provider.go @@ -257,18 +257,9 @@ func (g *InstanceGroup) createInstance(ctx context.Context) (string, error) { return "", err } - for { - volume, err := g.client.GetVolume(ctx, volume.ID) - - if err == nil && volume.Status == "available" { - break - } - select { - case <-ctx.Done(): - return "", errors.New("timeout waiting for volume to reach available status") - default: - } - time.Sleep(time.Second) + err = g.client.WaitForStatus(ctx, volume.ID, "available") + if err != nil { + return "", err } spec.BlockDevice = []servers.BlockDevice{ From 88233570d3c29e375e2177b15c6e4da591b2b54a Mon Sep 17 00:00:00 2001 From: Alex Morris Date: Fri, 10 Oct 2025 10:58:33 +1300 Subject: [PATCH 4/4] Rename func to WaitForVolumeStatus --- internal/openstackclient/openstackclient.go | 4 ++-- provider.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/openstackclient/openstackclient.go b/internal/openstackclient/openstackclient.go index d59b94e..ea78b6f 100644 --- a/internal/openstackclient/openstackclient.go +++ b/internal/openstackclient/openstackclient.go @@ -84,7 +84,7 @@ type Client interface { DeleteServer(ctx context.Context, serverId string) error CreateVolume(ctx context.Context, opts volumes.CreateOpts) (*volumes.Volume, error) GetVolume(ctx context.Context, volumeId string) (*volumes.Volume, error) - WaitForStatus(ctx context.Context, volumeId string, status string) error + WaitForVolumeStatus(ctx context.Context, volumeId string, status string) error } type client struct { @@ -334,6 +334,6 @@ func (c *client) GetVolume(ctx context.Context, volumeId string) (*volumes.Volum return volumes.Get(ctx, c.blockStorage, volumeId).Extract() } -func (c *client) WaitForStatus(ctx context.Context, volumeId string, status string) error { +func (c *client) WaitForVolumeStatus(ctx context.Context, volumeId string, status string) error { return volumes.WaitForStatus(ctx, c.blockStorage, volumeId, status) } diff --git a/provider.go b/provider.go index 53b4d02..cfb9901 100644 --- a/provider.go +++ b/provider.go @@ -257,7 +257,7 @@ func (g *InstanceGroup) createInstance(ctx context.Context) (string, error) { return "", err } - err = g.client.WaitForStatus(ctx, volume.ID, "available") + err = g.client.WaitForVolumeStatus(ctx, volume.ID, "available") if err != nil { return "", err }