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 }