blob: ebad6dca792f24a3cd1aa309c222b1dc585f7d9c [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"
Ash Wilson4dee1b82014-08-29 14:56:45 -04007
Jon Perritt27249f42016-02-18 10:35:59 -06008 "github.com/gophercloud/gophercloud"
9 tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
10 tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
11 "github.com/gophercloud/gophercloud/openstack/utils"
Ash Wilson8ba82242014-08-28 15:38:55 -040012)
13
Ash Wilson4dee1b82014-08-29 14:56:45 -040014const (
15 v20 = "v2.0"
16 v30 = "v3.0"
17)
Ash Wilson8ba82242014-08-28 15:38:55 -040018
Ash Wilsona87ee062014-09-03 11:26:06 -040019// NewClient prepares an unauthenticated ProviderClient instance.
20// Most users will probably prefer using the AuthenticatedClient function instead.
21// This is useful if you wish to explicitly control the version of the identity service that's used for authentication explicitly,
22// for example.
23func NewClient(endpoint string) (*gophercloud.ProviderClient, error) {
Ash Wilson09694b92014-09-09 14:08:27 -040024 u, err := url.Parse(endpoint)
25 if err != nil {
26 return nil, err
27 }
28 hadPath := u.Path != ""
29 u.Path, u.RawQuery, u.Fragment = "", "", ""
30 base := u.String()
31
Ash Wilsona8440642014-10-07 09:55:58 -040032 endpoint = gophercloud.NormalizeURL(endpoint)
33 base = gophercloud.NormalizeURL(base)
Ash Wilsone7da01c2014-09-09 12:31:06 -040034
Ash Wilson09694b92014-09-09 14:08:27 -040035 if hadPath {
36 return &gophercloud.ProviderClient{
37 IdentityBase: base,
38 IdentityEndpoint: endpoint,
39 }, nil
40 }
41
42 return &gophercloud.ProviderClient{
43 IdentityBase: base,
44 IdentityEndpoint: "",
45 }, nil
Ash Wilsona87ee062014-09-03 11:26:06 -040046}
47
48// 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 -040049// returns a Client instance that's ready to operate.
Ash Wilson8ba82242014-08-28 15:38:55 -040050// It first queries the root identity endpoint to determine which versions of the identity service are supported, then chooses
51// the most recent identity service available to proceed.
Ash Wilsona87ee062014-09-03 11:26:06 -040052func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) {
53 client, err := NewClient(options.IdentityEndpoint)
54 if err != nil {
55 return nil, err
56 }
57
58 err = Authenticate(client, options)
Ash Wilsonccd020b2014-09-02 10:40:54 -040059 if err != nil {
60 return nil, err
61 }
62 return client, nil
63}
64
Ash Wilsonccd020b2014-09-02 10:40:54 -040065// Authenticate or re-authenticate against the most recent identity service supported at the provided endpoint.
Ash Wilsona87ee062014-09-03 11:26:06 -040066func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
Ash Wilson4dee1b82014-08-29 14:56:45 -040067 versions := []*utils.Version{
feisky66803f02015-08-28 22:06:34 +080068 {ID: v20, Priority: 20, Suffix: "/v2.0/"},
69 {ID: v30, Priority: 30, Suffix: "/v3/"},
Ash Wilson4dee1b82014-08-29 14:56:45 -040070 }
71
Ash Wilson2491b4c2015-02-12 16:13:39 -050072 chosen, endpoint, err := utils.ChooseVersion(client, versions)
Ash Wilson4dee1b82014-08-29 14:56:45 -040073 if err != nil {
Ash Wilsonccd020b2014-09-02 10:40:54 -040074 return err
Ash Wilson4dee1b82014-08-29 14:56:45 -040075 }
76
77 switch chosen.ID {
78 case v20:
Jon Perritta33da232016-03-02 04:43:08 -060079 return v2auth(client, endpoint, options, gophercloud.EndpointOpts{})
Ash Wilson4dee1b82014-08-29 14:56:45 -040080 case v30:
Jon Perritta33da232016-03-02 04:43:08 -060081 return v3auth(client, endpoint, options, gophercloud.EndpointOpts{})
Ash Wilson4dee1b82014-08-29 14:56:45 -040082 default:
Ash Wilsonccd020b2014-09-02 10:40:54 -040083 // The switch statement must be out of date from the versions list.
Ash Wilsona87ee062014-09-03 11:26:06 -040084 return fmt.Errorf("Unrecognized identity version: %s", chosen.ID)
Ash Wilsonccd020b2014-09-02 10:40:54 -040085 }
86}
87
Ash Wilson09694b92014-09-09 14:08:27 -040088// AuthenticateV2 explicitly authenticates against the identity v2 endpoint.
Jon Perritta33da232016-03-02 04:43:08 -060089func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
90 return v2auth(client, "", options, eo)
Ash Wilson09694b92014-09-09 14:08:27 -040091}
92
Jon Perritta33da232016-03-02 04:43:08 -060093func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
Jon Perritt376dfce2016-02-28 23:39:09 -060094 v2Client, err := NewIdentityV2(client, eo)
95 if err != nil {
96 return err
97 }
98
Ash Wilson09694b92014-09-09 14:08:27 -040099 if endpoint != "" {
100 v2Client.Endpoint = endpoint
101 }
102
jrperritt64d0ef02016-04-13 13:10:04 -0500103 v2Opts := tokens2.AuthOptions{
104 IdentityEndpoint: options.IdentityEndpoint,
105 Username: options.Username,
106 Password: options.Password,
107 TenantID: options.TenantID,
108 TenantName: options.TenantName,
109 AllowReauth: options.AllowReauth,
110 TokenID: options.TokenID,
111 }
112
113 result := tokens2.Create(v2Client, v2Opts)
Ash Wilson52fbd182014-10-03 13:48:06 -0400114
115 token, err := result.ExtractToken()
Ash Wilson09694b92014-09-09 14:08:27 -0400116 if err != nil {
117 return err
118 }
119
Ash Wilson52fbd182014-10-03 13:48:06 -0400120 catalog, err := result.ExtractServiceCatalog()
Ash Wilson09694b92014-09-09 14:08:27 -0400121 if err != nil {
122 return err
123 }
124
Jon Perrittf4052c62015-02-14 09:48:18 -0700125 if options.AllowReauth {
Jon Perritt6fe7c402015-02-17 12:24:53 -0700126 client.ReauthFunc = func() error {
Masahiro Sano1b2bafe2015-03-06 23:26:54 +0900127 client.TokenID = ""
Jon Perritta33da232016-03-02 04:43:08 -0600128 return v2auth(client, endpoint, options, eo)
Jon Perritt6fe7c402015-02-17 12:24:53 -0700129 }
Jon Perrittf4052c62015-02-14 09:48:18 -0700130 }
Ash Wilson09694b92014-09-09 14:08:27 -0400131 client.TokenID = token.ID
Ash Wilson130a6e22014-10-07 10:48:17 -0400132 client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
133 return V2EndpointURL(catalog, opts)
134 }
Ash Wilson09694b92014-09-09 14:08:27 -0400135
136 return nil
137}
138
Ash Wilson09694b92014-09-09 14:08:27 -0400139// AuthenticateV3 explicitly authenticates against the identity v3 service.
Jon Perritta33da232016-03-02 04:43:08 -0600140func AuthenticateV3(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
141 return v3auth(client, "", options, eo)
Ash Wilson09694b92014-09-09 14:08:27 -0400142}
143
Jon Perritta33da232016-03-02 04:43:08 -0600144func v3auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
Ash Wilson09694b92014-09-09 14:08:27 -0400145 // Override the generated service endpoint with the one returned by the version endpoint.
Jon Perritt376dfce2016-02-28 23:39:09 -0600146 v3Client, err := NewIdentityV3(client, eo)
147 if err != nil {
148 return err
149 }
150
Ash Wilson09694b92014-09-09 14:08:27 -0400151 if endpoint != "" {
152 v3Client.Endpoint = endpoint
153 }
154
jrperritt69cc3372016-04-06 13:51:52 -0500155 // copy the auth options to a local variable that we can change. `options`
156 // needs to stay as-is for reauth purposes
157 v3Options := options
158
Guillaume Giamarchi08b33d52015-04-01 01:34:17 +0200159 var scope *tokens3.Scope
160 if options.TenantID != "" {
161 scope = &tokens3.Scope{
162 ProjectID: options.TenantID,
163 }
jrperritt69cc3372016-04-06 13:51:52 -0500164 v3Options.TenantID = ""
165 v3Options.TenantName = ""
Guillaume Giamarchi08b33d52015-04-01 01:34:17 +0200166 } else {
167 if options.TenantName != "" {
168 scope = &tokens3.Scope{
169 ProjectName: options.TenantName,
170 DomainID: options.DomainID,
171 DomainName: options.DomainName,
172 }
jrperritt69cc3372016-04-06 13:51:52 -0500173 v3Options.TenantName = ""
Guillaume Giamarchi08b33d52015-04-01 01:34:17 +0200174 }
175 }
176
jrperritt29ae6b32016-04-13 12:59:37 -0500177 v3Opts := tokens3.AuthOptions{
Carolyn Van Slyck9977e512016-07-20 14:34:52 -0500178 IdentityEndpoint: v3Options.IdentityEndpoint,
179 Username: v3Options.Username,
180 UserID: v3Options.UserID,
181 Password: v3Options.Password,
182 DomainID: v3Options.DomainID,
183 DomainName: v3Options.DomainName,
184 TenantID: v3Options.TenantID,
185 TenantName: v3Options.TenantName,
186 AllowReauth: v3Options.AllowReauth,
187 TokenID: v3Options.TokenID,
jrperritt29ae6b32016-04-13 12:59:37 -0500188 }
189
190 result := tokens3.Create(v3Client, v3Opts, scope)
Guillaume Giamarchib2663b22015-04-01 01:23:29 +0200191
192 token, err := result.ExtractToken()
Ash Wilson09694b92014-09-09 14:08:27 -0400193 if err != nil {
194 return err
195 }
Guillaume Giamarchib2663b22015-04-01 01:23:29 +0200196
197 catalog, err := result.ExtractServiceCatalog()
198 if err != nil {
199 return err
200 }
201
Ash Wilson63b2a292014-10-02 09:29:06 -0400202 client.TokenID = token.ID
Ash Wilson09694b92014-09-09 14:08:27 -0400203
Jon Perrittf4052c62015-02-14 09:48:18 -0700204 if options.AllowReauth {
Jon Perritt6fe7c402015-02-17 12:24:53 -0700205 client.ReauthFunc = func() error {
hzlouchaof6e29262015-10-27 12:51:08 +0800206 client.TokenID = ""
Jon Perritta33da232016-03-02 04:43:08 -0600207 return v3auth(client, endpoint, options, eo)
Jon Perritt6fe7c402015-02-17 12:24:53 -0700208 }
Jon Perrittf4052c62015-02-14 09:48:18 -0700209 }
Ash Wilson09694b92014-09-09 14:08:27 -0400210 client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
Guillaume Giamarchib2663b22015-04-01 01:23:29 +0200211 return V3EndpointURL(catalog, opts)
Ash Wilson09694b92014-09-09 14:08:27 -0400212 }
213
214 return nil
215}
216
Ash Wilsona87ee062014-09-03 11:26:06 -0400217// NewIdentityV2 creates a ServiceClient that may be used to interact with the v2 identity service.
Jon Perritt376dfce2016-02-28 23:39:09 -0600218func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
jrperritt93b4a3c2016-07-20 20:29:30 -0500219 endpoint := client.IdentityBase + "v2.0/"
220 var err error
221 if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) {
Jon Perritta33da232016-03-02 04:43:08 -0600222 eo.ApplyDefaults("identity")
jrperritt93b4a3c2016-07-20 20:29:30 -0500223 endpoint, err = client.EndpointLocator(eo)
Jon Perritta33da232016-03-02 04:43:08 -0600224 if err != nil {
225 return nil, err
226 }
jrperritt93b4a3c2016-07-20 20:29:30 -0500227 }
Ash Wilsonccd020b2014-09-02 10:40:54 -0400228
Ash Wilsona87ee062014-09-03 11:26:06 -0400229 return &gophercloud.ServiceClient{
Ash Wilson92c380c2014-10-22 09:14:53 -0400230 ProviderClient: client,
jrperritt93b4a3c2016-07-20 20:29:30 -0500231 Endpoint: endpoint,
Jon Perritt376dfce2016-02-28 23:39:09 -0600232 }, nil
Ash Wilson8ba82242014-08-28 15:38:55 -0400233}
234
Ash Wilsona87ee062014-09-03 11:26:06 -0400235// NewIdentityV3 creates a ServiceClient that may be used to access the v3 identity service.
Jon Perritt376dfce2016-02-28 23:39:09 -0600236func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
jrperritt93b4a3c2016-07-20 20:29:30 -0500237 endpoint := client.IdentityBase + "v3/"
238 var err error
239 if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) {
Jon Perritta33da232016-03-02 04:43:08 -0600240 eo.ApplyDefaults("identity")
jrperritt93b4a3c2016-07-20 20:29:30 -0500241 endpoint, err = client.EndpointLocator(eo)
Jon Perritta33da232016-03-02 04:43:08 -0600242 if err != nil {
243 return nil, err
244 }
jrperritt93b4a3c2016-07-20 20:29:30 -0500245 }
Ash Wilsona87ee062014-09-03 11:26:06 -0400246
247 return &gophercloud.ServiceClient{
Ash Wilson92c380c2014-10-22 09:14:53 -0400248 ProviderClient: client,
jrperritt93b4a3c2016-07-20 20:29:30 -0500249 Endpoint: endpoint,
Jon Perritt376dfce2016-02-28 23:39:09 -0600250 }, nil
feisky66803f02015-08-28 22:06:34 +0800251}
252
Jon Perrittbb5e9812014-10-15 17:53:44 -0500253// NewObjectStorageV1 creates a ServiceClient that may be used with the v1 object storage package.
254func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
Jon Perritt509fbb62014-09-10 13:29:56 -0500255 eo.ApplyDefaults("object-store")
256 url, err := client.EndpointLocator(eo)
Ash Wilson1cd3e692014-09-09 11:01:47 -0400257 if err != nil {
258 return nil, err
259 }
Ash Wilson92c380c2014-10-22 09:14:53 -0400260 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
Ash Wilson1cd3e692014-09-09 11:01:47 -0400261}
Ash Wilson5e57c1b2014-09-17 09:24:46 -0400262
263// NewComputeV2 creates a ServiceClient that may be used with the v2 compute package.
264func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
265 eo.ApplyDefaults("compute")
266 url, err := client.EndpointLocator(eo)
267 if err != nil {
268 return nil, err
269 }
Ash Wilson92c380c2014-10-22 09:14:53 -0400270 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
Ash Wilson5e57c1b2014-09-17 09:24:46 -0400271}
Ash Wilsonebc3d122014-09-24 13:44:05 -0400272
273// NewNetworkV2 creates a ServiceClient that may be used with the v2 network package.
Jamie Hannaford7ea29582014-09-11 15:49:46 +0200274func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
275 eo.ApplyDefaults("network")
276 url, err := client.EndpointLocator(eo)
277 if err != nil {
278 return nil, err
279 }
Ash Wilson99541ab2014-10-06 17:32:39 -0400280 return &gophercloud.ServiceClient{
Ash Wilson92c380c2014-10-22 09:14:53 -0400281 ProviderClient: client,
282 Endpoint: url,
283 ResourceBase: url + "v2.0/",
Ash Wilson99541ab2014-10-06 17:32:39 -0400284 }, nil
Jamie Hannaford7ea29582014-09-11 15:49:46 +0200285}
Jon Perrittc5ee85e2014-09-17 00:53:19 -0500286
287// NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 block storage service.
288func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
289 eo.ApplyDefaults("volume")
290 url, err := client.EndpointLocator(eo)
291 if err != nil {
292 return nil, err
293 }
Ash Wilson92c380c2014-10-22 09:14:53 -0400294 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
Jon Perrittc5ee85e2014-09-17 00:53:19 -0500295}
Jon Perrittebd18ec2015-01-16 09:13:31 -0700296
feiskyda546142015-09-17 12:28:23 +0800297// NewBlockStorageV2 creates a ServiceClient that may be used to access the v2 block storage service.
298func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
Nick Craig-Woodb64fd202016-05-13 15:56:18 +0100299 eo.ApplyDefaults("volumev2")
feiskyda546142015-09-17 12:28:23 +0800300 url, err := client.EndpointLocator(eo)
301 if err != nil {
302 return nil, err
303 }
Nick Craig-Woodb64fd202016-05-13 15:56:18 +0100304 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
feiskyda546142015-09-17 12:28:23 +0800305}
306
Jon Perrittebd18ec2015-01-16 09:13:31 -0700307// NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1
308// CDN service.
309func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
310 eo.ApplyDefaults("cdn")
311 url, err := client.EndpointLocator(eo)
312 if err != nil {
313 return nil, err
314 }
315 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
316}
Jon Perritt35e27e42014-12-05 11:10:46 -0700317
318// NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 orchestration service.
319func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
320 eo.ApplyDefaults("orchestration")
321 url, err := client.EndpointLocator(eo)
322 if err != nil {
323 return nil, err
324 }
325 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
326}
Jamie Hannaford05d200d2015-02-20 14:49:05 +0100327
Jamie Hannaford75e8cc42015-11-16 14:09:25 +0100328// NewDBV1 creates a ServiceClient that may be used to access the v1 DB service.
Jamie Hannaford05d200d2015-02-20 14:49:05 +0100329func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
330 eo.ApplyDefaults("database")
331 url, err := client.EndpointLocator(eo)
332 if err != nil {
333 return nil, err
334 }
335 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
336}