github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/instancemutater/manifold.go (about) 1 // Copyright 2019 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package instancemutater 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/names/v5" 9 "github.com/juju/worker/v3" 10 "github.com/juju/worker/v3/dependency" 11 12 "github.com/juju/juju/agent" 13 "github.com/juju/juju/api/base" 14 "github.com/juju/juju/environs" 15 ) 16 17 //go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/worker_mock.go github.com/juju/worker/v3 Worker 18 //go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/dependency_mock.go github.com/juju/worker/v3/dependency Context 19 //go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/environs_mock.go github.com/juju/juju/environs Environ,LXDProfiler,InstanceBroker 20 //go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/base_mock.go github.com/juju/juju/api/base APICaller 21 //go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/agent_mock.go github.com/juju/juju/agent Agent,Config 22 23 // ModelManifoldConfig describes the resources used by the instancemuter worker. 24 type ModelManifoldConfig struct { 25 APICallerName string 26 EnvironName string 27 AgentName string 28 29 Logger Logger 30 NewWorker func(Config) (worker.Worker, error) 31 NewClient func(base.APICaller) InstanceMutaterAPI 32 } 33 34 // Validate validates the manifold configuration. 35 func (config ModelManifoldConfig) Validate() error { 36 if config.Logger == nil { 37 return errors.NotValidf("nil Logger") 38 } 39 if config.NewWorker == nil { 40 return errors.NotValidf("nil NewWorker") 41 } 42 if config.NewClient == nil { 43 return errors.NotValidf("nil NewClient") 44 } 45 if config.AgentName == "" { 46 return errors.NotValidf("empty AgentName") 47 } 48 if config.EnvironName == "" { 49 return errors.NotValidf("empty EnvironName") 50 } 51 if config.APICallerName == "" { 52 return errors.NotValidf("empty APICallerName") 53 } 54 return nil 55 } 56 57 func (config ModelManifoldConfig) newWorker(environ environs.Environ, apiCaller base.APICaller, agent agent.Agent) (worker.Worker, error) { 58 if err := config.Validate(); err != nil { 59 return nil, errors.Trace(err) 60 } 61 // If we don't have a LXDProfiler, we should uninstall the worker as quickly 62 // as possible. 63 broker, ok := environ.(environs.LXDProfiler) 64 if !ok { 65 // If we don't have an LXDProfiler broker, there is no need to 66 // run this worker. 67 config.Logger.Debugf("Uninstalling worker because the broker is not a LXDProfiler %T", environ) 68 return nil, dependency.ErrUninstall 69 } 70 facade := config.NewClient(apiCaller) 71 agentConfig := agent.CurrentConfig() 72 cfg := Config{ 73 Logger: config.Logger, 74 Facade: facade, 75 Broker: broker, 76 AgentConfig: agentConfig, 77 Tag: agentConfig.Tag(), 78 } 79 80 w, err := config.NewWorker(cfg) 81 if err != nil { 82 return nil, errors.Annotate(err, "cannot start model instance-mutater worker") 83 } 84 return w, nil 85 } 86 87 // ModelManifold returns a Manifold that encapsulates the instancemutater worker. 88 func ModelManifold(config ModelManifoldConfig) dependency.Manifold { 89 typedConfig := EnvironAPIConfig{ 90 EnvironName: config.EnvironName, 91 APICallerName: config.APICallerName, 92 AgentName: config.AgentName, 93 } 94 return EnvironAPIManifold(typedConfig, config.newWorker) 95 } 96 97 // EnvironAPIConfig represents a typed manifold starter func, that handles 98 // getting resources from the configuration. 99 type EnvironAPIConfig struct { 100 EnvironName string 101 APICallerName string 102 AgentName string 103 } 104 105 // EnvironAPIStartFunc encapsulates creation of a worker based on the environ 106 // and APICaller. 107 type EnvironAPIStartFunc func(environs.Environ, base.APICaller, agent.Agent) (worker.Worker, error) 108 109 // EnvironAPIManifold returns a dependency.Manifold that calls the supplied 110 // start func with the API and envrion resources defined in the config 111 // (once those resources are present). 112 func EnvironAPIManifold(config EnvironAPIConfig, start EnvironAPIStartFunc) dependency.Manifold { 113 return dependency.Manifold{ 114 Inputs: []string{ 115 config.AgentName, 116 config.APICallerName, 117 config.EnvironName, 118 }, 119 Start: func(context dependency.Context) (worker.Worker, error) { 120 var agent agent.Agent 121 if err := context.Get(config.AgentName, &agent); err != nil { 122 return nil, errors.Trace(err) 123 } 124 var environ environs.Environ 125 if err := context.Get(config.EnvironName, &environ); err != nil { 126 return nil, errors.Trace(err) 127 } 128 var apiCaller base.APICaller 129 if err := context.Get(config.APICallerName, &apiCaller); err != nil { 130 return nil, errors.Trace(err) 131 } 132 return start(environ, apiCaller, agent) 133 }, 134 } 135 } 136 137 // MachineManifoldConfig describes the resources used by the instancemuter worker. 138 type MachineManifoldConfig struct { 139 APICallerName string 140 BrokerName string 141 AgentName string 142 143 Logger Logger 144 NewWorker func(Config) (worker.Worker, error) 145 NewClient func(base.APICaller) InstanceMutaterAPI 146 } 147 148 // Validate validates the manifold configuration. 149 func (config MachineManifoldConfig) Validate() error { 150 if config.Logger == nil { 151 return errors.NotValidf("nil Logger") 152 } 153 if config.NewWorker == nil { 154 return errors.NotValidf("nil NewWorker") 155 } 156 if config.NewClient == nil { 157 return errors.NotValidf("nil NewClient") 158 } 159 if config.AgentName == "" { 160 return errors.NotValidf("empty AgentName") 161 } 162 if config.BrokerName == "" { 163 return errors.NotValidf("empty BrokerName") 164 } 165 if config.APICallerName == "" { 166 return errors.NotValidf("empty APICallerName") 167 } 168 return nil 169 } 170 171 func (config MachineManifoldConfig) newWorker(instanceBroker environs.InstanceBroker, apiCaller base.APICaller, agent agent.Agent) (worker.Worker, error) { 172 if err := config.Validate(); err != nil { 173 return nil, errors.Trace(err) 174 } 175 // If we don't have a LXDProfiler, we should uninstall the worker as quickly 176 // as possible. 177 broker, ok := instanceBroker.(environs.LXDProfiler) 178 if !ok { 179 // If we don't have an LXDProfiler broker, there is no need to 180 // run this worker. 181 config.Logger.Debugf("Uninstalling worker because the broker is not a LXDProfiler %T", instanceBroker) 182 return nil, dependency.ErrUninstall 183 } 184 agentConfig := agent.CurrentConfig() 185 tag := agentConfig.Tag() 186 if _, ok := tag.(names.MachineTag); !ok { 187 config.Logger.Warningf("cannot start a ContainerWorker on a %q, not starting", tag.Kind()) 188 return nil, dependency.ErrUninstall 189 } 190 facade := config.NewClient(apiCaller) 191 cfg := Config{ 192 Logger: config.Logger, 193 Facade: facade, 194 Broker: broker, 195 AgentConfig: agentConfig, 196 Tag: tag, 197 } 198 199 w, err := config.NewWorker(cfg) 200 if err != nil { 201 return nil, errors.Annotate(err, "cannot start machine instancemutater worker") 202 } 203 return w, nil 204 } 205 206 // MachineManifold returns a Manifold that encapsulates the instancemutater worker. 207 func MachineManifold(config MachineManifoldConfig) dependency.Manifold { 208 typedConfig := BrokerAPIConfig{ 209 BrokerName: config.BrokerName, 210 APICallerName: config.APICallerName, 211 AgentName: config.AgentName, 212 } 213 return BrokerAPIManifold(typedConfig, config.newWorker) 214 } 215 216 // BrokerAPIConfig represents a typed manifold starter func, that handles 217 // getting resources from the configuration. 218 type BrokerAPIConfig struct { 219 BrokerName string 220 APICallerName string 221 AgentName string 222 } 223 224 // BrokerAPIStartFunc encapsulates creation of a worker based on the environ 225 // and APICaller. 226 type BrokerAPIStartFunc func(environs.InstanceBroker, base.APICaller, agent.Agent) (worker.Worker, error) 227 228 // BrokerAPIManifold returns a dependency.Manifold that calls the supplied 229 // start func with the API and envrion resources defined in the config 230 // (once those resources are present). 231 func BrokerAPIManifold(config BrokerAPIConfig, start BrokerAPIStartFunc) dependency.Manifold { 232 return dependency.Manifold{ 233 Inputs: []string{ 234 config.AgentName, 235 config.APICallerName, 236 config.BrokerName, 237 }, 238 Start: func(context dependency.Context) (worker.Worker, error) { 239 var agent agent.Agent 240 if err := context.Get(config.AgentName, &agent); err != nil { 241 return nil, errors.Trace(err) 242 } 243 var broker environs.InstanceBroker 244 if err := context.Get(config.BrokerName, &broker); err != nil { 245 return nil, errors.Trace(err) 246 } 247 var apiCaller base.APICaller 248 if err := context.Get(config.APICallerName, &apiCaller); err != nil { 249 return nil, errors.Trace(err) 250 } 251 return start(broker, apiCaller, agent) 252 }, 253 } 254 }