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