@@ -30,19 +30,30 @@ import (
3030 "github.com/gophercloud/utils/v2/openstack/clientconfig"
3131
3232 "sigs.k8s.io/cluster-api-provider-openstack/pkg/metrics"
33+ openstackutil "sigs.k8s.io/cluster-api-provider-openstack/pkg/utils/openstack"
3334)
3435
3536/*
36- NovaMinimumMicroversion is the minimum Nova microversion supported by CAPO
37- 2.60 corresponds to OpenStack Queens
37+ Constants for specific microversion requirements.
38+ 2.60 corresponds to OpenStack Queens and 2.53 to OpenStack Pike,
39+ 2.38 is the maximum in OpenStack Newton.
3840
3941For the canonical description of Nova microversions, see
4042https://docs.openstack.org/nova/latest/reference/api-microversion-history.html
4143
42- CAPO uses server tags, which were added in microversion 2.52.
44+ CAPO uses server tags, which were first added in microversion 2.26 and then refined
45+ in 2.52 so it is possible to apply them when creating a server (which is what CAPO does).
46+ We round up to 2.53 here since that takes us to the maximum in Pike.
47+
4348CAPO supports multiattach volume types, which were added in microversion 2.60.
49+
50+ 2.38 was chosen as a base level since it is reasonably old, but not too old.
4451*/
45- const NovaMinimumMicroversion = "2.60"
52+ const (
53+ MinimumNovaMicroversion = "2.38"
54+ NovaTagging = "2.53"
55+ NovaMultiAttachVolume = "2.60"
56+ )
4657
4758type ComputeClient interface {
4859 ListAvailabilityZones () ([]availabilityzones.AvailabilityZone , error )
@@ -57,9 +68,14 @@ type ComputeClient interface {
5768 DeleteAttachedInterface (serverID , portID string ) error
5869
5970 ListServerGroups () ([]servergroups.ServerGroup , error )
71+ WithMicroversion (required string ) (ComputeClient , error )
6072}
6173
62- type computeClient struct { client * gophercloud.ServiceClient }
74+ type computeClient struct {
75+ client * gophercloud.ServiceClient
76+ minVersion string
77+ maxVersion string
78+ }
6379
6480// NewComputeClient returns a new compute client.
6581func NewComputeClient (providerClient * gophercloud.ProviderClient , providerClientOpts * clientconfig.ClientOpts ) (ComputeClient , error ) {
@@ -70,9 +86,25 @@ func NewComputeClient(providerClient *gophercloud.ProviderClient, providerClient
7086 if err != nil {
7187 return nil , fmt .Errorf ("failed to create compute service client: %v" , err )
7288 }
73- compute .Microversion = NovaMinimumMicroversion
7489
75- return & computeClient {compute }, nil
90+ // Find the minimum and maximum versions supported by the server
91+ serviceMin , serviceMax , err := openstackutil .GetSupportedMicroversions (* compute )
92+ if err != nil {
93+ return nil , fmt .Errorf ("unable to verify compatible server version: %w" , err )
94+ }
95+
96+ supported , err := openstackutil .MicroversionSupported (MinimumNovaMicroversion , serviceMin , serviceMax )
97+ if err != nil {
98+ return nil , fmt .Errorf ("unable to verify compatible server version: %w" , err )
99+ }
100+ if ! supported {
101+ return nil , fmt .Errorf ("no compatible server version. CAPO requires %s, but min=%s and max=%s" ,
102+ MinimumNovaMicroversion , serviceMin , serviceMax )
103+ }
104+
105+ compute .Microversion = MinimumNovaMicroversion
106+
107+ return & computeClient {client : compute , minVersion : serviceMin , maxVersion : serviceMax }, nil
76108}
77109
78110func (c computeClient ) ListAvailabilityZones () ([]availabilityzones.AvailabilityZone , error ) {
@@ -154,6 +186,21 @@ func (c computeClient) ListServerGroups() ([]servergroups.ServerGroup, error) {
154186 return servergroups .ExtractServerGroups (allPages )
155187}
156188
189+ // WithMicroversion checks that the required Nova microversion is supported and sets it for
190+ // the ComputeClient.
191+ func (c computeClient ) WithMicroversion (required string ) (ComputeClient , error ) {
192+ supported , err := openstackutil .MicroversionSupported (required , c .minVersion , c .maxVersion )
193+ if err != nil {
194+ return nil , err
195+ }
196+ if ! supported {
197+ return nil , fmt .Errorf ("microversion %s not supported. Min=%s, max=%s" , required , c .minVersion , c .maxVersion )
198+ }
199+ versionedClient := c
200+ versionedClient .client .Microversion = required
201+ return versionedClient , nil
202+ }
203+
157204type computeErrorClient struct { error }
158205
159206// NewComputeErrorClient returns a ComputeClient in which every method returns the given error.
@@ -196,3 +243,7 @@ func (e computeErrorClient) DeleteAttachedInterface(_, _ string) error {
196243func (e computeErrorClient ) ListServerGroups () ([]servergroups.ServerGroup , error ) {
197244 return nil , e .error
198245}
246+
247+ func (e computeErrorClient ) WithMicroversion (_ string ) (ComputeClient , error ) {
248+ return nil , e .error
249+ }
0 commit comments