github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/meterstatus/runner.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package meterstatus 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "github.com/juju/mutex" 11 "github.com/juju/utils/clock" 12 "gopkg.in/juju/charm.v6-unstable/hooks" 13 "gopkg.in/juju/names.v2" 14 15 "github.com/juju/juju/agent" 16 "github.com/juju/juju/worker/uniter" 17 "github.com/juju/juju/worker/uniter/runner" 18 ) 19 20 // HookRunner implements the functionality necessary to run a meter-status-changed hook. 21 type HookRunner interface { 22 RunHook(string, string, <-chan struct{}) error 23 } 24 25 // hookRunner implements functionality for running a hook. 26 type hookRunner struct { 27 machineLockName string 28 config agent.Config 29 tag names.UnitTag 30 clock clock.Clock 31 } 32 33 func NewHookRunner(tag names.UnitTag, lockName string, config agent.Config, clock clock.Clock) HookRunner { 34 return &hookRunner{ 35 tag: tag, 36 machineLockName: lockName, 37 config: config, 38 clock: clock, 39 } 40 } 41 42 // acquireExecutionLock acquires the machine-level execution lock and returns a function to be used 43 // to unlock it. 44 func (w *hookRunner) acquireExecutionLock(interrupt <-chan struct{}) (mutex.Releaser, error) { 45 spec := mutex.Spec{ 46 Name: w.machineLockName, 47 Clock: w.clock, 48 Delay: 250 * time.Millisecond, 49 Cancel: interrupt, 50 } 51 logger.Debugf("acquire lock %q for meter status hook execution", w.machineLockName) 52 releaser, err := mutex.Acquire(spec) 53 if err != nil { 54 return nil, errors.Trace(err) 55 } 56 logger.Debugf("lock %q acquired", w.machineLockName) 57 return releaser, nil 58 } 59 60 func (w *hookRunner) RunHook(code, info string, interrupt <-chan struct{}) (runErr error) { 61 unitTag := w.tag 62 paths := uniter.NewPaths(w.config.DataDir(), unitTag) 63 ctx := NewLimitedContext(unitTag.String()) 64 ctx.SetEnvVars(map[string]string{ 65 "JUJU_METER_STATUS": code, 66 "JUJU_METER_INFO": info, 67 }) 68 r := runner.NewRunner(ctx, paths) 69 releaser, err := w.acquireExecutionLock(interrupt) 70 if err != nil { 71 return errors.Annotate(err, "failed to acquire machine lock") 72 } 73 // Defer the logging first so it is executed after the Release. LIFO. 74 defer logger.Debugf("release lock %q for meter status hook execution", w.machineLockName) 75 defer releaser.Release() 76 return r.RunHook(string(hooks.MeterStatusChanged)) 77 }