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