Skip to content

Commit 250b868

Browse files
author
dkokkinos
authored
[manila-csi-plugin] Add support for authentication via clouds.yaml (#2883)
* handle boolean values correctly when reading from secrets Previously, the validator failed when attempting to parse boolean fields like `UseClouds` in `AuthOpts` from secrets. This was due to secrets being stored as strings, leading to a type mismatch. Added logic to correctly parse string representations of booleans to match the expected type in the struct. * Implement clouds.yaml support and make auth params optional Enables reading credentials/config from clouds.yaml when UseClouds is set. Mark Region and AuthURL as optional.
1 parent 259ed77 commit 250b868

File tree

4 files changed

+93
-6
lines changed

4 files changed

+93
-6
lines changed

pkg/client/client.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import (
3737
)
3838

3939
type AuthOpts struct {
40-
AuthURL string `gcfg:"auth-url" mapstructure:"auth-url" name:"os-authURL" dependsOn:"os-password|os-trustID|os-applicationCredentialSecret|os-clientCertPath"`
40+
AuthURL string `gcfg:"auth-url" mapstructure:"auth-url" name:"os-authURL" value:"optional" dependsOn:"os-password|os-trustID|os-applicationCredentialSecret|os-clientCertPath"`
4141
UserID string `gcfg:"user-id" mapstructure:"user-id" name:"os-userID" value:"optional" dependsOn:"os-password"`
4242
Username string `name:"os-userName" value:"optional" dependsOn:"os-password"`
4343
Password string `name:"os-password" value:"optional" dependsOn:"os-domainID|os-domainName,os-projectID|os-projectName,os-userID|os-userName"`
@@ -52,7 +52,7 @@ type AuthOpts struct {
5252
TenantDomainName string `gcfg:"tenant-domain-name" mapstructure:"project-domain-name" name:"os-projectDomainName" value:"optional"`
5353
UserDomainID string `gcfg:"user-domain-id" mapstructure:"user-domain-id" name:"os-userDomainID" value:"optional"`
5454
UserDomainName string `gcfg:"user-domain-name" mapstructure:"user-domain-name" name:"os-userDomainName" value:"optional"`
55-
Region string `name:"os-region"`
55+
Region string `name:"os-region" value:"optional" dependsOn:"os-password|os-applicationCredentialSecret|os-trusteePassword"`
5656
EndpointType gophercloud.Availability `gcfg:"os-endpoint-type" mapstructure:"os-endpoint-type" name:"os-endpointType" value:"optional"`
5757
CAFile string `gcfg:"ca-file" mapstructure:"ca-file" name:"os-certAuthorityPath" value:"optional"`
5858
TLSInsecure string `gcfg:"tls-insecure" mapstructure:"tls-insecure" name:"os-TLSInsecure" value:"optional" matches:"^true|false$"`

pkg/csi/manila/manilaclient/builder.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ func (cb *ClientBuilder) New(ctx context.Context, o *client.AuthOpts) (Interface
4848

4949
func New(ctx context.Context, o *client.AuthOpts, userAgent string, extraUserAgentData []string) (*Client, error) {
5050
// Authenticate and create Manila v2 client
51+
// If UseClouds is set, read clouds.yaml file
52+
if o.UseClouds {
53+
err := client.ReadClouds(o)
54+
if err != nil {
55+
return nil, fmt.Errorf("failed to read clouds.yaml: %v", err)
56+
}
57+
}
58+
5159
provider, err := client.NewOpenStackClient(o, userAgent, extraUserAgentData...)
5260
if err != nil {
5361
return nil, fmt.Errorf("failed to authenticate: %v", err)

pkg/csi/manila/validator/validator.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package validator
1919
import (
2020
"fmt"
2121
"reflect"
22+
"strconv"
2223
)
2324

2425
// Validator validates input data and stores it in the output struct.
@@ -113,10 +114,24 @@ func (v *Validator) Populate(data map[string]string, out interface{}) error {
113114
return fmt.Errorf("parameter '%s' cannot be empty", fName)
114115
}
115116

116-
// The value is present, populate the field and continue with the next one
117+
field := vOut.Field(int(fIdx))
117118

118-
vOut.Field(int(fIdx)).SetString(value)
119-
continue
119+
switch field.Kind() {
120+
case reflect.Bool:
121+
boolValue, err := strconv.ParseBool(value)
122+
if err != nil {
123+
return fmt.Errorf("invalid boolean value for parameter '%s': %v", fName, err)
124+
}
125+
field.SetBool(boolValue)
126+
continue
127+
128+
case reflect.String:
129+
field.SetString(value)
130+
continue
131+
132+
default:
133+
return fmt.Errorf("unsupported field type for parameter '%s'", fName)
134+
}
120135
}
121136

122137
// Value not present, determine whether this field is required

pkg/csi/manila/validator/validator_test.go

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ limitations under the License.
1616

1717
package validator
1818

19-
import "testing"
19+
import (
20+
"testing"
21+
22+
"github.com/gophercloud/gophercloud/v2"
23+
)
2024

2125
func TestValueRequired(t *testing.T) {
2226
type s1 struct {
@@ -224,3 +228,63 @@ func TestFieldNames(t *testing.T) {
224228
}
225229
}
226230
}
231+
232+
func TestBooleanField(t *testing.T) {
233+
type s struct {
234+
A bool `name:"a"`
235+
}
236+
237+
v := New(&s{})
238+
239+
// Test with a valid boolean string "true"
240+
obj := &s{}
241+
err := v.Populate(map[string]string{"a": "true"}, obj)
242+
if err != nil {
243+
t.Errorf("expected no error, got: %v", err)
244+
}
245+
if obj.A != true {
246+
t.Errorf("expected A to be true, got: %v", obj.A)
247+
}
248+
249+
// Test with a valid boolean string "false"
250+
obj = &s{}
251+
err = v.Populate(map[string]string{"a": "false"}, obj)
252+
if err != nil {
253+
t.Errorf("expected no error, got: %v", err)
254+
}
255+
if obj.A != false {
256+
t.Errorf("expected A to be false, got: %v", obj.A)
257+
}
258+
259+
// Test with a invalid boolean string "foo"
260+
obj = &s{}
261+
err = v.Populate(map[string]string{"a": "foo"}, obj)
262+
if err == nil {
263+
t.Errorf("Populate with 'foo': expected an error, but got nil")
264+
}
265+
}
266+
267+
func TestAvailabilityField(t *testing.T) {
268+
type s struct {
269+
Avail gophercloud.Availability `name:"avail"`
270+
}
271+
272+
v := New(&s{})
273+
274+
// Test with a valid availability string
275+
obj := &s{}
276+
err := v.Populate(map[string]string{"avail": "public"}, obj)
277+
if err != nil {
278+
t.Errorf("expected no error, got: %v", err)
279+
}
280+
if obj.Avail != gophercloud.Availability("public") {
281+
t.Errorf("expected Avail to be 'public', got: %v", obj.Avail)
282+
}
283+
284+
// Test with empty value
285+
obj = &s{}
286+
err = v.Populate(map[string]string{"avail": ""}, obj)
287+
if err == nil {
288+
t.Errorf("expected error for empty value, got nil")
289+
}
290+
}

0 commit comments

Comments
 (0)