github.com/jonaz/heapster@v1.3.0-beta.0.0.20170208112634-cd3c15ca3d29/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 opts := ksClient.opts 127 var scope *tokens.Scope 128 if opts.TenantID != "" { 129 scope = &tokens.Scope{ 130 ProjectID: opts.TenantID, 131 } 132 opts.TenantID = "" 133 opts.TenantName = "" 134 } 135 token, err := tokens.Create(ksClient.client, opts, scope).Extract() 136 if err != nil { 137 return "", err 138 } 139 ksClient.token = token 140 return token.ID, nil 141 } 142 143 // NewKeystoneClient initializes a keystone client with the provided configuration. 144 func NewKeystoneClient(config Config) (KeystoneClient, error) { 145 opts := config.AuthOptions 146 provider, err := openstack.AuthenticatedClient(opts) 147 if err != nil { 148 return nil, err 149 } 150 client := openstack.NewIdentityV3(provider) 151 // build a closure for ksClient reauthentication 152 client.ReauthFunc = func() error { 153 return openstack.AuthenticateV3(client.ProviderClient, opts) 154 } 155 return &KeystoneClientImpl{client: client, opts: opts, token: nil, monascaURL: nil}, nil 156 }