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  }