github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/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  	"github.com/juju/charm/v12/hooks"
     8  	"github.com/juju/errors"
     9  	"github.com/juju/names/v5"
    10  
    11  	"github.com/juju/juju/agent"
    12  	"github.com/juju/juju/core/machinelock"
    13  	"github.com/juju/juju/worker/common/charmrunner"
    14  	"github.com/juju/juju/worker/uniter"
    15  	"github.com/juju/juju/worker/uniter/runner"
    16  )
    17  
    18  // HookRunner implements the functionality necessary to run a meter-status-changed hook.
    19  type HookRunner interface {
    20  	RunHook(string, string, <-chan struct{})
    21  }
    22  
    23  // hookRunner implements functionality for running a hook.
    24  type hookRunner struct {
    25  	machineLock machinelock.Lock
    26  	config      agent.Config
    27  	tag         names.UnitTag
    28  	clock       Clock
    29  	logger      Logger
    30  }
    31  
    32  // HookRunnerConfig is just an argument struct for NewHookRunner.
    33  type HookRunnerConfig struct {
    34  	MachineLock machinelock.Lock
    35  	AgentConfig agent.Config
    36  	Tag         names.UnitTag
    37  	Clock       Clock
    38  	Logger      Logger
    39  }
    40  
    41  func NewHookRunner(config HookRunnerConfig) HookRunner {
    42  	return &hookRunner{
    43  		tag:         config.Tag,
    44  		machineLock: config.MachineLock,
    45  		config:      config.AgentConfig,
    46  		clock:       config.Clock,
    47  		logger:      config.Logger,
    48  	}
    49  }
    50  
    51  // acquireExecutionLock acquires the machine-level execution lock and returns a function to be used
    52  // to unlock it.
    53  func (w *hookRunner) acquireExecutionLock(action string, interrupt <-chan struct{}) (func(), error) {
    54  	spec := machinelock.Spec{
    55  		Cancel:  interrupt,
    56  		Worker:  "meterstatus",
    57  		Comment: action,
    58  	}
    59  	releaser, err := w.machineLock.Acquire(spec)
    60  	if err != nil {
    61  		return nil, errors.Trace(err)
    62  	}
    63  	return releaser, nil
    64  }
    65  
    66  func (w *hookRunner) RunHook(code, info string, interrupt <-chan struct{}) {
    67  	unitTag := w.tag
    68  	ctx := newLimitedContext(hookConfig{
    69  		unitName: unitTag.String(),
    70  		clock:    w.clock,
    71  		logger:   w.logger,
    72  	})
    73  	ctx.SetEnvVars(map[string]string{
    74  		"JUJU_METER_STATUS": code,
    75  		"JUJU_METER_INFO":   info,
    76  	})
    77  	paths := uniter.NewPaths(w.config.DataDir(), unitTag, nil)
    78  	r := runner.NewRunner(ctx, paths, nil)
    79  	releaser, err := w.acquireExecutionLock(string(hooks.MeterStatusChanged), interrupt)
    80  	if err != nil {
    81  		w.logger.Errorf("failed to acquire machine lock: %v", err)
    82  		return
    83  	}
    84  	defer releaser()
    85  	handlerType, err := r.RunHook(string(hooks.MeterStatusChanged))
    86  	cause := errors.Cause(err)
    87  	switch {
    88  	case charmrunner.IsMissingHookError(cause):
    89  		w.logger.Infof("skipped %q hook (missing)", string(hooks.MeterStatusChanged))
    90  	case cause == runner.ErrTerminated:
    91  		w.logger.Warningf("%q hook was terminated", hooks.MeterStatusChanged)
    92  	case err != nil:
    93  		w.logger.Errorf("error running %q: %v", hooks.MeterStatusChanged, err)
    94  	default:
    95  		w.logger.Infof("ran %q hook (via %s)", hooks.MeterStatusChanged, handlerType)
    96  	}
    97  }