Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions pkg/csi/manila/manilaclient/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,12 @@ func New(ctx context.Context, o *client.AuthOpts, userAgent string, extraUserAge
// Check client's and server's versions for compatibility

client.Microversion = minimumManilaVersion
if err = validateManilaClient(ctx, client); err != nil {
serverMaxMicroversion, err := validateManilaClient(ctx, client)
if err != nil {
return nil, fmt.Errorf("manila v2 client validation failed: %v", err)
}

return &Client{c: client}, nil
return &Client{c: client, serverMaxMicroversion: serverMaxMicroversion}, nil
}

func splitManilaMicroversion(microversion string) (major, minor int) {
Expand Down Expand Up @@ -106,27 +107,27 @@ func compareManilaVersionsLessThan(a, b string) bool {
return aMaj < bMaj || (aMaj == bMaj && aMin < bMin)
}

func validateManilaClient(ctx context.Context, c *gophercloud.ServiceClient) error {
func validateManilaClient(ctx context.Context, c *gophercloud.ServiceClient) (string, error) {
serverVersion, err := apiversions.Get(ctx, c, "v2").Extract()
if err != nil {
return fmt.Errorf("failed to get Manila v2 API microversions: %v", err)
return "", fmt.Errorf("failed to get Manila v2 API microversions: %v", err)
}

if err = validateManilaMicroversion(serverVersion.MinVersion); err != nil {
return fmt.Errorf("server's minimum microversion is invalid: %v", err)
return "", fmt.Errorf("server's minimum microversion is invalid: %v", err)
}

if err = validateManilaMicroversion(serverVersion.Version); err != nil {
return fmt.Errorf("server's maximum microversion is invalid: %v", err)
return "", fmt.Errorf("server's maximum microversion is invalid: %v", err)
}

if compareManilaVersionsLessThan(c.Microversion, serverVersion.MinVersion) {
return fmt.Errorf("client's microversion %s is lower than server's minimum microversion %s", c.Microversion, serverVersion.MinVersion)
return "", fmt.Errorf("client's microversion %s is lower than server's minimum microversion %s", c.Microversion, serverVersion.MinVersion)
}

if compareManilaVersionsLessThan(serverVersion.Version, c.Microversion) {
return fmt.Errorf("client's microversion %s is higher than server's highest supported microversion %s", c.Microversion, serverVersion.Version)
return "", fmt.Errorf("client's microversion %s is higher than server's highest supported microversion %s", c.Microversion, serverVersion.Version)
}

return nil
return serverVersion.Version, nil
}
42 changes: 41 additions & 1 deletion pkg/csi/manila/manilaclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/gophercloud/gophercloud/v2"
"github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/messages"
"github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/shareaccessrules"
"github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/shares"
"github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/sharetypes"
"github.com/gophercloud/gophercloud/v2/openstack/sharedfilesystems/v2/snapshots"
Expand All @@ -29,8 +30,13 @@ import (
snapshots_utils "github.com/gophercloud/utils/v2/openstack/sharedfilesystems/v2/snapshots"
)

const (
accessRulesGETMicroversion = "2.45"
)

type Client struct {
c *gophercloud.ServiceClient
c *gophercloud.ServiceClient
serverMaxMicroversion string
}

func (c Client) GetMicroversion() string {
Expand Down Expand Up @@ -75,9 +81,43 @@ func (c Client) SetShareMetadata(ctx context.Context, shareID string, opts share
}

func (c Client) GetAccessRights(ctx context.Context, shareID string) ([]shares.AccessRight, error) {
if !compareManilaVersionsLessThan(c.serverMaxMicroversion, accessRulesGETMicroversion) {
return c.getAccessRightsV2(ctx, shareID)
}
return shares.ListAccessRights(ctx, c.c, shareID).Extract()
}

// getAccessRightsV2 uses the GET-based share-access-rules API (microversion 2.45+)
// instead of the deprecated os-access_list POST action.
func (c Client) getAccessRightsV2(ctx context.Context, shareID string) ([]shares.AccessRight, error) {
origMicroversion := c.c.Microversion
c.c.Microversion = accessRulesGETMicroversion
defer func() { c.c.Microversion = origMicroversion }()

accessList, err := shareaccessrules.List(ctx, c.c, shareID).Extract()
if err != nil {
return nil, err
}

rights := make([]shares.AccessRight, len(accessList))
for i, a := range accessList {
rights[i] = shares.AccessRight{
ID: a.ID,
ShareID: a.ShareID,
AccessType: a.AccessType,
AccessTo: a.AccessTo,
AccessKey: a.AccessKey,
AccessLevel: a.AccessLevel,
State: a.State,
}
}
return rights, nil
}

func (c Client) GetServerMaxMicroversion() string {
return c.serverMaxMicroversion
}

func (c Client) GrantAccess(ctx context.Context, shareID string, opts shares.GrantAccessOptsBuilder) (*shares.AccessRight, error) {
return shares.GrantAccess(ctx, c.c, shareID, opts).Extract()
}
Expand Down
59 changes: 59 additions & 0 deletions pkg/csi/manila/manilaclient/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
Copyright 2026 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package manilaclient

import (
"testing"
)

func TestAccessRulesVersionGating(t *testing.T) {
tests := []struct {
name string
serverMaxMicroversion string
wantNewAPI bool
}{
{
name: "server supports 2.45 exactly",
serverMaxMicroversion: "2.45",
wantNewAPI: true,
},
{
name: "server supports higher than 2.45",
serverMaxMicroversion: "2.94",
wantNewAPI: true,
},
{
name: "server only supports 2.44",
serverMaxMicroversion: "2.44",
wantNewAPI: false,
},
{
name: "server only supports 2.37",
serverMaxMicroversion: "2.37",
wantNewAPI: false,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
useNewAPI := !compareManilaVersionsLessThan(tc.serverMaxMicroversion, accessRulesGETMicroversion)
if useNewAPI != tc.wantNewAPI {
t.Errorf("server %s: expected new API = %v, got %v", tc.serverMaxMicroversion, tc.wantNewAPI, useNewAPI)
}
})
}
}
1 change: 1 addition & 0 deletions pkg/csi/manila/manilaclient/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
type Interface interface {
GetMicroversion() string
SetMicroversion(version string)
GetServerMaxMicroversion() string

GetShareByID(ctx context.Context, shareID string) (*shares.Share, error)
GetShareByName(ctx context.Context, shareName string) (*shares.Share, error)
Expand Down
4 changes: 4 additions & 0 deletions tests/sanity/manila/fakemanilaclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ func (c fakeManilaClient) GetMicroversion() string {
func (c fakeManilaClient) SetMicroversion(_ string) {
}

func (c fakeManilaClient) GetServerMaxMicroversion() string {
return "2.94"
}

func (c fakeManilaClient) GetShareByID(_ context.Context, shareID string) (*shares.Share, error) {
s, ok := fakeShares[strToInt(shareID)]
if !ok {
Expand Down