github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/apicaller/manifold.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  
     9  	"github.com/juju/juju/agent"
    10  	"github.com/juju/juju/api"
    11  	"github.com/juju/juju/api/base"
    12  	"github.com/juju/juju/worker"
    13  	"github.com/juju/juju/worker/dependency"
    14  )
    15  
    16  // ConnectFunc is responsible for making and validating an API connection
    17  // on behalf of an agent.
    18  type ConnectFunc func(agent.Agent, api.OpenFunc) (api.Connection, error)
    19  
    20  // ManifoldConfig defines a Manifold's dependencies.
    21  type ManifoldConfig struct {
    22  
    23  	// AgentName is the name of the Agent resource that supplies
    24  	// connection information.
    25  	AgentName string
    26  
    27  	// APIConfigWatcherName identifies a resource that will be
    28  	// invalidated when api configuration changes. It's not really
    29  	// fundamental, because it's not used directly, except to create
    30  	// Inputs; it would be perfectly reasonable to wrap a Manifold
    31  	// to report an extra Input instead.
    32  	APIConfigWatcherName string
    33  
    34  	// APIOpen is passed into NewConnection, and should be used to
    35  	// create an API connection. You should probably just set it to
    36  	// the local APIOpen func.
    37  	APIOpen api.OpenFunc
    38  
    39  	// NewConnection is responsible for getting a connection from an
    40  	// agent, and may be responsible for other things that need to be
    41  	// done before anyone else gets to see the connection.
    42  	//
    43  	// You should probably set it to ScaryConnect when running a
    44  	// machine agent, and to OnlyConnect when running a model agent
    45  	// (which doesn't have its own process). Unit agents should use
    46  	// ScaryConnect at the moment; and probably switch to OnlyConnect
    47  	// when they move into machine agent processes.
    48  	NewConnection ConnectFunc
    49  
    50  	// Filter is used to specialize responses to connection errors
    51  	// made on behalf of different kinds of agent.
    52  	Filter dependency.FilterFunc
    53  }
    54  
    55  // Manifold returns a manifold whose worker wraps an API connection
    56  // made as configured.
    57  func Manifold(config ManifoldConfig) dependency.Manifold {
    58  	return dependency.Manifold{
    59  		Inputs: []string{
    60  			config.AgentName,
    61  			config.APIConfigWatcherName,
    62  		},
    63  		Output: outputFunc,
    64  		Start:  config.startFunc(),
    65  		Filter: config.Filter,
    66  	}
    67  }
    68  
    69  // startFunc returns a StartFunc that creates a connection based on the
    70  // supplied manifold config and wraps it in a worker.
    71  func (config ManifoldConfig) startFunc() dependency.StartFunc {
    72  	return func(context dependency.Context) (worker.Worker, error) {
    73  		var agent agent.Agent
    74  		if err := context.Get(config.AgentName, &agent); err != nil {
    75  			return nil, err
    76  		}
    77  
    78  		conn, err := config.NewConnection(agent, config.APIOpen)
    79  		if errors.Cause(err) == ErrChangedPassword {
    80  			return nil, dependency.ErrBounce
    81  		} else if err != nil {
    82  			return nil, errors.Annotate(err, "cannot open api")
    83  		}
    84  		return newAPIConnWorker(conn), nil
    85  	}
    86  }
    87  
    88  // outputFunc extracts an API connection from a *apiConnWorker.
    89  func outputFunc(in worker.Worker, out interface{}) error {
    90  	inWorker, _ := in.(*apiConnWorker)
    91  	if inWorker == nil {
    92  		return errors.Errorf("in should be a %T; got %T", inWorker, in)
    93  	}
    94  
    95  	switch outPointer := out.(type) {
    96  	case *base.APICaller:
    97  		*outPointer = inWorker.conn
    98  	case *api.Connection:
    99  		// Using api.Connection is strongly discouraged as consumers
   100  		// of this API connection should not be able to close it. This
   101  		// option is only available to support legacy upgrade steps.
   102  		*outPointer = inWorker.conn
   103  	default:
   104  		return errors.Errorf("out should be *base.APICaller or *api.Connection; got %T", out)
   105  	}
   106  	return nil
   107  }