github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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/errors" 10 "github.com/juju/loggo" 11 "github.com/juju/names" 12 "github.com/juju/utils" 13 "github.com/juju/utils/set" 14 15 "github.com/juju/juju/agent" 16 apideployer "github.com/juju/juju/api/deployer" 17 "github.com/juju/juju/apiserver/params" 18 "github.com/juju/juju/watcher" 19 "github.com/juju/juju/worker" 20 ) 21 22 var logger = loggo.GetLogger("juju.worker.deployer") 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, error) { 58 d := &Deployer{ 59 st: st, 60 ctx: ctx, 61 deployed: make(set.Strings), 62 } 63 w, err := watcher.NewStringsWorker(watcher.StringsConfig{ 64 Handler: d, 65 }) 66 if err != nil { 67 return nil, errors.Trace(err) 68 } 69 return w, nil 70 } 71 72 func (d *Deployer) SetUp() (watcher.StringsWatcher, error) { 73 tag := d.ctx.AgentConfig().Tag() 74 machineTag, ok := tag.(names.MachineTag) 75 if !ok { 76 return nil, errors.Errorf("expected names.MachineTag, got %T", tag) 77 } 78 machine, err := d.st.Machine(machineTag) 79 if err != nil { 80 return nil, err 81 } 82 machineUnitsWatcher, err := machine.WatchUnits() 83 if err != nil { 84 return nil, err 85 } 86 87 deployed, err := d.ctx.DeployedUnits() 88 if err != nil { 89 return nil, err 90 } 91 for _, unitName := range deployed { 92 d.deployed.Add(unitName) 93 if err := d.changed(unitName); err != nil { 94 return nil, err 95 } 96 } 97 return machineUnitsWatcher, nil 98 } 99 100 func (d *Deployer) Handle(_ <-chan struct{}, unitNames []string) error { 101 for _, unitName := range unitNames { 102 if err := d.changed(unitName); err != nil { 103 return err 104 } 105 } 106 return nil 107 } 108 109 // changed ensures that the named unit is deployed, recalled, or removed, as 110 // indicated by its state. 111 func (d *Deployer) changed(unitName string) error { 112 unitTag := names.NewUnitTag(unitName) 113 // Determine unit life state, and whether we're responsible for it. 114 logger.Infof("checking unit %q", unitName) 115 var life params.Life 116 unit, err := d.st.Unit(unitTag) 117 if params.IsCodeNotFoundOrCodeUnauthorized(err) { 118 life = params.Dead 119 } else if err != nil { 120 return err 121 } else { 122 life = unit.Life() 123 } 124 // Deployed units must be removed if they're Dead, or if the deployer 125 // is no longer responsible for them. 126 if d.deployed.Contains(unitName) { 127 if life == params.Dead { 128 if err := d.recall(unitName); err != nil { 129 return err 130 } 131 } 132 } 133 // The only units that should be deployed are those that (1) we are responsible 134 // for and (2) are Alive -- if we're responsible for a Dying unit that is not 135 // yet deployed, we should remove it immediately rather than undergo the hassle 136 // of deploying a unit agent purely so it can set itself to Dead. 137 if !d.deployed.Contains(unitName) { 138 if life == params.Alive { 139 return d.deploy(unit) 140 } else if unit != nil { 141 return d.remove(unit) 142 } 143 } 144 return nil 145 } 146 147 // deploy will deploy the supplied unit with the deployer's manager. It will 148 // panic if it observes inconsistent internal state. 149 func (d *Deployer) deploy(unit *apideployer.Unit) error { 150 unitName := unit.Name() 151 if d.deployed.Contains(unit.Name()) { 152 panic("must not re-deploy a deployed unit") 153 } 154 logger.Infof("deploying unit %q", unitName) 155 initialPassword, err := utils.RandomPassword() 156 if err != nil { 157 return err 158 } 159 if err := unit.SetPassword(initialPassword); err != nil { 160 return fmt.Errorf("cannot set password for unit %q: %v", unitName, err) 161 } 162 if err := d.ctx.DeployUnit(unitName, initialPassword); err != nil { 163 return err 164 } 165 d.deployed.Add(unitName) 166 return nil 167 } 168 169 // recall will recall the named unit with the deployer's manager. It will 170 // panic if it observes inconsistent internal state. 171 func (d *Deployer) recall(unitName string) error { 172 if !d.deployed.Contains(unitName) { 173 panic("must not recall a unit that is not deployed") 174 } 175 logger.Infof("recalling unit %q", unitName) 176 if err := d.ctx.RecallUnit(unitName); err != nil { 177 return err 178 } 179 d.deployed.Remove(unitName) 180 return nil 181 } 182 183 // remove will remove the supplied unit from state. It will panic if it 184 // observes inconsistent internal state. 185 func (d *Deployer) remove(unit *apideployer.Unit) error { 186 unitName := unit.Name() 187 if d.deployed.Contains(unitName) { 188 panic("must not remove a deployed unit") 189 } else if unit.Life() == params.Alive { 190 panic("must not remove an Alive unit") 191 } 192 logger.Infof("removing unit %q", unitName) 193 return unit.Remove() 194 } 195 196 func (d *Deployer) TearDown() error { 197 // Nothing to do here. 198 return nil 199 }