github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/caasmodeloperator/modeloperator.go (about) 1 // Copyright 2020 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package caasmodeloperator 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/names/v5" 9 "github.com/juju/utils/v3" 10 "github.com/juju/version/v2" 11 "github.com/juju/worker/v3/catacomb" 12 13 "github.com/juju/juju/agent" 14 "github.com/juju/juju/api/controller/caasmodeloperator" 15 "github.com/juju/juju/caas" 16 "github.com/juju/juju/core/watcher" 17 ) 18 19 type ModelOperatorAPI interface { 20 SetPassword(password string) error 21 ModelOperatorProvisioningInfo() (caasmodeloperator.ModelOperatorProvisioningInfo, error) 22 WatchModelOperatorProvisioningInfo() (watcher.NotifyWatcher, error) 23 } 24 25 // ModelOperatorBroker describes the caas broker interface needed for installing 26 // a ModelOperator into Kubernetes 27 type ModelOperatorBroker interface { 28 EnsureModelOperator(string, string, *caas.ModelOperatorConfig) error 29 ModelOperator() (*caas.ModelOperatorConfig, error) 30 ModelOperatorExists() (bool, error) 31 } 32 33 // ModelOperatorManager defines the worker used for managing model operators in 34 // caas 35 type ModelOperatorManager struct { 36 agentConfig agent.Config 37 api ModelOperatorAPI 38 broker ModelOperatorBroker 39 catacomb catacomb.Catacomb 40 logger Logger 41 modelUUID string 42 } 43 44 const ( 45 // DefaultModelOperatorPort is the default port used for the api server on 46 // the model operator 47 DefaultModelOperatorPort = 17071 48 ) 49 50 // Kill implements worker kill method 51 func (m *ModelOperatorManager) Kill() { 52 m.catacomb.Kill(nil) 53 } 54 55 // Wait implements worker Wait method 56 func (m *ModelOperatorManager) Wait() error { 57 return m.catacomb.Wait() 58 } 59 60 func (m *ModelOperatorManager) loop() error { 61 watcher, err := m.api.WatchModelOperatorProvisioningInfo() 62 if err != nil { 63 return errors.Annotate(err, "cannot watch model operator provisioning info") 64 } 65 err = m.catacomb.Add(watcher) 66 if err != nil { 67 return errors.Trace(err) 68 } 69 70 for { 71 select { 72 case <-m.catacomb.Dying(): 73 return m.catacomb.ErrDying() 74 case <-watcher.Changes(): 75 err := m.update() 76 if err != nil { 77 return errors.Annotate(err, "failed to update model operator") 78 } 79 } 80 } 81 } 82 83 func (m *ModelOperatorManager) update() error { 84 m.logger.Debugf("gathering model operator provisioning information for model %s", m.modelUUID) 85 info, err := m.api.ModelOperatorProvisioningInfo() 86 if err != nil { 87 return errors.Trace(err) 88 } 89 90 exists, err := m.broker.ModelOperatorExists() 91 if err != nil { 92 return errors.Trace(err) 93 } 94 95 setPassword := true 96 password, err := utils.RandomPassword() 97 if err != nil { 98 return errors.Trace(err) 99 } 100 if exists { 101 mo, err := m.broker.ModelOperator() 102 if err != nil { 103 return errors.Trace(err) 104 } 105 prevConf, err := agent.ParseConfigData(mo.AgentConf) 106 if err != nil { 107 return errors.Annotate(err, "cannot parse model operator agent config: ") 108 } 109 // reuse old password 110 if prevInfo, ok := prevConf.APIInfo(); ok && prevInfo.Password != "" { 111 password = prevInfo.Password 112 setPassword = false 113 } else if prevConf.OldPassword() != "" { 114 password = prevConf.OldPassword() 115 setPassword = false 116 } 117 } 118 if setPassword { 119 err := m.api.SetPassword(password) 120 if err != nil { 121 return errors.Annotate(err, "failed to set model api passwords") 122 } 123 } 124 125 agentConf, err := m.updateAgentConf(info.APIAddresses, password, info.Version) 126 if err != nil { 127 return errors.Trace(err) 128 } 129 agentConfBuf, err := agentConf.Render() 130 if err != nil { 131 return errors.Trace(err) 132 } 133 134 m.logger.Debugf("ensuring model operator deployment in kubernetes for model %s", m.modelUUID) 135 err = m.broker.EnsureModelOperator( 136 m.modelUUID, 137 m.agentConfig.DataDir(), 138 &caas.ModelOperatorConfig{ 139 AgentConf: agentConfBuf, 140 ImageDetails: info.ImageDetails, 141 Port: DefaultModelOperatorPort, 142 }, 143 ) 144 if err != nil { 145 return errors.Annotate(err, "deploying model operator") 146 } 147 148 return nil 149 } 150 151 // NewModelOperatorManager constructs a new model operator manager worker 152 func NewModelOperatorManager( 153 logger Logger, 154 api ModelOperatorAPI, 155 broker ModelOperatorBroker, 156 modelUUID string, 157 agentConfig agent.Config, 158 ) (*ModelOperatorManager, error) { 159 m := &ModelOperatorManager{ 160 agentConfig: agentConfig, 161 api: api, 162 broker: broker, 163 logger: logger, 164 modelUUID: modelUUID, 165 } 166 167 if err := catacomb.Invoke(catacomb.Plan{ 168 Site: &m.catacomb, 169 Work: m.loop, 170 }); err != nil { 171 return m, errors.Trace(err) 172 } 173 174 return m, nil 175 } 176 177 func (m *ModelOperatorManager) updateAgentConf( 178 apiAddresses []string, 179 password string, 180 ver version.Number, 181 ) (agent.ConfigSetterWriter, error) { 182 modelTag := names.NewModelTag(m.modelUUID) 183 conf, err := agent.NewAgentConfig( 184 agent.AgentConfigParams{ 185 Paths: agent.Paths{ 186 DataDir: m.agentConfig.DataDir(), 187 LogDir: m.agentConfig.LogDir(), 188 }, 189 Tag: modelTag, 190 Controller: m.agentConfig.Controller(), 191 Model: modelTag, 192 APIAddresses: apiAddresses, 193 CACert: m.agentConfig.CACert(), 194 Password: password, 195 196 // UpgradedToVersion is mandatory but not used by 197 // caas operator agents as they are not upgraded insitu. 198 UpgradedToVersion: ver, 199 }, 200 ) 201 if err != nil { 202 return nil, errors.Annotatef(err, "creating new agent config") 203 } 204 205 return conf, nil 206 }