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 }