7 "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"
14 // v2 represents Keystone v2.
15 // It should never increase beyond 2.0.
18 // v3 represents Keystone v3.
19 // The version can be anything from v3 to v3.x.
24 NewClient prepares an unauthenticated ProviderClient instance.
25 Most users will probably prefer using the AuthenticatedClient function
28 This is useful if you wish to explicitly control the version of the identity
29 service that's used for authentication explicitly, for example.
31 A basic example of using this would be:
33 ao, err := openstack.AuthOptionsFromEnv()
34 provider, err := openstack.NewClient(ao.IdentityEndpoint)
35 client, err := openstack.NewIdentityV3(provider, gophercloud.EndpointOpts{})
37 func NewClient(endpoint string) (*gophercloud.ProviderClient, error) {
38 base, err := utils.BaseEndpoint(endpoint)
43 endpoint = gophercloud.NormalizeURL(endpoint)
44 base = gophercloud.NormalizeURL(base)
46 p := new(gophercloud.ProviderClient)
48 p.IdentityEndpoint = endpoint
55 AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint
56 specified by the options, acquires a token, and returns a Provider Client
57 instance that's ready to operate.
59 If the full path to a versioned identity endpoint was specified (example:
60 http://example.com:5000/v3), that path will be used as the endpoint to query.
62 If a versionless endpoint was specified (example: http://example.com:5000/),
63 the endpoint will be queried to determine which versions of the identity service
64 are available, then chooses the most recent or most supported version.
68 ao, err := openstack.AuthOptionsFromEnv()
69 provider, err := openstack.AuthenticatedClient(ao)
70 client, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{
71 Region: os.Getenv("OS_REGION_NAME"),
74 func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) {
75 client, err := NewClient(options.IdentityEndpoint)
80 err = Authenticate(client, options)
87 // Authenticate or re-authenticate against the most recent identity service
88 // supported at the provided endpoint.
89 func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
90 versions := []*utils.Version{
91 {ID: v2, Priority: 20, Suffix: "/v2.0/"},
92 {ID: v3, Priority: 30, Suffix: "/v3/"},
95 chosen, endpoint, err := utils.ChooseVersion(client, versions)
102 return v2auth(client, endpoint, options, gophercloud.EndpointOpts{})
104 return v3auth(client, endpoint, &options, gophercloud.EndpointOpts{})
106 // The switch statement must be out of date from the versions list.
107 return fmt.Errorf("Unrecognized identity version: %s", chosen.ID)
111 // AuthenticateV2 explicitly authenticates against the identity v2 endpoint.
112 func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
113 return v2auth(client, "", options, eo)
116 func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
117 v2Client, err := NewIdentityV2(client, eo)
123 v2Client.Endpoint = endpoint
126 v2Opts := tokens2.AuthOptions{
127 IdentityEndpoint: options.IdentityEndpoint,
128 Username: options.Username,
129 Password: options.Password,
130 TenantID: options.TenantID,
131 TenantName: options.TenantName,
132 AllowReauth: options.AllowReauth,
133 TokenID: options.TokenID,
136 result := tokens2.Create(v2Client, v2Opts)
138 err = client.SetTokenAndAuthResult(result)
143 catalog, err := result.ExtractServiceCatalog()
148 if options.AllowReauth {
149 // here we're creating a throw-away client (tac). it's a copy of the user's provider client, but
150 // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`,
151 // this should retry authentication only once
153 tac.SetThrowaway(true)
155 tac.SetTokenAndAuthResult(nil)
157 tao.AllowReauth = false
158 client.ReauthFunc = func() error {
159 err := v2auth(&tac, endpoint, tao, eo)
163 client.CopyTokenFrom(&tac)
167 client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
168 return V2EndpointURL(catalog, opts)
174 // AuthenticateV3 explicitly authenticates against the identity v3 service.
175 func AuthenticateV3(client *gophercloud.ProviderClient, options tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error {
176 return v3auth(client, "", options, eo)
179 func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error {
180 // Override the generated service endpoint with the one returned by the version endpoint.
181 v3Client, err := NewIdentityV3(client, eo)
187 v3Client.Endpoint = endpoint
190 result := tokens3.Create(v3Client, opts)
192 err = client.SetTokenAndAuthResult(result)
197 catalog, err := result.ExtractServiceCatalog()
202 if opts.CanReauth() {
203 // here we're creating a throw-away client (tac). it's a copy of the user's provider client, but
204 // with the token and reauth func zeroed out. combined with setting `AllowReauth` to `false`,
205 // this should retry authentication only once
207 tac.SetThrowaway(true)
209 tac.SetTokenAndAuthResult(nil)
210 var tao tokens3.AuthOptionsBuilder
211 switch ot := opts.(type) {
212 case *gophercloud.AuthOptions:
214 o.AllowReauth = false
216 case *tokens3.AuthOptions:
218 o.AllowReauth = false
223 client.ReauthFunc = func() error {
224 err := v3auth(&tac, endpoint, tao, eo)
228 client.CopyTokenFrom(&tac)
232 client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
233 return V3EndpointURL(catalog, opts)
239 // NewIdentityV2 creates a ServiceClient that may be used to interact with the
240 // v2 identity service.
241 func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
242 endpoint := client.IdentityBase + "v2.0/"
243 clientType := "identity"
245 if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) {
246 eo.ApplyDefaults(clientType)
247 endpoint, err = client.EndpointLocator(eo)
253 return &gophercloud.ServiceClient{
254 ProviderClient: client,
260 // NewIdentityV3 creates a ServiceClient that may be used to access the v3
262 func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
263 endpoint := client.IdentityBase + "v3/"
264 clientType := "identity"
266 if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) {
267 eo.ApplyDefaults(clientType)
268 endpoint, err = client.EndpointLocator(eo)
274 // Ensure endpoint still has a suffix of v3.
275 // This is because EndpointLocator might have found a versionless
276 // endpoint or the published endpoint is still /v2.0. In both
277 // cases, we need to fix the endpoint to point to /v3.
278 base, err := utils.BaseEndpoint(endpoint)
283 base = gophercloud.NormalizeURL(base)
285 endpoint = base + "v3/"
287 return &gophercloud.ServiceClient{
288 ProviderClient: client,
294 func initClientOpts(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts, clientType string) (*gophercloud.ServiceClient, error) {
295 sc := new(gophercloud.ServiceClient)
296 eo.ApplyDefaults(clientType)
297 url, err := client.EndpointLocator(eo)
301 sc.ProviderClient = client
307 // NewBareMetalV1 creates a ServiceClient that may be used with the v1
308 // bare metal package.
309 func NewBareMetalV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
310 return initClientOpts(client, eo, "baremetal")
313 // NewBareMetalIntrospectionV1 creates a ServiceClient that may be used with the v1
314 // bare metal introspection package.
315 func NewBareMetalIntrospectionV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
316 return initClientOpts(client, eo, "baremetal-inspector")
319 // NewObjectStorageV1 creates a ServiceClient that may be used with the v1
320 // object storage package.
321 func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
322 return initClientOpts(client, eo, "object-store")
325 // NewComputeV2 creates a ServiceClient that may be used with the v2 compute
327 func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
328 return initClientOpts(client, eo, "compute")
331 // NewNetworkV2 creates a ServiceClient that may be used with the v2 network
333 func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
334 sc, err := initClientOpts(client, eo, "network")
335 sc.ResourceBase = sc.Endpoint + "v2.0/"
339 // NewBlockStorageV1 creates a ServiceClient that may be used to access the v1
340 // block storage service.
341 func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
342 return initClientOpts(client, eo, "volume")
345 // NewBlockStorageV2 creates a ServiceClient that may be used to access the v2
346 // block storage service.
347 func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
348 return initClientOpts(client, eo, "volumev2")
351 // NewBlockStorageV3 creates a ServiceClient that may be used to access the v3 block storage service.
352 func NewBlockStorageV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
353 return initClientOpts(client, eo, "volumev3")
356 // NewSharedFileSystemV2 creates a ServiceClient that may be used to access the v2 shared file system service.
357 func NewSharedFileSystemV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
358 return initClientOpts(client, eo, "sharev2")
361 // NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1
363 func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
364 return initClientOpts(client, eo, "cdn")
367 // NewOrchestrationV1 creates a ServiceClient that may be used to access the v1
368 // orchestration service.
369 func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
370 return initClientOpts(client, eo, "orchestration")
373 // NewDBV1 creates a ServiceClient that may be used to access the v1 DB service.
374 func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
375 return initClientOpts(client, eo, "database")
378 // NewDNSV2 creates a ServiceClient that may be used to access the v2 DNS
380 func NewDNSV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
381 sc, err := initClientOpts(client, eo, "dns")
382 sc.ResourceBase = sc.Endpoint + "v2/"
386 // NewImageServiceV2 creates a ServiceClient that may be used to access the v2
388 func NewImageServiceV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
389 sc, err := initClientOpts(client, eo, "image")
390 sc.ResourceBase = sc.Endpoint + "v2/"
394 // NewLoadBalancerV2 creates a ServiceClient that may be used to access the v2
395 // load balancer service.
396 func NewLoadBalancerV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
397 sc, err := initClientOpts(client, eo, "load-balancer")
398 sc.ResourceBase = sc.Endpoint + "v2.0/"
402 // NewClusteringV1 creates a ServiceClient that may be used with the v1 clustering
404 func NewClusteringV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
405 return initClientOpts(client, eo, "clustering")
408 // NewMessagingV2 creates a ServiceClient that may be used with the v2 messaging
410 func NewMessagingV2(client *gophercloud.ProviderClient, clientID string, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
411 sc, err := initClientOpts(client, eo, "messaging")
412 sc.MoreHeaders = map[string]string{"Client-ID": clientID}
416 // NewContainerV1 creates a ServiceClient that may be used with v1 container package
417 func NewContainerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
418 return initClientOpts(client, eo, "container")
421 // NewKeyManagerV1 creates a ServiceClient that may be used with the v1 key
423 func NewKeyManagerV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
424 sc, err := initClientOpts(client, eo, "key-manager")
425 sc.ResourceBase = sc.Endpoint + "v1/"
429 // NewContainerInfraV1 creates a ServiceClient that may be used with the v1 container infra management
431 func NewContainerInfraV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
432 return initClientOpts(client, eo, "container-infra")
435 // NewWorkflowV2 creates a ServiceClient that may be used with the v2 workflow management package.
436 func NewWorkflowV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
437 return initClientOpts(client, eo, "workflowv2")