launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/worker/deployer/deployer.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package deployer 5 6 import ( 7 "github.com/loggo/loggo" 8 9 "launchpad.net/errgo/errors" 10 "launchpad.net/juju-core/agent" 11 "launchpad.net/juju-core/names" 12 apideployer "launchpad.net/juju-core/state/api/deployer" 13 "launchpad.net/juju-core/state/api/params" 14 "launchpad.net/juju-core/state/api/watcher" 15 "launchpad.net/juju-core/utils" 16 "launchpad.net/juju-core/utils/set" 17 "launchpad.net/juju-core/worker" 18 ) 19 20 var logger = loggo.GetLogger("juju.worker.deployer") 21 22 var mask = errors.Mask 23 24 // Deployer is responsible for deploying and recalling unit agents, according 25 // to changes in a set of state units; and for the final removal of its agents' 26 // units from state when they are no longer needed. 27 type Deployer struct { 28 st *apideployer.State 29 ctx Context 30 deployed set.Strings 31 } 32 33 // Context abstracts away the differences between different unit deployment 34 // strategies; where a Deployer is responsible for what to deploy, a Context 35 // is responsible for how to deploy. 36 type Context interface { 37 // DeployUnit causes the agent for the specified unit to be started and run 38 // continuously until further notice without further intervention. It will 39 // return an error if the agent is already deployed. 40 DeployUnit(unitName, initialPassword string) error 41 42 // RecallUnit causes the agent for the specified unit to be stopped, and 43 // the agent's data to be destroyed. It will return an error if the agent 44 // was not deployed by the manager. 45 RecallUnit(unitName string) error 46 47 // DeployedUnits returns the names of all units deployed by the manager. 48 DeployedUnits() ([]string, error) 49 50 // AgentConfig returns the agent config for the machine agent that is 51 // running the deployer. 52 AgentConfig() agent.Config 53 } 54 55 // NewDeployer returns a Worker that deploys and recalls unit agents 56 // via ctx, taking a machine id to operate on. 57 func NewDeployer(st *apideployer.State, ctx Context) worker.Worker { 58 d := &Deployer{ 59 st: st, 60 ctx: ctx, 61 } 62 return worker.NewStringsWorker(d) 63 } 64 65 func (d *Deployer) SetUp() (watcher.StringsWatcher, error) { 66 machineTag := d.ctx.AgentConfig().Tag() 67 machine, err := d.st.Machine(machineTag) 68 if err != nil { 69 return nil, mask(err) 70 } 71 machineUnitsWatcher, err := machine.WatchUnits() 72 if err != nil { 73 return nil, mask(err) 74 } 75 76 deployed, err := d.ctx.DeployedUnits() 77 if err != nil { 78 return nil, mask(err) 79 } 80 for _, unitName := range deployed { 81 d.deployed.Add(unitName) 82 if err := d.changed(unitName); err != nil { 83 return nil, mask(err) 84 } 85 } 86 return machineUnitsWatcher, nil 87 } 88 89 func (d *Deployer) Handle(unitNames []string) error { 90 for _, unitName := range unitNames { 91 if err := d.changed(unitName); err != nil { 92 return mask(err) 93 } 94 } 95 return nil 96 } 97 98 // changed ensures that the named unit is deployed, recalled, or removed, as 99 // indicated by its state. 100 func (d *Deployer) changed(unitName string) error { 101 unitTag := names.UnitTag(unitName) 102 // Determine unit life state, and whether we're responsible for it. 103 logger.Infof("checking unit %q", unitName) 104 var life params.Life 105 unit, err := d.st.Unit(unitTag) 106 if params.IsCodeNotFoundOrCodeUnauthorized(err) { 107 life = params.Dead 108 } else if err != nil { 109 return mask(err) 110 } else { 111 life = unit.Life() 112 } 113 // Deployed units must be removed if they're Dead, or if the deployer 114 // is no longer responsible for them. 115 if d.deployed.Contains(unitName) { 116 if life == params.Dead { 117 if err := d.recall(unitName); err != nil { 118 return mask(err) 119 } 120 } 121 } 122 // The only units that should be deployed are those that (1) we are responsible 123 // for and (2) are Alive -- if we're responsible for a Dying unit that is not 124 // yet deployed, we should remove it immediately rather than undergo the hassle 125 // of deploying a unit agent purely so it can set itself to Dead. 126 if !d.deployed.Contains(unitName) { 127 if life == params.Alive { 128 return d.deploy(unit) 129 } else if unit != nil { 130 return d.remove(unit) 131 } 132 } 133 return nil 134 } 135 136 // deploy will deploy the supplied unit with the deployer's manager. It will 137 // panic if it observes inconsistent internal state. 138 func (d *Deployer) deploy(unit *apideployer.Unit) error { 139 unitName := unit.Name() 140 if d.deployed.Contains(unit.Name()) { 141 panic("must not re-deploy a deployed unit") 142 } 143 logger.Infof("deploying unit %q", unitName) 144 initialPassword, err := utils.RandomPassword() 145 if err != nil { 146 return mask(err) 147 } 148 if err := unit.SetPassword(initialPassword); err != nil { 149 return errors.Notef(err, "cannot set password for unit %q", unitName) 150 } 151 if err := d.ctx.DeployUnit(unitName, initialPassword); err != nil { 152 return mask(err) 153 } 154 d.deployed.Add(unitName) 155 return nil 156 } 157 158 // recall will recall the named unit with the deployer's manager. It will 159 // panic if it observes inconsistent internal state. 160 func (d *Deployer) recall(unitName string) error { 161 if !d.deployed.Contains(unitName) { 162 panic("must not recall a unit that is not deployed") 163 } 164 logger.Infof("recalling unit %q", unitName) 165 if err := d.ctx.RecallUnit(unitName); err != nil { 166 return mask(err) 167 } 168 d.deployed.Remove(unitName) 169 return nil 170 } 171 172 // remove will remove the supplied unit from state. It will panic if it 173 // observes inconsistent internal state. 174 func (d *Deployer) remove(unit *apideployer.Unit) error { 175 unitName := unit.Name() 176 if d.deployed.Contains(unitName) { 177 panic("must not remove a deployed unit") 178 } else if unit.Life() == params.Alive { 179 panic("must not remove an Alive unit") 180 } 181 logger.Infof("removing unit %q", unitName) 182 return unit.Remove() 183 } 184 185 func (d *Deployer) TearDown() error { 186 // Nothing to do here. 187 return nil 188 }