github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/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 "github.com/juju/names" 11 "github.com/juju/utils" 12 "github.com/juju/utils/set" 13 14 "github.com/juju/juju/agent" 15 apideployer "github.com/juju/juju/state/api/deployer" 16 "github.com/juju/juju/state/api/params" 17 "github.com/juju/juju/state/api/watcher" 18 "github.com/juju/juju/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 if d.deployed.Contains(unitName) { 115 if life == params.Dead { 116 if err := d.recall(unitName); err != nil { 117 return err 118 } 119 } 120 } 121 // The only units that should be deployed are those that (1) we are responsible 122 // for and (2) are Alive -- if we're responsible for a Dying unit that is not 123 // yet deployed, we should remove it immediately rather than undergo the hassle 124 // of deploying a unit agent purely so it can set itself to Dead. 125 if !d.deployed.Contains(unitName) { 126 if life == params.Alive { 127 return d.deploy(unit) 128 } else if unit != nil { 129 return d.remove(unit) 130 } 131 } 132 return nil 133 } 134 135 // deploy will deploy the supplied unit with the deployer's manager. It will 136 // panic if it observes inconsistent internal state. 137 func (d *Deployer) deploy(unit *apideployer.Unit) error { 138 unitName := unit.Name() 139 if d.deployed.Contains(unit.Name()) { 140 panic("must not re-deploy a deployed unit") 141 } 142 logger.Infof("deploying unit %q", unitName) 143 initialPassword, err := utils.RandomPassword() 144 if err != nil { 145 return err 146 } 147 if err := unit.SetPassword(initialPassword); err != nil { 148 return fmt.Errorf("cannot set password for unit %q: %v", unitName, err) 149 } 150 if err := d.ctx.DeployUnit(unitName, initialPassword); err != nil { 151 return err 152 } 153 d.deployed.Add(unitName) 154 return nil 155 } 156 157 // recall will recall the named unit with the deployer's manager. It will 158 // panic if it observes inconsistent internal state. 159 func (d *Deployer) recall(unitName string) error { 160 if !d.deployed.Contains(unitName) { 161 panic("must not recall a unit that is not deployed") 162 } 163 logger.Infof("recalling unit %q", unitName) 164 if err := d.ctx.RecallUnit(unitName); err != nil { 165 return err 166 } 167 d.deployed.Remove(unitName) 168 return nil 169 } 170 171 // remove will remove the supplied unit from state. It will panic if it 172 // observes inconsistent internal state. 173 func (d *Deployer) remove(unit *apideployer.Unit) error { 174 unitName := unit.Name() 175 if d.deployed.Contains(unitName) { 176 panic("must not remove a deployed unit") 177 } else if unit.Life() == params.Alive { 178 panic("must not remove an Alive unit") 179 } 180 logger.Infof("removing unit %q", unitName) 181 return unit.Remove() 182 } 183 184 func (d *Deployer) TearDown() error { 185 // Nothing to do here. 186 return nil 187 }