blob: d5ad8c5260c8b9b1c2b4f6140a2d26130f8c207e [file] [log] [blame]
Ash Wilson1f110512014-10-02 15:43:47 -04001package tokens
2
3import (
Ash Wilsona8440642014-10-07 09:55:58 -04004 "fmt"
Ash Wilson1f110512014-10-02 15:43:47 -04005 "time"
6
7 "github.com/mitchellh/mapstructure"
8 "github.com/rackspace/gophercloud"
9 "github.com/rackspace/gophercloud/openstack/identity/v2/tenants"
10)
11
12// Token provides only the most basic information related to an authentication token.
13type Token struct {
14 // ID provides the primary means of identifying a user to the OpenStack API.
15 // OpenStack defines this field as an opaque value, so do not depend on its content.
16 // It is safe, however, to compare for equality.
17 ID string
18
19 // ExpiresAt provides a timestamp in ISO 8601 format, indicating when the authentication token becomes invalid.
20 // After this point in time, future API requests made using this authentication token will respond with errors.
21 // Either the caller will need to reauthenticate manually, or more preferably, the caller should exploit automatic re-authentication.
22 // See the AuthOptions structure for more details.
23 ExpiresAt time.Time
24
25 // Tenant provides information about the tenant to which this token grants access.
26 Tenant tenants.Tenant
27}
28
Ash Wilsonab48bbc2014-10-03 09:57:03 -040029// Endpoint represents a single API endpoint offered by a service.
30// It provides the public and internal URLs, if supported, along with a region specifier, again if provided.
31// The significance of the Region field will depend upon your provider.
32//
33// In addition, the interface offered by the service will have version information associated with it
34// through the VersionId, VersionInfo, and VersionList fields, if provided or supported.
35//
36// In all cases, fields which aren't supported by the provider and service combined will assume a zero-value ("").
37type Endpoint struct {
38 TenantID string `mapstructure:"tenantId"`
39 PublicURL string `mapstructure:"publicURL"`
40 InternalURL string `mapstructure:"internalURL"`
41 AdminURL string `mapstructure:"adminURL"`
42 Region string `mapstructure:"region"`
43 VersionID string `mapstructure:"versionId"`
44 VersionInfo string `mapstructure:"versionInfo"`
45 VersionList string `mapstructure:"versionList"`
46}
47
48// CatalogEntry provides a type-safe interface to an Identity API V2 service catalog listing.
49// Each class of service, such as cloud DNS or block storage services, will have a single
50// CatalogEntry representing it.
51//
52// Note: when looking for the desired service, try, whenever possible, to key off the type field.
53// Otherwise, you'll tie the representation of the service to a specific provider.
54type CatalogEntry struct {
55 // Name will contain the provider-specified name for the service.
56 Name string `mapstructure:"name"`
57
58 // Type will contain a type string if OpenStack defines a type for the service.
59 // Otherwise, for provider-specific services, the provider may assign their own type strings.
60 Type string `mapstructure:"type"`
61
62 // Endpoints will let the caller iterate over all the different endpoints that may exist for
63 // the service.
64 Endpoints []Endpoint `mapstructure:"endpoints"`
65}
66
67// ServiceCatalog provides a view into the service catalog from a previous, successful authentication.
68type ServiceCatalog struct {
69 Entries []CatalogEntry
70}
71
Ash Wilson1f110512014-10-02 15:43:47 -040072// CreateResult defers the interpretation of a created token.
73// Use ExtractToken() to interpret it as a Token, or ExtractServiceCatalog() to interpret it as a service catalog.
74type CreateResult struct {
75 gophercloud.CommonResult
76}
77
78// ExtractToken returns the just-created Token from a CreateResult.
79func (result CreateResult) ExtractToken() (*Token, error) {
Ash Wilson27d29e22014-10-03 11:57:14 -040080 if result.Err != nil {
81 return nil, result.Err
82 }
83
Ash Wilson1f110512014-10-02 15:43:47 -040084 var response struct {
85 Access struct {
86 Token struct {
87 Expires string `mapstructure:"expires"`
88 ID string `mapstructure:"id"`
89 Tenant tenants.Tenant `mapstructure:"tenant"`
90 } `mapstructure:"token"`
91 } `mapstructure:"access"`
92 }
93
94 err := mapstructure.Decode(result.Resp, &response)
95 if err != nil {
96 return nil, err
97 }
98
99 expiresTs, err := time.Parse(gophercloud.RFC3339Milli, response.Access.Token.Expires)
100 if err != nil {
101 return nil, err
102 }
103
104 return &Token{
105 ID: response.Access.Token.ID,
106 ExpiresAt: expiresTs,
107 Tenant: response.Access.Token.Tenant,
108 }, nil
109}
110
Ash Wilsonab48bbc2014-10-03 09:57:03 -0400111// ExtractServiceCatalog returns the ServiceCatalog that was generated along with the user's Token.
112func (result CreateResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
Ash Wilson27d29e22014-10-03 11:57:14 -0400113 if result.Err != nil {
114 return nil, result.Err
115 }
116
Ash Wilsonab48bbc2014-10-03 09:57:03 -0400117 var response struct {
118 Access struct {
119 Entries []CatalogEntry `mapstructure:"serviceCatalog"`
120 } `mapstructure:"access"`
121 }
122
123 err := mapstructure.Decode(result.Resp, &response)
124 if err != nil {
125 return nil, err
126 }
127
128 return &ServiceCatalog{Entries: response.Access.Entries}, nil
129}
130
Ash Wilson1f110512014-10-02 15:43:47 -0400131// createErr quickly packs an error in a CreateResult.
132func createErr(err error) CreateResult {
133 return CreateResult{gophercloud.CommonResult{Err: err}}
134}
Ash Wilsona8440642014-10-07 09:55:58 -0400135
Ash Wilson405f3102014-10-07 10:21:46 -0400136// EndpointURL discovers the endpoint URL for a specific service with a ServiceCatalog. The
137// specified EndpointOpts are used to identify a unique, unambiguous endpoint to return. The minimum
138// that can be specified is a Type, but you will also often need to specify a Name and/or a Region
139// depending on what's available on your OpenStack deployment.
140func (catalog *ServiceCatalog) EndpointURL(opts gophercloud.EndpointOpts) (string, error) {
Ash Wilsona8440642014-10-07 09:55:58 -0400141 // Extract Endpoints from the catalog entries that match the requested Type, Name if provided, and Region if provided.
142 var endpoints = make([]Endpoint, 0, 1)
143 for _, entry := range catalog.Entries {
144 if (entry.Type == opts.Type) && (opts.Name == "" || entry.Name == opts.Name) {
145 for _, endpoint := range entry.Endpoints {
146 if opts.Region == "" || endpoint.Region == opts.Region {
147 endpoints = append(endpoints, endpoint)
148 }
149 }
150 }
151 }
152
153 // Report an error if the options were ambiguous.
154 if len(endpoints) == 0 {
155 return "", gophercloud.ErrEndpointNotFound
156 }
157 if len(endpoints) > 1 {
158 return "", fmt.Errorf("Discovered %d matching endpoints: %#v", len(endpoints), endpoints)
159 }
160
161 // Extract the appropriate URL from the matching Endpoint.
162 for _, endpoint := range endpoints {
163 switch opts.Availability {
164 case gophercloud.AvailabilityPublic:
165 return gophercloud.NormalizeURL(endpoint.PublicURL), nil
166 case gophercloud.AvailabilityInternal:
167 return gophercloud.NormalizeURL(endpoint.InternalURL), nil
168 case gophercloud.AvailabilityAdmin:
169 return gophercloud.NormalizeURL(endpoint.AdminURL), nil
170 default:
171 return "", fmt.Errorf("Unexpected availability in endpoint query: %s", opts.Availability)
172 }
173 }
174
175 return "", gophercloud.ErrEndpointNotFound
176}