github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/names"
    11  	"github.com/juju/utils/fslock"
    12  	"gopkg.in/juju/charm.v6-unstable/hooks"
    13  	"launchpad.net/tomb"
    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  	machineLock *fslock.Lock
    28  	config      agent.Config
    29  	tag         names.UnitTag
    30  }
    31  
    32  func NewHookRunner(tag names.UnitTag, lock *fslock.Lock, config agent.Config) HookRunner {
    33  	return &hookRunner{
    34  		tag:         tag,
    35  		machineLock: lock,
    36  		config:      config,
    37  	}
    38  }
    39  
    40  // acquireExecutionLock acquires the machine-level execution lock and returns a function to be used
    41  // to unlock it.
    42  func (w *hookRunner) acquireExecutionLock(interrupt <-chan struct{}) (func() error, error) {
    43  	message := "running meter-status-changed hook"
    44  	logger.Tracef("lock: %v", message)
    45  	checkTomb := func() error {
    46  		select {
    47  		case <-interrupt:
    48  			return tomb.ErrDying
    49  		default:
    50  			return nil
    51  		}
    52  	}
    53  	message = fmt.Sprintf("%s: %s", w.tag.String(), message)
    54  	if err := w.machineLock.LockWithFunc(message, checkTomb); err != nil {
    55  		return nil, err
    56  	}
    57  	return func() error {
    58  		logger.Tracef("unlock: %v", message)
    59  		return w.machineLock.Unlock()
    60  	}, nil
    61  }
    62  
    63  func (w *hookRunner) RunHook(code, info string, interrupt <-chan struct{}) (runErr error) {
    64  	unitTag := w.tag
    65  	paths := uniter.NewPaths(w.config.DataDir(), unitTag)
    66  	ctx := NewLimitedContext(unitTag.String())
    67  	ctx.SetEnvVars(map[string]string{
    68  		"JUJU_METER_STATUS": code,
    69  		"JUJU_METER_INFO":   info,
    70  	})
    71  	r := runner.NewRunner(ctx, paths)
    72  	unlock, err := w.acquireExecutionLock(interrupt)
    73  	if err != nil {
    74  		return errors.Annotate(err, "failed to acquire machine lock")
    75  	}
    76  	defer func() {
    77  		unlockErr := unlock()
    78  		if unlockErr != nil {
    79  			logger.Criticalf("hook run resulted in error %v; unlock failure error: %v", runErr, unlockErr)
    80  		}
    81  	}()
    82  	return r.RunHook(string(hooks.MeterStatusChanged))
    83  }