@@ -21,6 +21,7 @@ import (
2121 "crypto/tls"
2222 "crypto/x509"
2323 "fmt"
24+ "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/trusts"
2425 "net/http"
2526
2627 "github.com/gophercloud/gophercloud"
@@ -37,13 +38,58 @@ import (
3738 infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha5"
3839)
3940
41+ type NewAuthInfo struct {
42+ clientconfig.AuthInfo
43+ TrustID string `yaml:"trust_id,omitempty" json:"trust_id,omitempty"`
44+ }
45+
46+ // NewCloud represents an entry in a clouds.yaml/public-clouds.yaml/secure.yaml file.
47+ type NewCloud struct {
48+ Cloud string `yaml:"cloud,omitempty" json:"cloud,omitempty"`
49+ Profile string `yaml:"profile,omitempty" json:"profile,omitempty"`
50+ AuthInfo * NewAuthInfo `yaml:"auth,omitempty" json:"auth,omitempty"`
51+ AuthType clientconfig.AuthType `yaml:"auth_type,omitempty" json:"auth_type,omitempty"`
52+ RegionName string `yaml:"region_name,omitempty" json:"region_name,omitempty"`
53+ Regions []clientconfig.Region `yaml:"regions,omitempty" json:"regions,omitempty"`
54+
55+ // EndpointType and Interface both specify whether to use the public, internal,
56+ // or admin interface of a service. They should be considered synonymous, but
57+ // EndpointType will take precedence when both are specified.
58+ EndpointType string `yaml:"endpoint_type,omitempty" json:"endpoint_type,omitempty"`
59+ Interface string `yaml:"interface,omitempty" json:"interface,omitempty"`
60+
61+ // API Version overrides.
62+ IdentityAPIVersion string `yaml:"identity_api_version,omitempty" json:"identity_api_version,omitempty"`
63+ VolumeAPIVersion string `yaml:"volume_api_version,omitempty" json:"volume_api_version,omitempty"`
64+
65+ // Verify whether or not SSL API requests should be verified.
66+ Verify * bool `yaml:"verify,omitempty" json:"verify,omitempty"`
67+
68+ // CACertFile a path to a CA Cert bundle that can be used as part of
69+ // verifying SSL API requests.
70+ CACertFile string `yaml:"cacert,omitempty" json:"cacert,omitempty"`
71+
72+ // ClientCertFile a path to a client certificate to use as part of the SSL
73+ // transaction.
74+ ClientCertFile string `yaml:"cert,omitempty" json:"cert,omitempty"`
75+
76+ // ClientKeyFile a path to a client key to use as part of the SSL
77+ // transaction.
78+ ClientKeyFile string `yaml:"key,omitempty" json:"key,omitempty"`
79+ }
80+
81+ type NewClouds struct {
82+ Clouds map [string ]NewCloud `yaml:"clouds" json:"clouds"`
83+ }
84+
4085const (
4186 cloudsSecretKey = "clouds.yaml"
4287 caSecretKey = "cacert"
4388)
4489
90+
4591func NewClientFromMachine (ctx context.Context , ctrlClient client.Client , openStackMachine * infrav1.OpenStackMachine ) (* gophercloud.ProviderClient , * clientconfig.ClientOpts , string , error ) {
46- var cloud clientconfig. Cloud
92+ var cloud NewCloud
4793 var caCert []byte
4894
4995 if openStackMachine .Spec .IdentityRef != nil {
@@ -57,7 +103,7 @@ func NewClientFromMachine(ctx context.Context, ctrlClient client.Client, openSta
57103}
58104
59105func NewClientFromCluster (ctx context.Context , ctrlClient client.Client , openStackCluster * infrav1.OpenStackCluster ) (* gophercloud.ProviderClient , * clientconfig.ClientOpts , string , error ) {
60- var cloud clientconfig. Cloud
106+ var cloud NewCloud
61107 var caCert []byte
62108
63109 if openStackCluster .Spec .IdentityRef != nil {
@@ -70,10 +116,10 @@ func NewClientFromCluster(ctx context.Context, ctrlClient client.Client, openSta
70116 return NewClient (cloud , caCert )
71117}
72118
73- func NewClient (cloud clientconfig. Cloud , caCert []byte ) (* gophercloud.ProviderClient , * clientconfig.ClientOpts , string , error ) {
119+ func NewClient (cloud NewCloud , caCert []byte ) (* gophercloud.ProviderClient , * clientconfig.ClientOpts , string , error ) {
74120 clientOpts := new (clientconfig.ClientOpts )
75121 if cloud .AuthInfo != nil {
76- clientOpts .AuthInfo = cloud .AuthInfo
122+ clientOpts .AuthInfo = & cloud . AuthInfo .AuthInfo
77123 clientOpts .AuthType = cloud .AuthType
78124 clientOpts .RegionName = cloud .RegionName
79125 }
@@ -84,11 +130,11 @@ func NewClient(cloud clientconfig.Cloud, caCert []byte) (*gophercloud.ProviderCl
84130 }
85131 opts .AllowReauth = true
86132
133+
87134 provider , err := openstack .NewClient (opts .IdentityEndpoint )
88135 if err != nil {
89136 return nil , nil , "" , fmt .Errorf ("create providerClient err: %v" , err )
90137 }
91-
92138 config := & tls.Config {
93139 RootCAs : x509 .NewCertPool (),
94140 MinVersion : tls .VersionTLS12 ,
@@ -101,17 +147,46 @@ func NewClient(cloud clientconfig.Cloud, caCert []byte) (*gophercloud.ProviderCl
101147 }
102148
103149 provider .HTTPClient .Transport = & http.Transport {Proxy : http .ProxyFromEnvironment , TLSClientConfig : config }
104- if klog .V (6 ).Enabled () {
105- provider .HTTPClient .Transport = & osclient.RoundTripper {
106- Rt : provider .HTTPClient .Transport ,
107- Logger : & defaultLogger {},
150+ provider .HTTPClient .Transport = & osclient.RoundTripper {
151+ Rt : provider .HTTPClient .Transport ,
152+ Logger : & defaultLogger {},
153+ }
154+ if cloud .AuthInfo .TrustID != "" {
155+ tokenauth := tokens.AuthOptions {}
156+ tokenauth .IdentityEndpoint = opts .IdentityEndpoint
157+ tokenauth .UserID = opts .UserID
158+ tokenauth .Username = opts .Username
159+ tokenauth .Password = opts .Password
160+ tokenauth .DomainID = opts .DomainID
161+ tokenauth .DomainName = opts .DomainName
162+ tokenauth .ApplicationCredentialID = opts .ApplicationCredentialID
163+ tokenauth .ApplicationCredentialName = opts .ApplicationCredentialName
164+ tokenauth .ApplicationCredentialSecret = opts .ApplicationCredentialSecret
165+ tokenauth .AllowReauth = opts .AllowReauth
166+ if opts .Scope != nil {
167+ tokenauth .Scope .ProjectID = opts .Scope .ProjectID
168+ tokenauth .Scope .ProjectName = opts .Scope .ProjectName
169+ tokenauth .Scope .DomainName = opts .Scope .DomainName
170+ tokenauth .Scope .DomainID = opts .Scope .DomainID
171+ }
172+ authOptsExt := trusts.AuthOptsExt {
173+ TrustID : cloud .AuthInfo .TrustID ,
174+ AuthOptionsBuilder : & tokenauth ,
108175 }
176+ err = openstack .AuthenticateV3 (provider , authOptsExt , gophercloud.EndpointOpts {})
177+ if err != nil {
178+ return nil , nil , "" , fmt .Errorf ("providerClient authentication err: %v" , err )
179+ }
180+ projectID , err := getProjectIDFromAuthResult (provider .GetAuthResult ())
181+ if err != nil {
182+ return nil , nil , "" , err
183+ }
184+ return provider ,clientOpts ,projectID ,nil
109185 }
110186 err = openstack .Authenticate (provider , * opts )
111187 if err != nil {
112188 return nil , nil , "" , fmt .Errorf ("providerClient authentication err: %v" , err )
113189 }
114-
115190 projectID , err := getProjectIDFromAuthResult (provider .GetAuthResult ())
116191 if err != nil {
117192 return nil , nil , "" , err
@@ -128,8 +203,8 @@ func (defaultLogger) Printf(format string, args ...interface{}) {
128203}
129204
130205// getCloudFromSecret extract a Cloud from the given namespace:secretName.
131- func getCloudFromSecret (ctx context.Context , ctrlClient client.Client , secretNamespace string , secretName string , cloudName string ) (clientconfig. Cloud , []byte , error ) {
132- emptyCloud := clientconfig. Cloud {}
206+ func getCloudFromSecret (ctx context.Context , ctrlClient client.Client , secretNamespace string , secretName string , cloudName string ) (NewCloud , []byte , error ) {
207+ emptyCloud := NewCloud {}
133208
134209 if secretName == "" {
135210 return emptyCloud , nil , nil
@@ -153,7 +228,7 @@ func getCloudFromSecret(ctx context.Context, ctrlClient client.Client, secretNam
153228 return emptyCloud , nil , fmt .Errorf ("OpenStack credentials secret %v did not contain key %v" ,
154229 secretName , cloudsSecretKey )
155230 }
156- var clouds clientconfig. Clouds
231+ var clouds NewClouds
157232 if err = yaml .Unmarshal (content , & clouds ); err != nil {
158233 return emptyCloud , nil , fmt .Errorf ("failed to unmarshal clouds credentials stored in secret %v: %v" , secretName , err )
159234 }
0 commit comments