github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/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 "fmt" 8 9 "github.com/juju/loggo" 10 11 "launchpad.net/juju-core/agent" 12 "launchpad.net/juju-core/names" 13 apideployer "launchpad.net/juju-core/state/api/deployer" 14 "launchpad.net/juju-core/state/api/params" 15 "launchpad.net/juju-core/state/api/watcher" 16 "launchpad.net/juju-core/utils" 17 "launchpad.net/juju-core/utils/set" 18 "launchpad.net/juju-core/worker" 19 ) 20 21 var logger = loggo.GetLogger("juju.worker.deployer") 22 23 // Deployer is responsible for deploying and recalling unit agents, according 24 // to changes in a set of state units; and for the final removal of its agents' 25 // units from state when they are no longer needed. 26 type Deployer struct { 27 st *apideployer.State 28 ctx Context 29 deployed set.Strings 30 } 31 32 // Context abstracts away the differences between different unit deployment 33 // strategies; where a Deployer is responsible for what to deploy, a Context 34 // is responsible for how to deploy. 35 type Context interface { 36 // DeployUnit causes the agent for the specified unit to be started and run 37 // continuously until further notice without further intervention. It will 38 // return an error if the agent is already deployed. 39 DeployUnit(unitName, initialPassword string) error 40 41 // RecallUnit causes the agent for the specified unit to be stopped, and 42 // the agent's data to be destroyed. It will return an error if the agent 43 // was not deployed by the manager. 44 RecallUnit(unitName string) error 45 46 // DeployedUnits returns the names of all units deployed by the manager. 47 DeployedUnits() ([]string, error) 48 49 // AgentConfig returns the agent config for the machine agent that is 50 // running the deployer. 51 AgentConfig() agent.Config 52 } 53 54 // NewDeployer returns a Worker that deploys and recalls unit agents 55 // via ctx, taking a machine id to operate on. 56 func NewDeployer(st *apideployer.State, ctx Context) worker.Worker { 57 d := &Deployer{ 58 st: st, 59 ctx: ctx, 60 } 61 return worker.NewStringsWorker(d) 62 } 63 64 func (d *Deployer) SetUp() (watcher.StringsWatcher, error) { 65 machineTag := d.ctx.AgentConfig().Tag() 66 machine, err := d.st.Machine(machineTag) 67 if err != nil { 68 return nil, err 69 } 70 machineUnitsWatcher, err := machine.WatchUnits() 71 if err != nil { 72 return nil, err 73 } 74 75 deployed, err := d.ctx.DeployedUnits() 76 if err != nil { 77 return nil, err 78 } 79 for _, unitName := range deployed { 80 d.deployed.Add(unitName) 81 if err := d.changed(unitName); err != nil { 82 return nil, err 83 } 84 } 85 return machineUnitsWatcher, nil 86 } 87 88 func (d *Deployer) Handle(unitNames []string) error { 89 for _, unitName := range unitNames { 90 if err := d.changed(unitName); err != nil { 91 return err 92 } 93 } 94 return nil 95 } 96 97 // changed ensures that the named unit is deployed, recalled, or removed, as 98 // indicated by its state. 99 func (d *Deployer) changed(unitName string) error { 100 unitTag := names.UnitTag(unitName) 101 // Determine unit life state, and whether we're responsible for it. 102 logger.Infof("checking unit %q", unitName) 103 var life params.Life 104 unit, err := d.st.Unit(unitTag) 105 if params.IsCodeNotFoundOrCodeUnauthorized(err) { 106 life = params.Dead 107 } else if err != nil { 108 return err 109 } else { 110 life = unit.Life() 111 } 112 // Deployed units must be removed if they're Dead, or if the deployer 113 // is no longer responsible for them. 114 logger.Infof("Unit life is %q %q", life, params.Dead) 115 if d.deployed.Contains(unitName) { 116 if life == params.Dead || life == params.Dying { 117 if err := d.recall(unitName); err != nil { 118 return 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 err 147 } 148 if err := unit.SetPassword(initialPassword); err != nil { 149 return fmt.Errorf("cannot set password for unit %q: %v", unitName, err) 150 } 151 if err := d.ctx.DeployUnit(unitName, initialPassword); err != nil { 152 return 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 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 }