github.com/aclisp/heapster@v0.19.2-0.20160613100040-51756f899a96/metrics/sinks/monasca/keystone_client.go (about) 1 // Copyright 2015 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package monasca 16 17 import ( 18 "net/url" 19 20 "github.com/rackspace/gophercloud" 21 "github.com/rackspace/gophercloud/openstack" 22 "github.com/rackspace/gophercloud/openstack/identity/v3/endpoints" 23 "github.com/rackspace/gophercloud/openstack/identity/v3/services" 24 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens" 25 "github.com/rackspace/gophercloud/pagination" 26 ) 27 28 // KeystoneClient defines the interface of any client that can talk with Keystone. 29 type KeystoneClient interface { 30 MonascaURL() (*url.URL, error) 31 GetToken() (string, error) 32 } 33 34 // KeystoneClientImpl can authenticate with keystone and provide 35 // tokens, required for accessing the Monasca APIs. 36 type KeystoneClientImpl struct { 37 client *gophercloud.ServiceClient 38 opts gophercloud.AuthOptions 39 token *tokens.Token 40 monascaURL *url.URL 41 } 42 43 // MonascaURL Discovers the monasca service API endpoint and returns it. 44 func (ksClient *KeystoneClientImpl) MonascaURL() (*url.URL, error) { 45 if ksClient.monascaURL == nil { 46 monascaURL, err := ksClient.serviceEndpoint("monitoring", gophercloud.AvailabilityPublic) 47 if err != nil { 48 return nil, err 49 } 50 ksClient.monascaURL = monascaURL 51 } 52 return ksClient.monascaURL, nil 53 } 54 55 // discovers a single service endpoint from a given service type 56 func (ksClient *KeystoneClientImpl) serviceEndpoint(serviceType string, availability gophercloud.Availability) (*url.URL, error) { 57 serviceID, err := ksClient.serviceID(serviceType) 58 if err != nil { 59 return nil, err 60 } 61 return ksClient.endpointURL(serviceID, availability) 62 } 63 64 // finds the URL for a given service ID and availability 65 func (ksClient *KeystoneClientImpl) endpointURL(serviceID string, availability gophercloud.Availability) (*url.URL, error) { 66 opts := endpoints.ListOpts{Availability: availability, ServiceID: serviceID, PerPage: 1, Page: 1} 67 pager := endpoints.List(ksClient.client, opts) 68 endpointURL := (*url.URL)(nil) 69 err := pager.EachPage(func(page pagination.Page) (bool, error) { 70 endpointList, err := endpoints.ExtractEndpoints(page) 71 if err != nil { 72 return false, err 73 } 74 for _, e := range endpointList { 75 URL, err := url.Parse(e.URL) 76 if err != nil { 77 return false, err 78 } 79 endpointURL = URL 80 } 81 return false, nil 82 }) 83 if err != nil { 84 return nil, err 85 } 86 return endpointURL, nil 87 } 88 89 // returns the first found service ID from a given service type 90 func (ksClient *KeystoneClientImpl) serviceID(serviceType string) (string, error) { 91 opts := services.ListOpts{ServiceType: serviceType, PerPage: 1, Page: 1} 92 pager := services.List(ksClient.client, opts) 93 serviceID := "" 94 err := pager.EachPage(func(page pagination.Page) (bool, error) { 95 serviceList, err := services.ExtractServices(page) 96 if err != nil { 97 return false, err 98 } 99 for _, s := range serviceList { 100 serviceID = s.ID 101 } 102 return false, nil 103 }) 104 if err != nil { 105 return "", err 106 } 107 return serviceID, nil 108 } 109 110 // GetToken returns a valid X-Auth-Token. 111 func (ksClient *KeystoneClientImpl) GetToken() (string, error) { 112 // generate if needed 113 if ksClient.token == nil { 114 return ksClient.newToken() 115 } 116 // validate 117 valid, err := tokens.Validate(ksClient.client, ksClient.token.ID) 118 if err != nil || !valid { 119 return ksClient.newToken() 120 } 121 return ksClient.token.ID, nil 122 } 123 124 // generates a brand new Keystone token 125 func (ksClient *KeystoneClientImpl) newToken() (string, error) { 126 token, err := tokens.Create(ksClient.client, ksClient.opts, nil).Extract() 127 if err != nil { 128 return "", err 129 } 130 ksClient.token = token 131 return token.ID, nil 132 } 133 134 // NewKeystoneClient initilizes a keystone client with the provided configuration. 135 func NewKeystoneClient(config Config) (KeystoneClient, error) { 136 opts := config.AuthOptions 137 provider, err := openstack.AuthenticatedClient(opts) 138 if err != nil { 139 return nil, err 140 } 141 client := openstack.NewIdentityV3(provider) 142 // build a closure for ksClient reauthentication 143 client.ReauthFunc = func() error { 144 return openstack.AuthenticateV3(client.ProviderClient, opts) 145 } 146 return &KeystoneClientImpl{client: client, opts: opts, token: nil, monascaURL: nil}, nil 147 }