github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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/tomb.v1"
    10  
    11  	"github.com/juju/juju/api"
    12  	"github.com/juju/juju/worker"
    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  	go func() {
    28  		defer w.tomb.Done()
    29  		w.tomb.Kill(w.loop())
    30  	}()
    31  	return w
    32  }
    33  
    34  type apiConnWorker struct {
    35  	tomb tomb.Tomb
    36  	conn api.Connection
    37  }
    38  
    39  // Kill is part of the worker.Worker interface.
    40  func (w *apiConnWorker) Kill() {
    41  	w.tomb.Kill(nil)
    42  }
    43  
    44  // Wait is part of the worker.Worker interface.
    45  func (w *apiConnWorker) Wait() error {
    46  	return w.tomb.Wait()
    47  }
    48  
    49  // loop is somewhat out of the ordinary, because an api.Connection
    50  // *does* maintain an internal workeresque heartbeat goroutine, but it
    51  // doesn't implement Worker.
    52  func (w *apiConnWorker) loop() (err error) {
    53  	// TODO(fwereade): we should make this rational at some point.
    54  
    55  	defer func() {
    56  		// Since we can't tell for sure what error killed the connection, any
    57  		// error out of Close is more important and relevant than any error we
    58  		// might return in the loop.
    59  		if closeErr := w.conn.Close(); closeErr != nil {
    60  			err = closeErr
    61  		}
    62  	}()
    63  
    64  	// Note that we should never return a nil error from this loop. If we're
    65  	// shut down deliberately we should return ErrDying, to be overwritten by
    66  	// any non-nil Close error and otherwise not perturb the tomb; and if the
    67  	// connection closes on its own there's surely *something* wrong even if
    68  	// there's no error reported from Close. (sample problem: *someone else*
    69  	// closed the conn that we're meant to be in control of).
    70  	for {
    71  		select {
    72  		case <-w.tomb.Dying():
    73  			return tomb.ErrDying
    74  		case <-w.conn.Broken():
    75  			return errors.New("api connection broken unexpectedly")
    76  		}
    77  	}
    78  }