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  }