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