github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/uniter/uniter.go (about) 1 // Copyright 2012-2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package uniter 5 6 import ( 7 "fmt" 8 "os" 9 "strings" 10 11 "github.com/juju/errors" 12 "github.com/juju/loggo" 13 "github.com/juju/names" 14 "github.com/juju/utils/exec" 15 "github.com/juju/utils/fslock" 16 corecharm "gopkg.in/juju/charm.v4" 17 "launchpad.net/tomb" 18 19 "github.com/juju/juju/api/uniter" 20 "github.com/juju/juju/apiserver/params" 21 "github.com/juju/juju/state/watcher" 22 "github.com/juju/juju/version" 23 "github.com/juju/juju/worker" 24 "github.com/juju/juju/worker/uniter/charm" 25 "github.com/juju/juju/worker/uniter/filter" 26 "github.com/juju/juju/worker/uniter/operation" 27 "github.com/juju/juju/worker/uniter/runner" 28 "github.com/juju/juju/worker/uniter/runner/jujuc" 29 ) 30 31 var logger = loggo.GetLogger("juju.worker.uniter") 32 33 // A UniterExecutionObserver gets the appropriate methods called when a hook 34 // is executed and either succeeds or fails. Missing hooks don't get reported 35 // in this way. 36 type UniterExecutionObserver interface { 37 HookCompleted(hookName string) 38 HookFailed(hookName string) 39 } 40 41 // Uniter implements the capabilities of the unit agent. It is not intended to 42 // implement the actual *behaviour* of the unit agent; that responsibility is 43 // delegated to Mode values, which are expected to react to events and direct 44 // the uniter's responses to them. 45 type Uniter struct { 46 tomb tomb.Tomb 47 st *uniter.State 48 paths Paths 49 f filter.Filter 50 unit *uniter.Unit 51 relations Relations 52 53 deployer *deployerProxy 54 operationFactory operation.Factory 55 operationExecutor operation.Executor 56 57 hookLock *fslock.Lock 58 runListener *RunListener 59 60 ranConfigChanged bool 61 62 // The execution observer is only used in tests at this stage. Should this 63 // need to be extended, perhaps a list of observers would be needed. 64 observer UniterExecutionObserver 65 66 // collectMetricsAt defines a function that will be used to generate signals 67 // for the collect-metrics hook. 68 collectMetricsAt CollectMetricsSignal 69 } 70 71 // NewUniter creates a new Uniter which will install, run, and upgrade 72 // a charm on behalf of the unit with the given unitTag, by executing 73 // hooks and operations provoked by changes in st. 74 func NewUniter(st *uniter.State, unitTag names.UnitTag, dataDir string, hookLock *fslock.Lock) *Uniter { 75 u := &Uniter{ 76 st: st, 77 paths: NewPaths(dataDir, unitTag), 78 hookLock: hookLock, 79 collectMetricsAt: inactiveMetricsTimer, 80 } 81 go func() { 82 defer u.tomb.Done() 83 u.tomb.Kill(u.loop(unitTag)) 84 }() 85 return u 86 } 87 88 func (u *Uniter) loop(unitTag names.UnitTag) (err error) { 89 if err := u.init(unitTag); err != nil { 90 if err == worker.ErrTerminateAgent { 91 return err 92 } 93 return fmt.Errorf("failed to initialize uniter for %q: %v", unitTag, err) 94 } 95 defer u.runListener.Close() 96 logger.Infof("unit %q started", u.unit) 97 98 // Start filtering state change events for consumption by modes. 99 u.f, err = filter.NewFilter(u.st, unitTag) 100 if err != nil { 101 return err 102 } 103 defer watcher.Stop(u.f, &u.tomb) 104 go func() { 105 u.tomb.Kill(u.f.Wait()) 106 }() 107 108 // Run modes until we encounter an error. 109 mode := ModeContinue 110 for err == nil { 111 select { 112 case <-u.tomb.Dying(): 113 err = tomb.ErrDying 114 default: 115 mode, err = mode(u) 116 switch cause := errors.Cause(err); cause { 117 case operation.ErrHookFailed: 118 mode, err = ModeHookError, nil 119 case operation.ErrNeedsReboot: 120 err = worker.ErrRebootMachine 121 case tomb.ErrDying, worker.ErrTerminateAgent: 122 err = cause 123 } 124 } 125 } 126 logger.Infof("unit %q shutting down: %s", u.unit, err) 127 return err 128 } 129 130 func (u *Uniter) setupLocks() (err error) { 131 if message := u.hookLock.Message(); u.hookLock.IsLocked() && message != "" { 132 // Look to see if it was us that held the lock before. If it was, we 133 // should be safe enough to break it, as it is likely that we died 134 // before unlocking, and have been restarted by the init system. 135 parts := strings.SplitN(message, ":", 2) 136 if len(parts) > 1 && parts[0] == u.unit.Name() { 137 if err := u.hookLock.BreakLock(); err != nil { 138 return err 139 } 140 } 141 } 142 return nil 143 } 144 145 func (u *Uniter) init(unitTag names.UnitTag) (err error) { 146 u.unit, err = u.st.Unit(unitTag) 147 if err != nil { 148 return err 149 } 150 if u.unit.Life() == params.Dead { 151 // If we started up already dead, we should not progress further. If we 152 // become Dead immediately after starting up, we may well complete any 153 // operations in progress before detecting it; but that race is fundamental 154 // and inescapable, whereas this one is not. 155 return worker.ErrTerminateAgent 156 } 157 if err = u.setupLocks(); err != nil { 158 return err 159 } 160 if err := jujuc.EnsureSymlinks(u.paths.ToolsDir); err != nil { 161 return err 162 } 163 if err := os.MkdirAll(u.paths.State.RelationsDir, 0755); err != nil { 164 return errors.Trace(err) 165 } 166 relations, err := newRelations(u.st, unitTag, u.paths, u.tomb.Dying()) 167 if err != nil { 168 return errors.Annotatef(err, "cannot create relations") 169 } 170 u.relations = relations 171 172 deployer, err := charm.NewDeployer( 173 u.paths.State.CharmDir, 174 u.paths.State.DeployerDir, 175 charm.NewBundlesDir(u.paths.State.BundlesDir), 176 ) 177 if err != nil { 178 return errors.Annotatef(err, "cannot create deployer") 179 } 180 u.deployer = &deployerProxy{deployer} 181 runnerFactory, err := runner.NewFactory( 182 u.st, unitTag, u.relations.GetInfo, u.paths, 183 ) 184 if err != nil { 185 return err 186 } 187 u.operationFactory = operation.NewFactory( 188 u.deployer, 189 runnerFactory, 190 &operationCallbacks{u}, 191 u.tomb.Dying(), 192 ) 193 194 operationExecutor, err := operation.NewExecutor( 195 u.paths.State.OperationsFile, u.getServiceCharmURL, 196 ) 197 if err != nil { 198 return err 199 } 200 u.operationExecutor = operationExecutor 201 202 logger.Debugf("starting juju-run listener on unix:%s", u.paths.Runtime.JujuRunSocket) 203 u.runListener, err = NewRunListener(u, u.paths.Runtime.JujuRunSocket) 204 if err != nil { 205 return err 206 } 207 // The socket needs to have permissions 777 in order for other users to use it. 208 if version.Current.OS != version.Windows { 209 return os.Chmod(u.paths.Runtime.JujuRunSocket, 0777) 210 } 211 return nil 212 } 213 214 func (u *Uniter) Kill() { 215 u.tomb.Kill(nil) 216 } 217 218 func (u *Uniter) Wait() error { 219 return u.tomb.Wait() 220 } 221 222 func (u *Uniter) Stop() error { 223 u.tomb.Kill(nil) 224 return u.Wait() 225 } 226 227 func (u *Uniter) Dead() <-chan struct{} { 228 return u.tomb.Dead() 229 } 230 231 func (u *Uniter) getServiceCharmURL() (*corecharm.URL, error) { 232 // TODO(fwereade): pretty sure there's no reason to make 2 API calls here. 233 service, err := u.st.Service(u.unit.ServiceTag()) 234 if err != nil { 235 return nil, err 236 } 237 charmURL, _, err := service.CharmURL() 238 return charmURL, err 239 } 240 241 func (u *Uniter) operationState() operation.State { 242 return u.operationExecutor.State() 243 } 244 245 // initializeMetricsCollector enables the periodic collect-metrics hook 246 // for charms that declare metrics. 247 func (u *Uniter) initializeMetricsCollector() error { 248 charm, err := corecharm.ReadCharmDir(u.paths.State.CharmDir) 249 if err != nil { 250 return err 251 } 252 u.collectMetricsAt = getMetricsTimer(charm) 253 return nil 254 } 255 256 // RunCommands executes the supplied commands in a hook context. 257 func (u *Uniter) RunCommands(args RunCommandsArgs) (results *exec.ExecResponse, err error) { 258 // TODO(fwereade): this is *still* all sorts of messed-up and not especially 259 // goroutine-safe, but that's not what I'm fixing at the moment. We could 260 // address this by: 261 // 1) implementing an operation to encapsulate the relations.Update call 262 // 2) (quick+dirty) mutex runOperation until we can 263 // 3) (correct) feed RunCommands requests into the mode funcs (or any queue 264 // that replaces them) such that they're handled and prioritised like 265 // every other operation. 266 logger.Tracef("run commands: %s", args.Commands) 267 268 type responseInfo struct { 269 response *exec.ExecResponse 270 err error 271 } 272 responseChan := make(chan responseInfo, 1) 273 sendResponse := func(response *exec.ExecResponse, err error) { 274 responseChan <- responseInfo{response, err} 275 } 276 277 commandArgs := operation.CommandArgs{ 278 Commands: args.Commands, 279 RelationId: args.RelationId, 280 RemoteUnitName: args.RemoteUnitName, 281 ForceRemoteUnit: args.ForceRemoteUnit, 282 } 283 err = u.runOperation(newCommandsOp(commandArgs, sendResponse)) 284 if err == nil { 285 select { 286 case response := <-responseChan: 287 results, err = response.response, response.err 288 default: 289 err = errors.New("command response never sent") 290 } 291 } 292 if errors.Cause(err) == operation.ErrNeedsReboot { 293 u.tomb.Kill(worker.ErrRebootMachine) 294 err = nil 295 } 296 if err != nil { 297 u.tomb.Kill(err) 298 } 299 return results, err 300 } 301 302 // runOperation uses the uniter's operation factory to run the supplied creation 303 // func, and then runs the resulting operation. 304 // 305 // This has a number of advantages over having mode funcs use the factory and 306 // executor directly: 307 // * it cuts down on duplicated code in the mode funcs, making the logic easier 308 // to parse 309 // * it narrows the (conceptual) interface exposed to the mode funcs -- one day 310 // we might even be able to use a (real) interface and maybe even approach a 311 // point where we can run direct unit tests(!) on the modes themselves. 312 // * it opens a path to fixing RunCommands -- all operation creation and 313 // execution is done in a single place, and it's much easier to force those 314 // onto a single thread. 315 // * this can't be done quite yet, though, because relation changes are 316 // not yet encapsulated in operations, and that needs to happen before 317 // RunCommands will *actually* be goroutine-safe. 318 func (u *Uniter) runOperation(creator creator) error { 319 op, err := creator(u.operationFactory) 320 if err != nil { 321 return errors.Annotatef(err, "cannot create operation") 322 } 323 return u.operationExecutor.Run(op) 324 }