blob: 3e115080080050a1a30ba853e68542386dbb7c4e [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"
Ash Wilson4dee1b82014-08-29 14:56:45 -04006
Jon Perritt27249f42016-02-18 10:35:59 -06007 "github.com/gophercloud/gophercloud"
8 tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
9 tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
10 "github.com/gophercloud/gophercloud/openstack/utils"
Ash Wilson8ba82242014-08-28 15:38:55 -040011)
12
Ash Wilson4dee1b82014-08-29 14:56:45 -040013const (
14 v20 = "v2.0"
15 v30 = "v3.0"
16)
Ash Wilson8ba82242014-08-28 15:38:55 -040017
Ash Wilsona87ee062014-09-03 11:26:06 -040018// NewClient prepares an unauthenticated ProviderClient instance.
19// Most users will probably prefer using the AuthenticatedClient function instead.
20// This is useful if you wish to explicitly control the version of the identity service that's used for authentication explicitly,
21// for example.
22func NewClient(endpoint string) (*gophercloud.ProviderClient, error) {
Ash Wilson09694b92014-09-09 14:08:27 -040023 u, err := url.Parse(endpoint)
24 if err != nil {
25 return nil, err
26 }
27 hadPath := u.Path != ""
28 u.Path, u.RawQuery, u.Fragment = "", "", ""
29 base := u.String()
30
Ash Wilsona8440642014-10-07 09:55:58 -040031 endpoint = gophercloud.NormalizeURL(endpoint)
32 base = gophercloud.NormalizeURL(base)
Ash Wilsone7da01c2014-09-09 12:31:06 -040033
Ash Wilson09694b92014-09-09 14:08:27 -040034 if hadPath {
35 return &gophercloud.ProviderClient{
36 IdentityBase: base,
37 IdentityEndpoint: endpoint,
38 }, nil
39 }
40
41 return &gophercloud.ProviderClient{
42 IdentityBase: base,
43 IdentityEndpoint: "",
44 }, nil
Ash Wilsona87ee062014-09-03 11:26:06 -040045}
46
47// 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 -040048// returns a Client instance that's ready to operate.
Ash Wilson8ba82242014-08-28 15:38:55 -040049// It first queries the root identity endpoint to determine which versions of the identity service are supported, then chooses
50// the most recent identity service available to proceed.
Ash Wilsona87ee062014-09-03 11:26:06 -040051func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) {
52 client, err := NewClient(options.IdentityEndpoint)
53 if err != nil {
54 return nil, err
55 }
56
57 err = Authenticate(client, options)
Ash Wilsonccd020b2014-09-02 10:40:54 -040058 if err != nil {
59 return nil, err
60 }
61 return client, nil
62}
63
Ash Wilsonccd020b2014-09-02 10:40:54 -040064// Authenticate or re-authenticate against the most recent identity service supported at the provided endpoint.
Ash Wilsona87ee062014-09-03 11:26:06 -040065func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
Ash Wilson4dee1b82014-08-29 14:56:45 -040066 versions := []*utils.Version{
feisky66803f02015-08-28 22:06:34 +080067 {ID: v20, Priority: 20, Suffix: "/v2.0/"},
68 {ID: v30, Priority: 30, Suffix: "/v3/"},
Ash Wilson4dee1b82014-08-29 14:56:45 -040069 }
70
Ash Wilson2491b4c2015-02-12 16:13:39 -050071 chosen, endpoint, err := utils.ChooseVersion(client, versions)
Ash Wilson4dee1b82014-08-29 14:56:45 -040072 if err != nil {
Ash Wilsonccd020b2014-09-02 10:40:54 -040073 return err
Ash Wilson4dee1b82014-08-29 14:56:45 -040074 }
75
76 switch chosen.ID {
77 case v20:
Jon Perritta33da232016-03-02 04:43:08 -060078 return v2auth(client, endpoint, options, gophercloud.EndpointOpts{})
Ash Wilson4dee1b82014-08-29 14:56:45 -040079 case v30:
Jon Perritta33da232016-03-02 04:43:08 -060080 return v3auth(client, endpoint, options, gophercloud.EndpointOpts{})
Ash Wilson4dee1b82014-08-29 14:56:45 -040081 default:
Ash Wilsonccd020b2014-09-02 10:40:54 -040082 // The switch statement must be out of date from the versions list.
Ash Wilsona87ee062014-09-03 11:26:06 -040083 return fmt.Errorf("Unrecognized identity version: %s", chosen.ID)
Ash Wilsonccd020b2014-09-02 10:40:54 -040084 }
85}
86
Ash Wilson09694b92014-09-09 14:08:27 -040087// AuthenticateV2 explicitly authenticates against the identity v2 endpoint.
Jon Perritta33da232016-03-02 04:43:08 -060088func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
89 return v2auth(client, "", options, eo)
Ash Wilson09694b92014-09-09 14:08:27 -040090}
91
Jon Perritta33da232016-03-02 04:43:08 -060092func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
Jon Perritt376dfce2016-02-28 23:39:09 -060093 v2Client, err := NewIdentityV2(client, eo)
94 if err != nil {
95 return err
96 }
97
Ash Wilson09694b92014-09-09 14:08:27 -040098 if endpoint != "" {
99 v2Client.Endpoint = endpoint
100 }
101
jrperritt64d0ef02016-04-13 13:10:04 -0500102 v2Opts := tokens2.AuthOptions{
103 IdentityEndpoint: options.IdentityEndpoint,
104 Username: options.Username,
105 Password: options.Password,
106 TenantID: options.TenantID,
107 TenantName: options.TenantName,
108 AllowReauth: options.AllowReauth,
109 TokenID: options.TokenID,
110 }
111
112 result := tokens2.Create(v2Client, v2Opts)
Ash Wilson52fbd182014-10-03 13:48:06 -0400113
114 token, err := result.ExtractToken()
Ash Wilson09694b92014-09-09 14:08:27 -0400115 if err != nil {
116 return err
117 }
118
Ash Wilson52fbd182014-10-03 13:48:06 -0400119 catalog, err := result.ExtractServiceCatalog()
Ash Wilson09694b92014-09-09 14:08:27 -0400120 if err != nil {
121 return err
122 }
123
Jon Perrittf4052c62015-02-14 09:48:18 -0700124 if options.AllowReauth {
Jon Perritt6fe7c402015-02-17 12:24:53 -0700125 client.ReauthFunc = func() error {
Masahiro Sano1b2bafe2015-03-06 23:26:54 +0900126 client.TokenID = ""
Jon Perritta33da232016-03-02 04:43:08 -0600127 return v2auth(client, endpoint, options, eo)
Jon Perritt6fe7c402015-02-17 12:24:53 -0700128 }
Jon Perrittf4052c62015-02-14 09:48:18 -0700129 }
Ash Wilson09694b92014-09-09 14:08:27 -0400130 client.TokenID = token.ID
Ash Wilson130a6e22014-10-07 10:48:17 -0400131 client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
132 return V2EndpointURL(catalog, opts)
133 }
Ash Wilson09694b92014-09-09 14:08:27 -0400134
135 return nil
136}
137
Ash Wilson09694b92014-09-09 14:08:27 -0400138// AuthenticateV3 explicitly authenticates against the identity v3 service.
Jon Perritta33da232016-03-02 04:43:08 -0600139func AuthenticateV3(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
140 return v3auth(client, "", options, eo)
Ash Wilson09694b92014-09-09 14:08:27 -0400141}
142
Jon Perritta33da232016-03-02 04:43:08 -0600143func v3auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
Ash Wilson09694b92014-09-09 14:08:27 -0400144 // Override the generated service endpoint with the one returned by the version endpoint.
Jon Perritt376dfce2016-02-28 23:39:09 -0600145 v3Client, err := NewIdentityV3(client, eo)
146 if err != nil {
147 return err
148 }
149
Ash Wilson09694b92014-09-09 14:08:27 -0400150 if endpoint != "" {
151 v3Client.Endpoint = endpoint
152 }
153
jrperritt69cc3372016-04-06 13:51:52 -0500154 // copy the auth options to a local variable that we can change. `options`
155 // needs to stay as-is for reauth purposes
156 v3Options := options
157
Guillaume Giamarchi08b33d52015-04-01 01:34:17 +0200158 var scope *tokens3.Scope
159 if options.TenantID != "" {
160 scope = &tokens3.Scope{
161 ProjectID: options.TenantID,
162 }
jrperritt69cc3372016-04-06 13:51:52 -0500163 v3Options.TenantID = ""
164 v3Options.TenantName = ""
Guillaume Giamarchi08b33d52015-04-01 01:34:17 +0200165 } else {
166 if options.TenantName != "" {
167 scope = &tokens3.Scope{
168 ProjectName: options.TenantName,
169 DomainID: options.DomainID,
170 DomainName: options.DomainName,
171 }
jrperritt69cc3372016-04-06 13:51:52 -0500172 v3Options.TenantName = ""
Guillaume Giamarchi08b33d52015-04-01 01:34:17 +0200173 }
174 }
175
jrperritt29ae6b32016-04-13 12:59:37 -0500176 v3Opts := tokens3.AuthOptions{
177 IdentityEndpoint: options.IdentityEndpoint,
178 Username: options.Username,
179 UserID: options.UserID,
180 Password: options.Password,
181 DomainID: options.DomainID,
182 DomainName: options.DomainName,
183 TenantID: options.TenantID,
184 TenantName: options.TenantName,
185 AllowReauth: options.AllowReauth,
186 TokenID: options.TokenID,
187 }
188
189 result := tokens3.Create(v3Client, v3Opts, scope)
Guillaume Giamarchib2663b22015-04-01 01:23:29 +0200190
191 token, err := result.ExtractToken()
Ash Wilson09694b92014-09-09 14:08:27 -0400192 if err != nil {
193 return err
194 }
Guillaume Giamarchib2663b22015-04-01 01:23:29 +0200195
196 catalog, err := result.ExtractServiceCatalog()
197 if err != nil {
198 return err
199 }
200
Ash Wilson63b2a292014-10-02 09:29:06 -0400201 client.TokenID = token.ID
Ash Wilson09694b92014-09-09 14:08:27 -0400202
Jon Perrittf4052c62015-02-14 09:48:18 -0700203 if options.AllowReauth {
Jon Perritt6fe7c402015-02-17 12:24:53 -0700204 client.ReauthFunc = func() error {
hzlouchaof6e29262015-10-27 12:51:08 +0800205 client.TokenID = ""
Jon Perritta33da232016-03-02 04:43:08 -0600206 return v3auth(client, endpoint, options, eo)
Jon Perritt6fe7c402015-02-17 12:24:53 -0700207 }
Jon Perrittf4052c62015-02-14 09:48:18 -0700208 }
Ash Wilson09694b92014-09-09 14:08:27 -0400209 client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
Guillaume Giamarchib2663b22015-04-01 01:23:29 +0200210 return V3EndpointURL(catalog, opts)
Ash Wilson09694b92014-09-09 14:08:27 -0400211 }
212
213 return nil
214}
215
Ash Wilsona87ee062014-09-03 11:26:06 -0400216// NewIdentityV2 creates a ServiceClient that may be used to interact with the v2 identity service.
Jon Perritt376dfce2016-02-28 23:39:09 -0600217func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
Ash Wilson09694b92014-09-09 14:08:27 -0400218 v2Endpoint := client.IdentityBase + "v2.0/"
Jon Perritta33da232016-03-02 04:43:08 -0600219 /*
220 eo.ApplyDefaults("identity")
221 url, err := client.EndpointLocator(eo)
222 if err != nil {
223 return nil, err
224 }
225 */
Ash Wilsonccd020b2014-09-02 10:40:54 -0400226
Ash Wilsona87ee062014-09-03 11:26:06 -0400227 return &gophercloud.ServiceClient{
Ash Wilson92c380c2014-10-22 09:14:53 -0400228 ProviderClient: client,
229 Endpoint: v2Endpoint,
Jon Perritta33da232016-03-02 04:43:08 -0600230 //Endpoint: url,
Jon Perritt376dfce2016-02-28 23:39:09 -0600231 }, nil
Ash Wilson8ba82242014-08-28 15:38:55 -0400232}
233
Ash Wilsona87ee062014-09-03 11:26:06 -0400234// NewIdentityV3 creates a ServiceClient that may be used to access the v3 identity service.
Jon Perritt376dfce2016-02-28 23:39:09 -0600235func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
Ash Wilson09694b92014-09-09 14:08:27 -0400236 v3Endpoint := client.IdentityBase + "v3/"
Jon Perritta33da232016-03-02 04:43:08 -0600237 /*
238 eo.ApplyDefaults("identity")
239 url, err := client.EndpointLocator(eo)
240 if err != nil {
241 return nil, err
242 }
243 */
Ash Wilsona87ee062014-09-03 11:26:06 -0400244
245 return &gophercloud.ServiceClient{
Ash Wilson92c380c2014-10-22 09:14:53 -0400246 ProviderClient: client,
247 Endpoint: v3Endpoint,
Jon Perritta33da232016-03-02 04:43:08 -0600248 //Endpoint: url,
Jon Perritt376dfce2016-02-28 23:39:09 -0600249 }, nil
feisky66803f02015-08-28 22:06:34 +0800250}
251
Jon Perrittbb5e9812014-10-15 17:53:44 -0500252// NewObjectStorageV1 creates a ServiceClient that may be used with the v1 object storage package.
253func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
Jon Perritt509fbb62014-09-10 13:29:56 -0500254 eo.ApplyDefaults("object-store")
255 url, err := client.EndpointLocator(eo)
Ash Wilson1cd3e692014-09-09 11:01:47 -0400256 if err != nil {
257 return nil, err
258 }
Ash Wilson92c380c2014-10-22 09:14:53 -0400259 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
Ash Wilson1cd3e692014-09-09 11:01:47 -0400260}
Ash Wilson5e57c1b2014-09-17 09:24:46 -0400261
262// NewComputeV2 creates a ServiceClient that may be used with the v2 compute package.
263func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
264 eo.ApplyDefaults("compute")
265 url, err := client.EndpointLocator(eo)
266 if err != nil {
267 return nil, err
268 }
Ash Wilson92c380c2014-10-22 09:14:53 -0400269 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
Ash Wilson5e57c1b2014-09-17 09:24:46 -0400270}
Ash Wilsonebc3d122014-09-24 13:44:05 -0400271
272// NewNetworkV2 creates a ServiceClient that may be used with the v2 network package.
Jamie Hannaford7ea29582014-09-11 15:49:46 +0200273func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
274 eo.ApplyDefaults("network")
275 url, err := client.EndpointLocator(eo)
276 if err != nil {
277 return nil, err
278 }
Ash Wilson99541ab2014-10-06 17:32:39 -0400279 return &gophercloud.ServiceClient{
Ash Wilson92c380c2014-10-22 09:14:53 -0400280 ProviderClient: client,
281 Endpoint: url,
282 ResourceBase: url + "v2.0/",
Ash Wilson99541ab2014-10-06 17:32:39 -0400283 }, nil
Jamie Hannaford7ea29582014-09-11 15:49:46 +0200284}
Jon Perrittc5ee85e2014-09-17 00:53:19 -0500285
286// NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 block storage service.
287func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
288 eo.ApplyDefaults("volume")
289 url, err := client.EndpointLocator(eo)
290 if err != nil {
291 return nil, err
292 }
Ash Wilson92c380c2014-10-22 09:14:53 -0400293 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
Jon Perrittc5ee85e2014-09-17 00:53:19 -0500294}
Jon Perrittebd18ec2015-01-16 09:13:31 -0700295
296// NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1
297// CDN service.
298func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
299 eo.ApplyDefaults("cdn")
300 url, err := client.EndpointLocator(eo)
301 if err != nil {
302 return nil, err
303 }
304 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
305}
Jon Perritt35e27e42014-12-05 11:10:46 -0700306
307// NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 orchestration service.
308func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
309 eo.ApplyDefaults("orchestration")
310 url, err := client.EndpointLocator(eo)
311 if err != nil {
312 return nil, err
313 }
314 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
315}
Jamie Hannaford05d200d2015-02-20 14:49:05 +0100316
Jamie Hannaford75e8cc42015-11-16 14:09:25 +0100317// NewDBV1 creates a ServiceClient that may be used to access the v1 DB service.
Jamie Hannaford05d200d2015-02-20 14:49:05 +0100318func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
319 eo.ApplyDefaults("database")
320 url, err := client.EndpointLocator(eo)
321 if err != nil {
322 return nil, err
323 }
324 return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
325}