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