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