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 }