Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(1055)

Delta Between Two Patch Sets: juju/api.go

Issue 52050043: juju: cache API endpoints and credentials (Closed)
Left Patch Set: juju: cache API endpoints and credentials Created 11 years, 1 month ago
Right Patch Set: juju: cache API endpoints and credentials Created 11 years, 1 month ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « [revision details] ('k') | juju/apiconn_test.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 // Copyright 2013 Canonical Ltd. 1 // Copyright 2013 Canonical Ltd.
2 // Licensed under the AGPLv3, see LICENCE file for details. 2 // Licensed under the AGPLv3, see LICENCE file for details.
3 3
4 package juju 4 package juju
5 5
6 import ( 6 import (
7 "fmt" 7 "fmt"
8 "io" 8 "io"
9 "time" 9 "time"
10 10
(...skipping 15 matching lines...) Expand all
26 // changed by tests. 26 // changed by tests.
27 var ( 27 var (
28 apiOpen = api.Open 28 apiOpen = api.Open
29 apiClose = (*api.State).Close 29 apiClose = (*api.State).Close
30 providerConnectDelay = 2 * time.Second 30 providerConnectDelay = 2 * time.Second
31 ) 31 )
32 32
33 // apiState wraps an api.State, redefining its Close method 33 // apiState wraps an api.State, redefining its Close method
34 // so we can abuse it for testing purposes. 34 // so we can abuse it for testing purposes.
35 type apiState struct { 35 type apiState struct {
36 » st *api.State 36 » st *api.State
37 » connectedInfo *api.Info 37 » // If cachedInfo is non-nil, it indicates that the info has been
38 » // fromEnviron is true if the connection was made using the 38 » // newly retrieved, and should be cached in the config store.
39 » // environment, rather than the local .jenv connection info. 39 » cachedInfo *api.Info
40 » fromEnviron bool
rog 2014/01/15 13:31:09 We could lose this field and rename connectedInfo
dimitern 2014/01/15 14:24:03 Done.
41 } 40 }
42 41
43 func (st apiState) Close() error { 42 func (st apiState) Close() error {
44 return apiClose(st.st) 43 return apiClose(st.st)
45 } 44 }
46 45
47 // APIConn holds a connection to a juju environment and its 46 // APIConn holds a connection to a juju environment and its
48 // associated state through its API interface. 47 // associated state through its API interface.
49 type APIConn struct { 48 type APIConn struct {
50 Environ environs.Environ 49 Environ environs.Environ
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
168 // Delay the config connection until we've spent 167 // Delay the config connection until we've spent
169 // some time trying to connect to the cached info. 168 // some time trying to connect to the cached info.
170 delay = providerConnectDelay 169 delay = providerConnectDelay
171 } else { 170 } else {
172 logger.Debugf("no cached API connection settings found") 171 logger.Debugf("no cached API connection settings found")
173 } 172 }
174 try.Start(func(stop <-chan struct{}) (io.Closer, error) { 173 try.Start(func(stop <-chan struct{}) (io.Closer, error) {
175 return apiConfigConnect(info, envs, envName, stop, delay) 174 return apiConfigConnect(info, envs, envName, stop, delay)
176 }) 175 })
177 try.Close() 176 try.Close()
178 » val, err := try.Result() 177 » val0, err := try.Result()
179 if err != nil { 178 if err != nil {
180 if ierr, ok := err.(*infoConnectError); ok { 179 if ierr, ok := err.(*infoConnectError); ok {
181 // lose error encapsulation: 180 // lose error encapsulation:
182 err = ierr.error 181 err = ierr.error
183 } 182 }
184 return nil, err 183 return nil, err
185 } 184 }
rog 2014/01/15 13:31:09 Please do the dynamic type conversion once only.
dimitern 2014/01/15 14:24:03 Done.
186 » connectedInfo := val.(apiState).connectedInfo 185 » val := val0.(apiState)
187 » connectedState := val.(apiState).st 186
188 » connectedFromEnviron := val.(apiState).fromEnviron 187 » if val.cachedInfo != nil && info != nil {
189 188 » » // Cache the connection settings only if we used the
190 » if connectedFromEnviron && info != nil { 189 » » // environment config, but any errors are just logged
191 » » // Cache the successful connection info for future use, but only if we 190 » » // as warnings, because they're not fatal.
rog 2014/01/15 13:31:09 How about putting the body of this into its own fu
dimitern 2014/01/15 14:24:03 Done.
192 » » // connected using the environment config. 191 » » err = cacheAPIInfo(info, val.cachedInfo)
193 » » info.SetAPIEndpoint(configstore.APIEndpoint{
194 » » » Addresses: connectedInfo.Addrs,
195 » » » CACert: string(connectedInfo.CACert),
196 » » })
197 » » _, username, err := names.ParseTag(connectedInfo.Tag, names.User TagKind)
198 if err != nil { 192 if err != nil {
199 » » » logger.Warningf("not caching API connection settings: in valid API user tag: %v", err) 193 » » » logger.Warningf(err.Error())
200 » » » return connectedState, nil 194 » » } else {
201 » » } 195 » » » logger.Debugf("updated API connection settings cache")
202 » » info.SetAPICredentials(configstore.APICredentials{ 196 » » }
203 » » » User: username, 197 » }
204 » » » Password: connectedInfo.Password, 198 » return val.st, nil
205 » » })
206 » » if err := info.Write(); err != nil {
207 » » » // Not fatal, just the cache won't be updated.
208 » » » logger.Warningf("cannot cache API connection settings: % v", err)
209 » » }
210 » » logger.Debugf("updated API connection settings cache")
211 » }
212 » return connectedState, nil
213 } 199 }
214 200
215 func errorImportance(err error) int { 201 func errorImportance(err error) int {
216 if err == nil { 202 if err == nil {
217 return 0 203 return 0
218 } 204 }
219 if errors.IsNotFoundError(err) { 205 if errors.IsNotFoundError(err) {
220 // An error from an actual connection attempt 206 // An error from an actual connection attempt
221 // is more interesting than the fact that there's 207 // is more interesting than the fact that there's
222 // no environment info available. 208 // no environment info available.
(...skipping 22 matching lines...) Expand all
245 apiInfo := &api.Info{ 231 apiInfo := &api.Info{
246 Addrs: endpoint.Addresses, 232 Addrs: endpoint.Addresses,
247 CACert: []byte(endpoint.CACert), 233 CACert: []byte(endpoint.CACert),
248 Tag: names.UserTag(info.APICredentials().User), 234 Tag: names.UserTag(info.APICredentials().User),
249 Password: info.APICredentials().Password, 235 Password: info.APICredentials().Password,
250 } 236 }
251 st, err := apiOpen(apiInfo, api.DefaultDialOpts()) 237 st, err := apiOpen(apiInfo, api.DefaultDialOpts())
252 if err != nil { 238 if err != nil {
253 return apiState{}, &infoConnectError{err} 239 return apiState{}, &infoConnectError{err}
254 } 240 }
255 » return apiState{st, apiInfo, false}, err 241 » return apiState{st, nil}, err
256 } 242 }
257 243
258 // apiConfigConnect looks for configuration info on the given environment, 244 // apiConfigConnect looks for configuration info on the given environment,
259 // and tries to use an Environ constructed from that to connect to 245 // and tries to use an Environ constructed from that to connect to
260 // its endpoint. It only starts the attempt after the given delay, 246 // its endpoint. It only starts the attempt after the given delay,
261 // to allow the faster apiInfoConnect to hopefully succeed first. 247 // to allow the faster apiInfoConnect to hopefully succeed first.
262 // It returns nil if there was no configuration information found. 248 // It returns nil if there was no configuration information found.
263 func apiConfigConnect(info configstore.EnvironInfo, envs *environs.Environs, env Name string, stop <-chan struct{}, delay time.Duration) (apiState, error) { 249 func apiConfigConnect(info configstore.EnvironInfo, envs *environs.Environs, env Name string, stop <-chan struct{}, delay time.Duration) (apiState, error) {
264 var cfg *config.Config 250 var cfg *config.Config
265 var err error 251 var err error
(...skipping 18 matching lines...) Expand all
284 } 270 }
285 apiInfo, err := environAPIInfo(environ) 271 apiInfo, err := environAPIInfo(environ)
286 if err != nil { 272 if err != nil {
287 return apiState{}, err 273 return apiState{}, err
288 } 274 }
289 st, err := apiOpen(apiInfo, api.DefaultDialOpts()) 275 st, err := apiOpen(apiInfo, api.DefaultDialOpts())
290 // TODO(rog): handle errUnauthorized when the API handles passwords. 276 // TODO(rog): handle errUnauthorized when the API handles passwords.
291 if err != nil { 277 if err != nil {
292 return apiState{}, err 278 return apiState{}, err
293 } 279 }
294 » return apiState{st, apiInfo, true}, nil 280 » return apiState{st, apiInfo}, nil
295 } 281 }
296 282
297 func environAPIInfo(environ environs.Environ) (*api.Info, error) { 283 func environAPIInfo(environ environs.Environ) (*api.Info, error) {
298 _, info, err := environ.StateInfo() 284 _, info, err := environ.StateInfo()
299 if err != nil { 285 if err != nil {
300 return nil, err 286 return nil, err
301 } 287 }
302 info.Tag = "user-admin" 288 info.Tag = "user-admin"
303 password := environ.Config().AdminSecret() 289 password := environ.Config().AdminSecret()
304 if password == "" { 290 if password == "" {
305 return nil, fmt.Errorf("cannot connect without admin-secret") 291 return nil, fmt.Errorf("cannot connect without admin-secret")
306 } 292 }
307 info.Password = password 293 info.Password = password
308 return info, nil 294 return info, nil
309 } 295 }
296
297 // cacheAPIInfo updates the local environment settings (.jenv file)
298 // with the provided apiInfo, assuming we've just successfully
299 // connected to the API server.
300 func cacheAPIInfo(info configstore.EnvironInfo, apiInfo *api.Info) error {
301 info.SetAPIEndpoint(configstore.APIEndpoint{
302 Addresses: apiInfo.Addrs,
303 CACert: string(apiInfo.CACert),
304 })
305 _, username, err := names.ParseTag(apiInfo.Tag, names.UserTagKind)
306 if err != nil {
307 return fmt.Errorf("not caching API connection settings: invalid API user tag: %v", err)
308 }
309 info.SetAPICredentials(configstore.APICredentials{
310 User: username,
311 Password: apiInfo.Password,
312 })
313 if err := info.Write(); err != nil {
314 return fmt.Errorf("cannot cache API connection settings: %v", er r)
315 }
316 return nil
317 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b