github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/worker/apicaller/worker.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package apicaller 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "launchpad.net/tomb" 10 11 "github.com/juju/juju/api/base" 12 jujudagent "github.com/juju/juju/cmd/jujud/agent" 13 "github.com/juju/juju/worker" 14 "github.com/juju/juju/worker/agent" 15 ) 16 17 var logger = loggo.GetLogger("juju.worker.apicaller") 18 19 // Connection includes the relevant features of a *api.State, and exists primarily 20 // so that we can patch out the openConnection function in tests (and not have to 21 // return a real *State). 22 type Connection interface { 23 base.APICaller 24 Broken() <-chan struct{} 25 Close() error 26 } 27 28 // openConnection exists to be patched out in export_test.go (and let us test 29 // this component without using a real API connection). 30 var openConnection = func(agent agent.Agent) (Connection, error) { 31 currentConfig := agent.CurrentConfig() 32 st, _, err := jujudagent.OpenAPIState(currentConfig, agent) 33 if err != nil { 34 return nil, errors.Trace(err) 35 } 36 return st, nil 37 } 38 39 // newApiConnWorker returns a worker that exists for as long as the associated 40 // connection, and provides access to a base.APICaller via its manifold's Output 41 // func. If the worker is killed, the connection will be closed; and if the 42 // connection is broken, the worker will be killed. 43 func newApiConnWorker(conn Connection) (worker.Worker, error) { 44 w := &apiConnWorker{conn: conn} 45 go func() { 46 defer w.tomb.Done() 47 w.tomb.Kill(w.loop()) 48 }() 49 return w, nil 50 } 51 52 type apiConnWorker struct { 53 tomb tomb.Tomb 54 conn Connection 55 } 56 57 // Kill is part of the worker.Worker interface. 58 func (w *apiConnWorker) Kill() { 59 w.tomb.Kill(nil) 60 } 61 62 // Wait is part of the worker.Worker interface. 63 func (w *apiConnWorker) Wait() error { 64 return w.tomb.Wait() 65 } 66 67 // loop is somewhat out of the ordinary, because an api.State *does* maintain an 68 // internal workeresque heartbeat goroutine, but it doesn't implement Worker. 69 func (w *apiConnWorker) loop() (err error) { 70 // TODO(fwereade): we should make this rational at some point. 71 72 defer func() { 73 // Since we can't tell for sure what error killed the connection, any 74 // error out of Close is more important and relevant than any error we 75 // might return in the loop. 76 if closeErr := w.conn.Close(); closeErr != nil { 77 err = closeErr 78 } 79 }() 80 81 // Note that we should never return a nil error from this loop. If we're 82 // shut down deliberately we should return ErrDying, to be overwritten by 83 // any non-nil Close error and otherwise not perturb the tomb; and if the 84 // connection closes on its own there's surely *something* wrong even if 85 // there's no error reported from Close. (sample problem: *someone else* 86 // closed the conn that we're meant to be in control of). 87 for { 88 select { 89 case <-w.tomb.Dying(): 90 return tomb.ErrDying 91 case <-w.conn.Broken(): 92 return errors.New("api connection broken unexpectedly") 93 } 94 } 95 }