Skip to content

Commit ad897c8

Browse files
committed
Add microversion utilities
This adds GetSupportedMicroversions, MicroversionSupported, RequireMicroversion and ParseMicroversion utility functions for working with microversions. The doc string for ChooseVersion is also updated to clarify how it works.
1 parent 32dbd09 commit ad897c8

2 files changed

Lines changed: 432 additions & 3 deletions

File tree

openstack/utils/choose_version.go

Lines changed: 127 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package utils
22

33
import (
44
"fmt"
5+
"strconv"
56
"strings"
67

78
"github.com/gophercloud/gophercloud"
@@ -20,9 +21,12 @@ var goodStatus = map[string]bool{
2021
"stable": true,
2122
}
2223

23-
// ChooseVersion queries the base endpoint of an API to choose the most recent non-experimental alternative from a service's
24-
// published versions.
25-
// It returns the highest-Priority Version among the alternatives that are provided, as well as its corresponding endpoint.
24+
// ChooseVersion queries the base endpoint of an API to choose the identity service version.
25+
// It will pick a version among the recognized, taking into account the priority and avoiding
26+
// experimental alternatives from the published versions. However, if the client specifies a full
27+
// endpoint that is among the recognized versions, it will be used regardless of priority.
28+
// It returns the highest-Priority Version, OR exact match with client endpoint,
29+
// among the alternatives that are provided, as well as its corresponding endpoint.
2630
func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (*Version, string, error) {
2731
type linkResp struct {
2832
Href string `json:"href"`
@@ -109,3 +113,123 @@ func ChooseVersion(client *gophercloud.ProviderClient, recognized []*Version) (*
109113

110114
return highest, endpoint, nil
111115
}
116+
117+
type SupportedMicroversions struct {
118+
MaxMajor int
119+
MaxMinor int
120+
MinMajor int
121+
MinMinor int
122+
}
123+
124+
// GetSupportedMicroversions returns the minimum and maximum microversion that is supported by the ServiceClient Endpoint.
125+
func GetSupportedMicroversions(client *gophercloud.ServiceClient) (SupportedMicroversions, error) {
126+
type valueResp struct {
127+
ID string `json:"id"`
128+
Status string `json:"status"`
129+
Version string `json:"version"`
130+
MinVersion string `json:"min_version"`
131+
}
132+
133+
type response struct {
134+
Version valueResp `json:"version"`
135+
Versions []valueResp `json:"versions"`
136+
}
137+
var minVersion, maxVersion string
138+
var supportedMicroversions SupportedMicroversions
139+
var resp response
140+
_, err := client.Get(client.Endpoint, &resp, &gophercloud.RequestOpts{
141+
OkCodes: []int{200, 300},
142+
})
143+
144+
if err != nil {
145+
return supportedMicroversions, err
146+
}
147+
148+
if len(resp.Versions) > 0 {
149+
// We are dealing with an unversioned endpoint
150+
// We only handle the case when there is exactly one, and assume it is the correct one
151+
if len(resp.Versions) > 1 {
152+
return supportedMicroversions, fmt.Errorf("unversioned endpoint with multiple alternatives not supported")
153+
}
154+
minVersion = resp.Versions[0].MinVersion
155+
maxVersion = resp.Versions[0].Version
156+
} else {
157+
minVersion = resp.Version.MinVersion
158+
maxVersion = resp.Version.Version
159+
}
160+
161+
// Return early if the endpoint does not support microversions
162+
if minVersion == "" && maxVersion == "" {
163+
return supportedMicroversions, fmt.Errorf("microversions not supported by ServiceClient Endpoint")
164+
}
165+
166+
supportedMicroversions.MinMajor, supportedMicroversions.MinMinor, err = ParseMicroversion(minVersion)
167+
if err != nil {
168+
return supportedMicroversions, err
169+
}
170+
171+
supportedMicroversions.MaxMajor, supportedMicroversions.MaxMinor, err = ParseMicroversion(maxVersion)
172+
if err != nil {
173+
return supportedMicroversions, err
174+
}
175+
176+
return supportedMicroversions, nil
177+
}
178+
179+
// RequireMicroversion checks that the required microversion is supported and
180+
// returns a ServiceClient with the microversion set.
181+
func RequireMicroversion(client gophercloud.ServiceClient, required string) (gophercloud.ServiceClient, error) {
182+
supportedMicroversions, err := GetSupportedMicroversions(&client)
183+
if err != nil {
184+
return client, fmt.Errorf("unable to determine supported microversions: %w", err)
185+
}
186+
supported, err := supportedMicroversions.IsSupported(required)
187+
if err != nil {
188+
return client, err
189+
}
190+
if !supported {
191+
return client, fmt.Errorf("microversion %s not supported. Supported versions: %v", required, supportedMicroversions)
192+
}
193+
client.Microversion = required
194+
return client, nil
195+
}
196+
197+
// IsSupported checks if a microversion falls in the supported interval.
198+
// It returns true if the version is within the interval and false otherwise.
199+
func (supported SupportedMicroversions) IsSupported(version string) (bool, error) {
200+
// Parse the version X.Y into X and Y integers that are easier to compare.
201+
vMajor, vMinor, err := ParseMicroversion(version)
202+
if err != nil {
203+
return false, err
204+
}
205+
206+
// Check that the major version number is supported.
207+
if (vMajor < supported.MinMajor) || (vMajor > supported.MaxMajor) {
208+
return false, nil
209+
}
210+
211+
// Check that the minor version number is supported
212+
if (vMinor <= supported.MaxMinor) && (vMinor >= supported.MinMinor) {
213+
return true, nil
214+
}
215+
216+
return false, nil
217+
}
218+
219+
// ParseMicroversion parses the version major.minor into separate integers major and minor.
220+
// For example, "2.53" becomes 2 and 53.
221+
func ParseMicroversion(version string) (major int, minor int, err error) {
222+
parts := strings.Split(version, ".")
223+
if len(parts) != 2 {
224+
return 0, 0, fmt.Errorf("invalid microversion format: %q", version)
225+
}
226+
major, err = strconv.Atoi(parts[0])
227+
if err != nil {
228+
return 0, 0, err
229+
}
230+
minor, err = strconv.Atoi(parts[1])
231+
if err != nil {
232+
return 0, 0, err
233+
}
234+
return major, minor, nil
235+
}

0 commit comments

Comments
 (0)