blob: 84543da19758549c943c5abb2c34a41200e913da [file] [log] [blame]
Ash Wilson8ba82242014-08-28 15:38:55 -04001package openstack
2
3import (
Ash Wilsona87ee062014-09-03 11:26:06 -04004 "fmt"
Ash Wilson09694b92014-09-09 14:08:27 -04005 "net/url"
jrperritt93b4a3c2016-07-20 20:29:30 -05006 "reflect"
Joe Topjiand74641f2017-07-25 02:58:19 +00007 "regexp"
8 "strings"
Ash Wilson4dee1b82014-08-29 14:56:45 -04009
Krzysztof Szukiełojć3f41d082017-05-07 14:43:06 +020010 "gerrit.mcp.mirantis.net/debian/gophercloud.git"
Krzysztof Szukiełojć24a29ce2017-05-07 14:24:02 +020011 tokens2 "gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/identity/v2/tokens"
12 tokens3 "gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/identity/v3/tokens"
13 "gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/utils"
Ash Wilson8ba82242014-08-28 15:38:55 -040014)
15
Ash Wilson4dee1b82014-08-29 14:56:45 -040016const (
Joe Topjiand74641f2017-07-25 02:58:19 +000017 // v2 represents Keystone v2.
18 // It should never increase beyond 2.0.
19 v2 = "v2.0"
20
21 // v3 represents Keystone v3.
22 // The version can be anything from v3 to v3.x.
23 v3 = "v3"
Ash Wilson4dee1b82014-08-29 14:56:45 -040024)
Ash Wilson8ba82242014-08-28 15:38:55 -040025
Ash Wilsona87ee062014-09-03 11:26:06 -040026// NewClient prepares an unauthenticated ProviderClient instance.
27// Most users will probably prefer using the AuthenticatedClient function instead.
28// This is useful if you wish to explicitly control the version of the identity service that's used for authentication explicitly,
29// for example.
30func NewClient(endpoint string) (*gophercloud.ProviderClient, error) {
Ash Wilson09694b92014-09-09 14:08:27 -040031 u, err := url.Parse(endpoint)
32 if err != nil {
33 return nil, err
34 }
Joe Topjiand74641f2017-07-25 02:58:19 +000035
36 u.RawQuery, u.Fragment = "", ""
37
38 var base string
39 versionRe := regexp.MustCompile("v[0-9.]+/?")
40 if version := versionRe.FindString(u.Path); version != "" {
41 base = strings.Replace(u.String(), version, "", -1)
42 } else {
43 base = u.String()
44 }
Ash Wilson09694b92014-09-09 14:08:27 -040045
Ash Wilsona8440642014-10-07 09:55:58 -040046 endpoint = gophercloud.NormalizeURL(endpoint)
47 base = gophercloud.NormalizeURL(base)
Ash Wilsone7da01c2014-09-09 12:31:06 -040048
Ash Wilson09694b92014-09-09 14:08:27 -040049 return &gophercloud.ProviderClient{
50 IdentityBase: base,
Joe Topjiand74641f2017-07-25 02:58:19 +000051 IdentityEndpoint: endpoint,
Ash Wilson09694b92014-09-09 14:08:27 -040052 }, nil
Joe Topjiand74641f2017-07-25 02:58:19 +000053
Ash Wilsona87ee062014-09-03 11:26:06 -040054}
55
56// AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint specified by options, acquires a token, and
Ash Wilsonccd020b2014-09-02 10:40:54 -040057// returns a Client instance that's ready to operate.
Ash Wilson8ba82242014-08-28 15:38:55 -040058// It first queries the root identity endpoint to determine which versions of the identity service are supported, then chooses
59// the most recent identity service available to proceed.
Ash Wilsona87ee062014-09-03 11:26:06 -040060func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) {
61 client, err := NewClient(options.IdentityEndpoint)
62 if err != nil {
63 return nil, err
64 }
65
66 err = Authenticate(client, options)
Ash Wilsonccd020b2014-09-02 10:40:54 -040067 if err != nil {
68 return nil, err
69 }
70 return client, nil
71}
72
Ash Wilsonccd020b2014-09-02 10:40:54 -040073// Authenticate or re-authenticate against the most recent identity service supported at the provided endpoint.
Ash Wilsona87ee062014-09-03 11:26:06 -040074func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
Ash Wilson4dee1b82014-08-29 14:56:45 -040075 versions := []*utils.Version{
Joe Topjiand74641f2017-07-25 02:58:19 +000076 {ID: v2, Priority: 20, Suffix: "/v2.0/"},
77 {ID: v3, Priority: 30, Suffix: "/v3/"},
Ash Wilson4dee1b82014-08-29 14:56:45 -040078 }
79
Ash Wilson2491b4c2015-02-12 16:13:39 -050080 chosen, endpoint, err := utils.ChooseVersion(client, versions)
Ash Wilson4dee1b82014-08-29 14:56:45 -040081 if err != nil {
Ash Wilsonccd020b2014-09-02 10:40:54 -040082 return err
Ash Wilson4dee1b82014-08-29 14:56:45 -040083 }
84
85 switch chosen.ID {
Joe Topjiand74641f2017-07-25 02:58:19 +000086 case v2:
Jon Perritta33da232016-03-02 04:43:08 -060087 return v2auth(client, endpoint, options, gophercloud.EndpointOpts{})
Joe Topjiand74641f2017-07-25 02:58:19 +000088 case v3:
jrperritt0bc55782016-07-27 13:50:14 -050089 return v3auth(client, endpoint, &options, gophercloud.EndpointOpts{})
Ash Wilson4dee1b82014-08-29 14:56:45 -040090 default:
Ash Wilsonccd020b2014-09-02 10:40:54 -040091 // The switch statement must be out of date from the versions list.
Ash Wilsona87ee062014-09-03 11:26:06 -040092 return fmt.Errorf("Unrecognized identity version: %s", chosen.ID)
Ash Wilsonccd020b2014-09-02 10:40:54 -040093 }
94}
95
Ash Wilson09694b92014-09-09 14:08:27 -040096// AuthenticateV2 explicitly authenticates against the identity v2 endpoint.
Jon Perritta33da232016-03-02 04:43:08 -060097func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
98 return v2auth(client, "", options, eo)
Ash Wilson09694b92014-09-09 14:08:27 -040099}
100
Jon Perritta33da232016-03-02 04:43:08 -0600101func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
Jon Perritt376dfce2016-02-28 23:39:09 -0600102 v2Client, err := NewIdentityV2(client, eo)
103 if err != nil {
104 return err
105 }
106
Ash Wilson09694b92014-09-09 14:08:27 -0400107 if endpoint != "" {
108 v2Client.Endpoint = endpoint
109 }
110
jrperritt64d0ef02016-04-13 13:10:04 -0500111 v2Opts := tokens2.AuthOptions{
112 IdentityEndpoint: options.IdentityEndpoint,
113 Username: options.Username,
114 Password: options.Password,
115 TenantID: options.TenantID,
116 TenantName: options.TenantName,
117 AllowReauth: options.AllowReauth,
118 TokenID: options.TokenID,
119 }
120
121 result := tokens2.Create(v2Client, v2Opts)
Ash Wilson52fbd182014-10-03 13:48:06 -0400122
123 token, err := result.ExtractToken()
Ash Wilson09694b92014-09-09 14:08:27 -0400124 if err != nil {
125 return err
126 }
127
Ash Wilson52fbd182014-10-03 13:48:06 -0400128 catalog, err := result.ExtractServiceCatalog()
Ash Wilson09694b92014-09-09 14:08:27 -0400129 if err != nil {
130 return err
131 }
132
Jon Perrittf4052c62015-02-14 09:48:18 -0700133 if options.AllowReauth {
Jon Perritt6fe7c402015-02-17 12:24:53 -0700134 client.ReauthFunc = func() error {
Masahiro Sano1b2bafe2015-03-06 23:26:54 +0900135 client.TokenID = ""
Jon Perritta33da232016-03-02 04:43:08 -0600136 return v2auth(client, endpoint, options, eo)
Jon Perritt6fe7c402015-02-17 12:24:53 -0700137 }
Jon Perrittf4052c62015-02-14 09:48:18 -0700138 }
Ash Wilson09694b92014-09-09 14:08:27 -0400139 client.TokenID = token.ID
Ash Wilson130a6e22014-10-07 10:48:17 -0400140 client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
141 return V2EndpointURL(catalog, opts)
142 }
Ash Wilson09694b92014-09-09 14:08:27 -0400143
144 return nil
145}
146
Ash Wilson09694b92014-09-09 14:08:27 -0400147// AuthenticateV3 explicitly authenticates against the identity v3 service.
jrperritt0bc55782016-07-27 13:50:14 -0500148func AuthenticateV3(client *gophercloud.ProviderClient, options tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error {
Jon Perritta33da232016-03-02 04:43:08 -0600149 return v3auth(client, "", options, eo)
Ash Wilson09694b92014-09-09 14:08:27 -0400150}
151
jrperritt0bc55782016-07-27 13:50:14 -0500152func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error {
Ash Wilson09694b92014-09-09 14:08:27 -0400153 // Override the generated service endpoint with the one returned by the version endpoint.
Jon Perritt376dfce2016-02-28 23:39:09 -0600154 v3Client, err := NewIdentityV3(client, eo)
155 if err != nil {
156 return err
157 }
158
Ash Wilson09694b92014-09-09 14:08:27 -0400159 if endpoint != "" {
160 v3Client.Endpoint = endpoint
161 }
162
jrperritt0bc55782016-07-27 13:50:14 -0500163 result := tokens3.Create(v3Client, opts)
Guillaume Giamarchib2663b22015-04-01 01:23:29 +0200164
165 token, err := result.ExtractToken()
Ash Wilson09694b92014-09-09 14:08:27 -0400166 if err != nil {
167 return err
168 }
Guillaume Giamarchib2663b22015-04-01 01:23:29 +0200169
170 catalog, err := result.ExtractServiceCatalog()
171 if err != nil {
172 return err
173 }
174
Ash Wilson63b2a292014-10-02 09:29:06 -0400175 client.TokenID = token.ID
Ash Wilson09694b92014-09-09 14:08:27 -0400176
jrperritt0bc55782016-07-27 13:50:14 -0500177 if opts.CanReauth() {
Jon Perritt6fe7c402015-02-17 12:24:53 -0700178 client.ReauthFunc = func() error {
hzlouchaof6e29262015-10-27 12:51:08 +0800179 client.TokenID = ""
jrperritt0bc55782016-07-27 13:50:14 -0500180 return v3auth(client, endpoint, opts, eo)
Jon Perritt6fe7c402015-02-17 12:24:53 -0700181 }
Jon Perrittf4052c62015-02-14 09:48:18 -0700182 }
Ash Wilson09694b92014-09-09 14:08:27 -0400183 client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
Guillaume Giamarchib2663b22015-04-01 01:23:29 +0200184 return V3EndpointURL(catalog, opts)
Ash Wilson09694b92014-09-09 14:08:27 -0400185 }
186
187 return nil
188}
189
Ash Wilsona87ee062014-09-03 11:26:06 -0400190// NewIdentityV2 creates a ServiceClient that may be used to interact with the v2 identity service.
Jon Perritt376dfce2016-02-28 23:39:09 -0600191func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
jrperritt93b4a3c2016-07-20 20:29:30 -0500192 endpoint := client.IdentityBase + "v2.0/"
193 var err error
194 if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) {
Jon Perritta33da232016-03-02 04:43:08 -0600195 eo.ApplyDefaults("identity")
jrperritt93b4a3c2016-07-20 20:29:30 -0500196 endpoint, err = client.EndpointLocator(eo)
Jon Perritta33da232016-03-02 04:43:08 -0600197 if err != nil {
198 return nil, err
199 }
jrperritt93b4a3c2016-07-20 20:29:30 -0500200 }
Ash Wilsonccd020b2014-09-02 10:40:54 -0400201
Ash Wilsona87ee062014-09-03 11:26:06 -0400202 return &gophercloud.ServiceClient{
Ash Wilson92c380c2014-10-22 09:14:53 -0400203 ProviderClient: client,
jrperritt93b4a3c2016-07-20 20:29:30 -0500204 Endpoint: endpoint,
Jon Perritt376dfce2016-02-28 23:39:09 -0600205 }, nil
Ash Wilson8ba82242014-08-28 15:38:55 -0400206}
207
Ash Wilsona87ee062014-09-03 11:26:06 -0400208// NewIdentityV3 creates a ServiceClient that may be used to access the v3 identity service.
Jon Perritt376dfce2016-02-28 23:39:09 -0600209func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
jrperritt93b4a3c2016-07-20 20:29:30 -0500210 endpoint := client.IdentityBase + "v3/"
211 var err error
212 if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) {
Jon Perritta33da232016-03-02 04:43:08 -0600213 eo.ApplyDefaults("identity")
jrperritt93b4a3c2016-07-20 20:29:30 -0500214 endpoint, err = client.EndpointLocator(eo)
Jon Perritta33da232016-03-02 04:43:08 -0600215 if err != nil {
216 return nil, err
217 }
jrperritt93b4a3c2016-07-20 20:29:30 -0500218 }
Ash Wilsona87ee062014-09-03 11:26:06 -0400219
220 return &gophercloud.ServiceClient{
Ash Wilson92c380c2014-10-22 09:14:53 -0400221 ProviderClient: client,
jrperritt93b4a3c2016-07-20 20:29:30 -0500222 Endpoint: endpoint,
Jon Perritt376dfce2016-02-28 23:39:09 -0600223 }, nil
feisky66803f02015-08-28 22:06:34 +0800224}
225
Jon Perrittbb5e9812014-10-15 17:53:44 -0500226// NewObjectStorageV1 creates a ServiceClient that may be used with the v1 object storage package.
227func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
Jon Perritt509fbb62014-09-10 13:29:56 -0500228 eo.ApplyDefaults("object-store")
229 url, err := client.EndpointLocator(eo)
Ash Wilson1cd3e692014-09-09 11:01:47 -0400230 if err != nil {
231 return nil, err
232 }
Ash Wilson92c380c2014-10-22 09:14:53 -0400233 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
Ash Wilson1cd3e692014-09-09 11:01:47 -0400234}
Ash Wilson5e57c1b2014-09-17 09:24:46 -0400235
236// NewComputeV2 creates a ServiceClient that may be used with the v2 compute package.
237func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
238 eo.ApplyDefaults("compute")
239 url, err := client.EndpointLocator(eo)
240 if err != nil {
241 return nil, err
242 }
Ash Wilson92c380c2014-10-22 09:14:53 -0400243 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
Ash Wilson5e57c1b2014-09-17 09:24:46 -0400244}
Ash Wilsonebc3d122014-09-24 13:44:05 -0400245
246// NewNetworkV2 creates a ServiceClient that may be used with the v2 network package.
Jamie Hannaford7ea29582014-09-11 15:49:46 +0200247func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
248 eo.ApplyDefaults("network")
249 url, err := client.EndpointLocator(eo)
250 if err != nil {
251 return nil, err
252 }
Ash Wilson99541ab2014-10-06 17:32:39 -0400253 return &gophercloud.ServiceClient{
Ash Wilson92c380c2014-10-22 09:14:53 -0400254 ProviderClient: client,
255 Endpoint: url,
256 ResourceBase: url + "v2.0/",
Ash Wilson99541ab2014-10-06 17:32:39 -0400257 }, nil
Jamie Hannaford7ea29582014-09-11 15:49:46 +0200258}
Jon Perrittc5ee85e2014-09-17 00:53:19 -0500259
260// NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 block storage service.
261func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
262 eo.ApplyDefaults("volume")
263 url, err := client.EndpointLocator(eo)
264 if err != nil {
265 return nil, err
266 }
Ash Wilson92c380c2014-10-22 09:14:53 -0400267 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
Jon Perrittc5ee85e2014-09-17 00:53:19 -0500268}
Jon Perrittebd18ec2015-01-16 09:13:31 -0700269
feiskyda546142015-09-17 12:28:23 +0800270// NewBlockStorageV2 creates a ServiceClient that may be used to access the v2 block storage service.
271func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
Nick Craig-Woodb64fd202016-05-13 15:56:18 +0100272 eo.ApplyDefaults("volumev2")
feiskyda546142015-09-17 12:28:23 +0800273 url, err := client.EndpointLocator(eo)
274 if err != nil {
275 return nil, err
276 }
Nick Craig-Woodb64fd202016-05-13 15:56:18 +0100277 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
feiskyda546142015-09-17 12:28:23 +0800278}
279
Krzysztof Szukiełojće28b2e22017-07-31 11:31:06 +0200280// NewBlockStorageV3 creates a ServiceClient that may be used to access the v3 block storage service.
281func NewBlockStorageV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
282 eo.ApplyDefaults("volumev3")
283 url, err := client.EndpointLocator(eo)
284 if err != nil {
285 return nil, err
286 }
287 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
288}
289
ehdou10f1f852016-10-14 20:58:23 +0300290// NewSharedFileSystemV2 creates a ServiceClient that may be used to access the v2 shared file system service.
291func NewSharedFileSystemV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
292 eo.ApplyDefaults("sharev2")
293 url, err := client.EndpointLocator(eo)
294 if err != nil {
295 return nil, err
296 }
297 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
298}
299
Jon Perrittebd18ec2015-01-16 09:13:31 -0700300// NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1
301// CDN service.
302func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
303 eo.ApplyDefaults("cdn")
304 url, err := client.EndpointLocator(eo)
305 if err != nil {
306 return nil, err
307 }
308 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
309}
Jon Perritt35e27e42014-12-05 11:10:46 -0700310
311// NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 orchestration service.
312func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
313 eo.ApplyDefaults("orchestration")
314 url, err := client.EndpointLocator(eo)
315 if err != nil {
316 return nil, err
317 }
318 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
319}
Jamie Hannaford05d200d2015-02-20 14:49:05 +0100320
Jamie Hannaford75e8cc42015-11-16 14:09:25 +0100321// NewDBV1 creates a ServiceClient that may be used to access the v1 DB service.
Jamie Hannaford05d200d2015-02-20 14:49:05 +0100322func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
323 eo.ApplyDefaults("database")
324 url, err := client.EndpointLocator(eo)
325 if err != nil {
326 return nil, err
327 }
328 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
329}
jrperrittc5c590a2016-11-04 14:41:15 -0500330
Joe Topjian71b85bd2017-03-09 18:55:36 -0700331// NewDNSV2 creates a ServiceClient that may be used to access the v2 DNS service.
332func NewDNSV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
333 eo.ApplyDefaults("dns")
334 url, err := client.EndpointLocator(eo)
335 if err != nil {
336 return nil, err
337 }
338 return &gophercloud.ServiceClient{
339 ProviderClient: client,
340 Endpoint: url,
341 ResourceBase: url + "v2/"}, nil
342}
343
jrperrittc5c590a2016-11-04 14:41:15 -0500344// NewImageServiceV2 creates a ServiceClient that may be used to access the v2 image service.
345func NewImageServiceV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
346 eo.ApplyDefaults("image")
347 url, err := client.EndpointLocator(eo)
348 if err != nil {
349 return nil, err
350 }
Krzysztof Szukiełojće4c5dcc2017-09-14 14:04:46 +0200351 return &gophercloud.ServiceClient{ProviderClient: client,
352 Endpoint: url,
353 ResourceBase: url + "v2/"}, nil
jrperrittc5c590a2016-11-04 14:41:15 -0500354}
Michal Kobus4f4220e2018-12-17 18:45:17 +0100355
356// NewLoadBalancerV2 creates a ServiceClient that may be used to access the v2
357// load balancer service.
358func NewLoadBalancerV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
359 eo.ApplyDefaults("load-balancer")
360 url, err := client.EndpointLocator(eo)
361 if err != nil {
362 return nil, err
363 }
364 return &gophercloud.ServiceClient{ProviderClient: client,
365 Endpoint: url,
366 ResourceBase: url + "v2.0/"}, nil
367}