blob: 97556d6cf94b4a04e6ba1cc30f2c5a9248f2bbb9 [file] [log] [blame]
Jamie Hannaford8c072a32014-10-16 14:33:32 +02001package openstack
2
3import (
4 "fmt"
5 "net/url"
6
7 "github.com/rackspace/gophercloud"
8 tokens2 "github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
9 tokens3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens"
10 "github.com/rackspace/gophercloud/openstack/utils"
11)
12
13const (
14 v20 = "v2.0"
15 v30 = "v3.0"
16)
17
18// 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) {
23 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
31 endpoint = gophercloud.NormalizeURL(endpoint)
32 base = gophercloud.NormalizeURL(base)
33
34 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
45}
46
47// AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint specified by options, acquires a token, and
48// returns a Client instance that's ready to operate.
49// 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.
51func 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)
58 if err != nil {
59 return nil, err
60 }
61 return client, nil
62}
63
64// Authenticate or re-authenticate against the most recent identity service supported at the provided endpoint.
65func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
66 versions := []*utils.Version{
67 &utils.Version{ID: v20, Priority: 20, Suffix: "/v2.0/"},
68 &utils.Version{ID: v30, Priority: 30, Suffix: "/v3/"},
69 }
70
71 chosen, endpoint, err := utils.ChooseVersion(client.IdentityBase, client.IdentityEndpoint, versions)
72 if err != nil {
73 return err
74 }
75
76 switch chosen.ID {
77 case v20:
78 return v2auth(client, endpoint, options)
79 case v30:
80 return v3auth(client, endpoint, options)
81 default:
82 // The switch statement must be out of date from the versions list.
83 return fmt.Errorf("Unrecognized identity version: %s", chosen.ID)
84 }
85}
86
87// AuthenticateV2 explicitly authenticates against the identity v2 endpoint.
88func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
89 return v2auth(client, "", options)
90}
91
92func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions) error {
93 v2Client := NewIdentityV2(client)
94 if endpoint != "" {
95 v2Client.Endpoint = endpoint
96 }
97
98 result := tokens2.Create(v2Client, tokens2.AuthOptions{AuthOptions: options})
99
100 token, err := result.ExtractToken()
101 if err != nil {
102 return err
103 }
104
105 catalog, err := result.ExtractServiceCatalog()
106 if err != nil {
107 return err
108 }
109
110 client.TokenID = token.ID
111 client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
112 return V2EndpointURL(catalog, opts)
113 }
114
115 return nil
116}
117
118// AuthenticateV3 explicitly authenticates against the identity v3 service.
119func AuthenticateV3(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
120 return v3auth(client, "", options)
121}
122
123func v3auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions) error {
124 // Override the generated service endpoint with the one returned by the version endpoint.
125 v3Client := NewIdentityV3(client)
126 if endpoint != "" {
127 v3Client.Endpoint = endpoint
128 }
129
130 token, err := tokens3.Create(v3Client, options, nil).Extract()
131 if err != nil {
132 return err
133 }
134 client.TokenID = token.ID
135
136 client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
137 return V3EndpointURL(v3Client, opts)
138 }
139
140 return nil
141}
142
143// NewIdentityV2 creates a ServiceClient that may be used to interact with the v2 identity service.
144func NewIdentityV2(client *gophercloud.ProviderClient) *gophercloud.ServiceClient {
145 v2Endpoint := client.IdentityBase + "v2.0/"
146
147 return &gophercloud.ServiceClient{
148 Provider: client,
149 Endpoint: v2Endpoint,
150 }
151}
152
153// NewIdentityV3 creates a ServiceClient that may be used to access the v3 identity service.
154func NewIdentityV3(client *gophercloud.ProviderClient) *gophercloud.ServiceClient {
155 v3Endpoint := client.IdentityBase + "v3/"
156
157 return &gophercloud.ServiceClient{
158 Provider: client,
159 Endpoint: v3Endpoint,
160 }
161}
162
163// NewStorageV1 creates a ServiceClient that may be used with the v1 object storage package.
164func NewStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
165 eo.ApplyDefaults("object-store")
166 url, err := client.EndpointLocator(eo)
167 if err != nil {
168 return nil, err
169 }
170 return &gophercloud.ServiceClient{Provider: client, Endpoint: url}, nil
171}
172
173// NewComputeV2 creates a ServiceClient that may be used with the v2 compute package.
174func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
175 eo.ApplyDefaults("compute")
176 url, err := client.EndpointLocator(eo)
177 if err != nil {
178 return nil, err
179 }
180 return &gophercloud.ServiceClient{Provider: client, Endpoint: url}, nil
181}
182
183// NewNetworkV2 creates a ServiceClient that may be used with the v2 network package.
184func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
185 eo.ApplyDefaults("network")
186 url, err := client.EndpointLocator(eo)
187 if err != nil {
188 return nil, err
189 }
190 return &gophercloud.ServiceClient{
191 Provider: client,
192 Endpoint: url,
193 ResourceBase: url + "v2.0/",
194 }, nil
195}
196
197// NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 block storage service.
198func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
199 eo.ApplyDefaults("volume")
200 url, err := client.EndpointLocator(eo)
201 if err != nil {
202 return nil, err
203 }
204 return &gophercloud.ServiceClient{Provider: client, Endpoint: url}, nil
205}