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  }