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