blob: 0585211c75df65f9eace197816e722eeaf3145b9 [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"
feisky66803f02015-08-28 22:06:34 +08006 "strings"
Ash Wilson4dee1b82014-08-29 14:56:45 -04007
Ash Wilson8ba82242014-08-28 15:38:55 -04008 "github.com/rackspace/gophercloud"
Ash Wilson52fbd182014-10-03 13:48:06 -04009 tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
Ash Wilsona87ee062014-09-03 11:26:06 -040010 tokens3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens"
Ash Wilson4dee1b82014-08-29 14:56:45 -040011 "github.com/rackspace/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:
Ash Wilson09694b92014-09-09 14:08:27 -040079 return v2auth(client, endpoint, options)
Ash Wilson4dee1b82014-08-29 14:56:45 -040080 case v30:
Ash Wilson09694b92014-09-09 14:08:27 -040081 return v3auth(client, endpoint, options)
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.
89func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
90 return v2auth(client, "", options)
91}
92
93func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions) error {
94 v2Client := NewIdentityV2(client)
95 if endpoint != "" {
96 v2Client.Endpoint = endpoint
97 }
98
Ash Wilson40095f02014-10-07 15:46:40 -040099 result := tokens2.Create(v2Client, tokens2.AuthOptions{AuthOptions: options})
Ash Wilson52fbd182014-10-03 13:48:06 -0400100
101 token, err := result.ExtractToken()
Ash Wilson09694b92014-09-09 14:08:27 -0400102 if err != nil {
103 return err
104 }
105
Ash Wilson52fbd182014-10-03 13:48:06 -0400106 catalog, err := result.ExtractServiceCatalog()
Ash Wilson09694b92014-09-09 14:08:27 -0400107 if err != nil {
108 return err
109 }
110
Jon Perrittf4052c62015-02-14 09:48:18 -0700111 if options.AllowReauth {
Jon Perritt6fe7c402015-02-17 12:24:53 -0700112 client.ReauthFunc = func() error {
Masahiro Sano1b2bafe2015-03-06 23:26:54 +0900113 client.TokenID = ""
alexcern5ef9a232016-02-10 11:41:00 +0100114 return v2auth(client, endpoint, options)
Jon Perritt6fe7c402015-02-17 12:24:53 -0700115 }
Jon Perrittf4052c62015-02-14 09:48:18 -0700116 }
Ash Wilson09694b92014-09-09 14:08:27 -0400117 client.TokenID = token.ID
Ash Wilson130a6e22014-10-07 10:48:17 -0400118 client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
119 return V2EndpointURL(catalog, opts)
120 }
Ash Wilson09694b92014-09-09 14:08:27 -0400121
122 return nil
123}
124
Ash Wilson09694b92014-09-09 14:08:27 -0400125// AuthenticateV3 explicitly authenticates against the identity v3 service.
126func AuthenticateV3(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
127 return v3auth(client, "", options)
128}
129
130func v3auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions) error {
131 // Override the generated service endpoint with the one returned by the version endpoint.
132 v3Client := NewIdentityV3(client)
133 if endpoint != "" {
134 v3Client.Endpoint = endpoint
135 }
136
jrperritt69cc3372016-04-06 13:51:52 -0500137 // copy the auth options to a local variable that we can change. `options`
138 // needs to stay as-is for reauth purposes
139 v3Options := options
140
Guillaume Giamarchi08b33d52015-04-01 01:34:17 +0200141 var scope *tokens3.Scope
142 if options.TenantID != "" {
143 scope = &tokens3.Scope{
144 ProjectID: options.TenantID,
145 }
jrperritt69cc3372016-04-06 13:51:52 -0500146 v3Options.TenantID = ""
147 v3Options.TenantName = ""
Guillaume Giamarchi08b33d52015-04-01 01:34:17 +0200148 } else {
149 if options.TenantName != "" {
150 scope = &tokens3.Scope{
151 ProjectName: options.TenantName,
152 DomainID: options.DomainID,
153 DomainName: options.DomainName,
154 }
jrperritt69cc3372016-04-06 13:51:52 -0500155 v3Options.TenantName = ""
Guillaume Giamarchi08b33d52015-04-01 01:34:17 +0200156 }
157 }
158
jrperritt69cc3372016-04-06 13:51:52 -0500159 result := tokens3.Create(v3Client, v3Options, scope)
Guillaume Giamarchib2663b22015-04-01 01:23:29 +0200160
161 token, err := result.ExtractToken()
Ash Wilson09694b92014-09-09 14:08:27 -0400162 if err != nil {
163 return err
164 }
Guillaume Giamarchib2663b22015-04-01 01:23:29 +0200165
166 catalog, err := result.ExtractServiceCatalog()
167 if err != nil {
168 return err
169 }
170
Ash Wilson63b2a292014-10-02 09:29:06 -0400171 client.TokenID = token.ID
Ash Wilson09694b92014-09-09 14:08:27 -0400172
Jon Perrittf4052c62015-02-14 09:48:18 -0700173 if options.AllowReauth {
Jon Perritt6fe7c402015-02-17 12:24:53 -0700174 client.ReauthFunc = func() error {
hzlouchaof6e29262015-10-27 12:51:08 +0800175 client.TokenID = ""
alexcern5ef9a232016-02-10 11:41:00 +0100176 return v3auth(client, endpoint, options)
Jon Perritt6fe7c402015-02-17 12:24:53 -0700177 }
Jon Perrittf4052c62015-02-14 09:48:18 -0700178 }
Ash Wilson09694b92014-09-09 14:08:27 -0400179 client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
Guillaume Giamarchib2663b22015-04-01 01:23:29 +0200180 return V3EndpointURL(catalog, opts)
Ash Wilson09694b92014-09-09 14:08:27 -0400181 }
182
183 return nil
184}
185
Ash Wilsona87ee062014-09-03 11:26:06 -0400186// NewIdentityV2 creates a ServiceClient that may be used to interact with the v2 identity service.
187func NewIdentityV2(client *gophercloud.ProviderClient) *gophercloud.ServiceClient {
Ash Wilson09694b92014-09-09 14:08:27 -0400188 v2Endpoint := client.IdentityBase + "v2.0/"
Ash Wilsonccd020b2014-09-02 10:40:54 -0400189
Ash Wilsona87ee062014-09-03 11:26:06 -0400190 return &gophercloud.ServiceClient{
Ash Wilson92c380c2014-10-22 09:14:53 -0400191 ProviderClient: client,
192 Endpoint: v2Endpoint,
Ash Wilson4dee1b82014-08-29 14:56:45 -0400193 }
Ash Wilson8ba82242014-08-28 15:38:55 -0400194}
195
Ash Wilsona87ee062014-09-03 11:26:06 -0400196// NewIdentityV3 creates a ServiceClient that may be used to access the v3 identity service.
197func NewIdentityV3(client *gophercloud.ProviderClient) *gophercloud.ServiceClient {
Ash Wilson09694b92014-09-09 14:08:27 -0400198 v3Endpoint := client.IdentityBase + "v3/"
Ash Wilsona87ee062014-09-03 11:26:06 -0400199
200 return &gophercloud.ServiceClient{
Ash Wilson92c380c2014-10-22 09:14:53 -0400201 ProviderClient: client,
202 Endpoint: v3Endpoint,
Ash Wilsona87ee062014-09-03 11:26:06 -0400203 }
Ash Wilson8ba82242014-08-28 15:38:55 -0400204}
Ash Wilson1cd3e692014-09-09 11:01:47 -0400205
feisky66803f02015-08-28 22:06:34 +0800206func NewIdentityAdminV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
207 eo.ApplyDefaults("identity")
208 eo.Availability = gophercloud.AvailabilityAdmin
209
210 url, err := client.EndpointLocator(eo)
211 if err != nil {
212 return nil, err
213 }
214
215 // Force using v2 API
216 if strings.Contains(url, "/v3") {
217 url = strings.Replace(url, "/v3", "/v2.0", -1)
218 }
219
220 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
221}
222
223func NewIdentityAdminV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
224 eo.ApplyDefaults("identity")
225 eo.Availability = gophercloud.AvailabilityAdmin
226
227 url, err := client.EndpointLocator(eo)
228 if err != nil {
229 return nil, err
230 }
231
232 // Force using v3 API
233 if strings.Contains(url, "/v2.0") {
234 url = strings.Replace(url, "/v2.0", "/v3", -1)
235 }
236
237 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
238}
239
Jon Perrittbb5e9812014-10-15 17:53:44 -0500240// NewObjectStorageV1 creates a ServiceClient that may be used with the v1 object storage package.
241func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
Jon Perritt509fbb62014-09-10 13:29:56 -0500242 eo.ApplyDefaults("object-store")
243 url, err := client.EndpointLocator(eo)
Ash Wilson1cd3e692014-09-09 11:01:47 -0400244 if err != nil {
245 return nil, err
246 }
Ash Wilson92c380c2014-10-22 09:14:53 -0400247 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
Ash Wilson1cd3e692014-09-09 11:01:47 -0400248}
Ash Wilson5e57c1b2014-09-17 09:24:46 -0400249
250// NewComputeV2 creates a ServiceClient that may be used with the v2 compute package.
251func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
252 eo.ApplyDefaults("compute")
253 url, err := client.EndpointLocator(eo)
254 if err != nil {
255 return nil, err
256 }
Ash Wilson92c380c2014-10-22 09:14:53 -0400257 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
Ash Wilson5e57c1b2014-09-17 09:24:46 -0400258}
Ash Wilsonebc3d122014-09-24 13:44:05 -0400259
260// NewNetworkV2 creates a ServiceClient that may be used with the v2 network package.
Jamie Hannaford7ea29582014-09-11 15:49:46 +0200261func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
262 eo.ApplyDefaults("network")
263 url, err := client.EndpointLocator(eo)
264 if err != nil {
265 return nil, err
266 }
Ash Wilson99541ab2014-10-06 17:32:39 -0400267 return &gophercloud.ServiceClient{
Ash Wilson92c380c2014-10-22 09:14:53 -0400268 ProviderClient: client,
269 Endpoint: url,
270 ResourceBase: url + "v2.0/",
Ash Wilson99541ab2014-10-06 17:32:39 -0400271 }, nil
Jamie Hannaford7ea29582014-09-11 15:49:46 +0200272}
Jon Perrittc5ee85e2014-09-17 00:53:19 -0500273
274// NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 block storage service.
275func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
276 eo.ApplyDefaults("volume")
277 url, err := client.EndpointLocator(eo)
278 if err != nil {
279 return nil, err
280 }
Ash Wilson92c380c2014-10-22 09:14:53 -0400281 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
Jon Perrittc5ee85e2014-09-17 00:53:19 -0500282}
Jon Perrittebd18ec2015-01-16 09:13:31 -0700283
feiskyda546142015-09-17 12:28:23 +0800284// NewBlockStorageV2 creates a ServiceClient that may be used to access the v2 block storage service.
285func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
Nick Craig-Woodb64fd202016-05-13 15:56:18 +0100286 eo.ApplyDefaults("volumev2")
feiskyda546142015-09-17 12:28:23 +0800287 url, err := client.EndpointLocator(eo)
288 if err != nil {
289 return nil, err
290 }
Nick Craig-Woodb64fd202016-05-13 15:56:18 +0100291 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
feiskyda546142015-09-17 12:28:23 +0800292}
293
Jon Perrittebd18ec2015-01-16 09:13:31 -0700294// NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1
295// CDN service.
296func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
297 eo.ApplyDefaults("cdn")
298 url, err := client.EndpointLocator(eo)
299 if err != nil {
300 return nil, err
301 }
302 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
303}
Jon Perritt35e27e42014-12-05 11:10:46 -0700304
305// NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 orchestration service.
306func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
307 eo.ApplyDefaults("orchestration")
308 url, err := client.EndpointLocator(eo)
309 if err != nil {
310 return nil, err
311 }
312 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
313}
Jamie Hannaford05d200d2015-02-20 14:49:05 +0100314
Jamie Hannaford75e8cc42015-11-16 14:09:25 +0100315// NewDBV1 creates a ServiceClient that may be used to access the v1 DB service.
Jamie Hannaford05d200d2015-02-20 14:49:05 +0100316func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
317 eo.ApplyDefaults("database")
318 url, err := client.EndpointLocator(eo)
319 if err != nil {
320 return nil, err
321 }
322 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
323}