github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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  	"gopkg.in/juju/worker.v1"
     9  	"gopkg.in/juju/worker.v1/dependency"
    10  
    11  	"github.com/juju/juju/agent"
    12  	"github.com/juju/juju/api"
    13  	"github.com/juju/juju/api/base"
    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  	inputs := []string{config.AgentName}
    59  	if config.APIConfigWatcherName != "" {
    60  		// config.APIConfigWatcherName is only applicable to unit
    61  		// and machine scoped manifold.
    62  		// It will be empty for model manifolds.
    63  		inputs = append(inputs, config.APIConfigWatcherName)
    64  	}
    65  	return dependency.Manifold{
    66  		Inputs: inputs,
    67  		Output: outputFunc,
    68  		Start:  config.startFunc(),
    69  		Filter: config.Filter,
    70  	}
    71  }
    72  
    73  // startFunc returns a StartFunc that creates a connection based on the
    74  // supplied manifold config and wraps it in a worker.
    75  func (config ManifoldConfig) startFunc() dependency.StartFunc {
    76  	return func(context dependency.Context) (worker.Worker, error) {
    77  		var agent agent.Agent
    78  		if err := context.Get(config.AgentName, &agent); err != nil {
    79  			return nil, err
    80  		}
    81  
    82  		conn, err := config.NewConnection(agent, config.APIOpen)
    83  		if errors.Cause(err) == ErrChangedPassword {
    84  			return nil, dependency.ErrBounce
    85  		} else if err != nil {
    86  			cfg := agent.CurrentConfig()
    87  			return nil, errors.Annotatef(err, "[%s] %q cannot open api",
    88  				shortModelUUID(cfg.Model()), cfg.Tag().String())
    89  		}
    90  		return newAPIConnWorker(conn), nil
    91  	}
    92  }
    93  
    94  // outputFunc extracts an API connection from a *apiConnWorker.
    95  func outputFunc(in worker.Worker, out interface{}) error {
    96  	inWorker, _ := in.(*apiConnWorker)
    97  	if inWorker == nil {
    98  		return errors.Errorf("in should be a %T; got %T", inWorker, in)
    99  	}
   100  
   101  	switch outPointer := out.(type) {
   102  	case *base.APICaller:
   103  		*outPointer = inWorker.conn
   104  	case *api.Connection:
   105  		// Using api.Connection is strongly discouraged as consumers
   106  		// of this API connection should not be able to close it. This
   107  		// option is only available to support legacy upgrade steps.
   108  		*outPointer = inWorker.conn
   109  	default:
   110  		return errors.Errorf("out should be *base.APICaller or *api.Connection; got %T", out)
   111  	}
   112  	return nil
   113  }