Skip to content

Commit

Permalink
WIP: Flexible Nova Microversion
Browse files Browse the repository at this point in the history
Set the microversion using a separate method and use integer maths
instead of strings and floats when comparing versions.
  • Loading branch information
lentzi90 committed Jun 14, 2023
1 parent 213708f commit cd74f5a
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 15 deletions.
52 changes: 48 additions & 4 deletions pkg/clients/compute.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,19 @@ CAPO uses server tags, which were first added in microversion 2.26 and then refi
in 2.52 so it is possible to apply them when creating a server (which is what CAPO does).
CAPO supports multiattach volume types, which were added in microversion 2.60.
*/
var NovaSupportedVersions = []*utils.Version{
{ID: "2.1", Priority: 10, Suffix: "/v2.1/"}, // Initial microversion release, same as specifying no version
{ID: "2.53", Priority: 20, Suffix: "/v2.53/"}, // Maximum in Pike
{ID: "2.60", Priority: 30, Suffix: "/v2.60/"}, // Maximum in Queens
var NovaSupportedVersions = map[int]*utils.Version{
38: {ID: "2.38", Priority: 10, Suffix: "/v2.38/"}, // Maximum in Newton
53: {ID: "2.53", Priority: 20, Suffix: "/v2.53/"}, // Maximum in Pike
60: {ID: "2.60", Priority: 30, Suffix: "/v2.60/"}, // Maximum in Queens
}

// Constants for specific microversion requirement.
// The values correspond to the decimal part of the Nova version for easy comparisons.
// For example, we require 2.53 for tagging support, so NovaTagging is set to 53.
const (
NovaTagging = 53
)

// ServerExt is the base gophercloud Server with extensions used by InstanceStatus.
type ServerExt struct {
servers.Server
Expand All @@ -66,6 +73,7 @@ type ComputeClient interface {

ListAttachedInterfaces(serverID string) ([]attachinterfaces.Interface, error)
DeleteAttachedInterface(serverID, portID string) error
RequireMicroversion(decimal int) error
}

type computeClient struct{ client *gophercloud.ServiceClient }
Expand All @@ -79,6 +87,18 @@ func NewComputeClient(providerClient *gophercloud.ProviderClient, providerClient
return nil, fmt.Errorf("failed to create compute service client: %v", err)
}

// Make an initial version negotiation.
// The caller can redo this later with more restrictions via RequireMicroversion.
supportedVersions := []*utils.Version{}
for k := range NovaSupportedVersions {
supportedVersions = append(supportedVersions, NovaSupportedVersions[k])
}
version, _, err := utils.ChooseVersion(providerClient, supportedVersions)
if err != nil {
return nil, fmt.Errorf("failed to negotiation compute service version: %w", err)
}
compute.Microversion = version.ID

return &computeClient{compute}, nil
}

Expand Down Expand Up @@ -153,6 +173,26 @@ func (c computeClient) DeleteAttachedInterface(serverID, portID string) error {
return mc.ObserveRequestIgnoreNotFoundorConflict(err)
}

// RequireMicroversion negotiates the Nova microversion for the ComputeClient while taking into account
// the version requirement given. The microversion will be set to minimum `2.requiredDecimal`,
// or an error will be returned.
func (c computeClient) RequireMicroversion(requiredDecimal int) error {
supportedMicroversions := []*utils.Version{}
for decimal := range NovaSupportedVersions {
if decimal < requiredDecimal {
continue
}
supportedMicroversions = append(supportedMicroversions, NovaSupportedVersions[decimal])
}
chosen, _, err := utils.ChooseVersion(c.client.ProviderClient, supportedMicroversions)
if err != nil {
return fmt.Errorf("failed to negotiate compute client version: %v", err)
}
fmt.Println("TODO: Add proper logging. Chose microversion %s", chosen.ID)
c.client.Microversion = chosen.ID
return nil
}

type computeErrorClient struct{ error }

// NewComputeErrorClient returns a ComputeClient in which every method returns the given error.
Expand Down Expand Up @@ -191,3 +231,7 @@ func (e computeErrorClient) ListAttachedInterfaces(_ string) ([]attachinterfaces
func (e computeErrorClient) DeleteAttachedInterface(_, _ string) error {
return e.error
}

func (e computeErrorClient) RequireMicroversion(_ int) error {
return e.error
}
10 changes: 10 additions & 0 deletions pkg/clients/mock/compute.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 5 additions & 11 deletions pkg/cloud/services/compute/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import (
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
"github.com/gophercloud/gophercloud/openstack/utils"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/cluster-api/util"

Expand Down Expand Up @@ -346,19 +345,14 @@ func (s *Service) createInstanceImpl(eventObject runtime.Object, openStackCluste

serverCreateOpts = applyServerGroupID(serverCreateOpts, instanceSpec.ServerGroupID)

// Filter supported versions based on features used
recognizedMicroversions := clients.NovaSupportedVersions
supportedMicroversions := []*utils.Version{}
compute := s.getComputeClient()
if len(instanceSpec.Tags) > 0 {
for i := range recognizedMicroversions {
if recognizedMicroversions[i].ID == "2.1" {
continue
}
supportedMicroversions = append(supportedMicroversions, recognizedMicroversions[i])
err = compute.RequireMicroversion(clients.NovaTagging)
if err != nil {
return nil, fmt.Errorf("tagging is not supported by the server: %w", err)
}
}

server, err = s.getComputeClient().CreateServer(keypairs.CreateOptsExt{
server, err = compute.CreateServer(keypairs.CreateOptsExt{
CreateOptsBuilder: serverCreateOpts,
KeyName: instanceSpec.SSHKeyName,
})
Expand Down

0 comments on commit cd74f5a

Please sign in to comment.