github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/uniter/runner/factory.go (about) 1 // Copyright 2012-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package runner 5 6 import ( 7 "github.com/juju/charm/v12" 8 "github.com/juju/errors" 9 "github.com/juju/names/v5" 10 11 "github.com/juju/juju/api/agent/uniter" 12 "github.com/juju/juju/core/actions" 13 "github.com/juju/juju/worker/common/charmrunner" 14 "github.com/juju/juju/worker/uniter/hook" 15 "github.com/juju/juju/worker/uniter/runner/context" 16 ) 17 18 // Factory represents a long-lived object that can create runners 19 // relevant to a specific unit. 20 type Factory interface { 21 22 // NewCommandRunner returns an execution context suitable for running 23 // an arbitrary script. 24 NewCommandRunner(commandInfo context.CommandInfo) (Runner, error) 25 26 // NewHookRunner returns an execution context suitable for running the 27 // supplied hook definition (which must be valid). 28 NewHookRunner(hookInfo hook.Info) (Runner, error) 29 30 // NewActionRunner returns an execution context suitable for running the action. 31 NewActionRunner(action *uniter.Action, cancel <-chan struct{}) (Runner, error) 32 } 33 34 // NewFactory returns a Factory capable of creating runners for executing 35 // charm hooks, actions and commands. 36 func NewFactory( 37 paths context.Paths, 38 contextFactory context.ContextFactory, 39 newProcessRunner NewRunnerFunc, 40 remoteExecutor ExecFunc, 41 ) ( 42 Factory, error, 43 ) { 44 f := &factory{ 45 paths: paths, 46 contextFactory: contextFactory, 47 newProcessRunner: newProcessRunner, 48 remoteExecutor: remoteExecutor, 49 } 50 51 return f, nil 52 } 53 54 type factory struct { 55 contextFactory context.ContextFactory 56 57 // Fields that shouldn't change in a factory's lifetime. 58 paths context.Paths 59 newProcessRunner NewRunnerFunc 60 remoteExecutor ExecFunc 61 } 62 63 // NewCommandRunner exists to satisfy the Factory interface. 64 func (f *factory) NewCommandRunner(commandInfo context.CommandInfo) (Runner, error) { 65 ctx, err := f.contextFactory.CommandContext(commandInfo) 66 if err != nil { 67 return nil, errors.Trace(err) 68 } 69 runner := f.newProcessRunner(ctx, f.paths, f.remoteExecutor) 70 return runner, nil 71 } 72 73 // NewHookRunner exists to satisfy the Factory interface. 74 func (f *factory) NewHookRunner(hookInfo hook.Info) (Runner, error) { 75 if err := hookInfo.Validate(); err != nil { 76 return nil, errors.Trace(err) 77 } 78 79 ctx, err := f.contextFactory.HookContext(hookInfo) 80 if err != nil { 81 return nil, errors.Trace(err) 82 } 83 runner := f.newProcessRunner(ctx, f.paths, f.remoteExecutor) 84 return runner, nil 85 } 86 87 // NewActionRunner exists to satisfy the Factory interface. 88 func (f *factory) NewActionRunner(action *uniter.Action, cancel <-chan struct{}) (Runner, error) { 89 ch, err := getCharm(f.paths.GetCharmDir()) 90 if err != nil { 91 return nil, errors.Trace(err) 92 } 93 94 name := action.Name() 95 spec, ok := actions.PredefinedActionsSpec[name] 96 if !ok { 97 var ok bool 98 spec, ok = ch.Actions().ActionSpecs[name] 99 if !ok { 100 return nil, charmrunner.NewBadActionError(name, "not defined") 101 } 102 } 103 104 params := action.Params() 105 if err := spec.ValidateParams(params); err != nil { 106 return nil, charmrunner.NewBadActionError(name, err.Error()) 107 } 108 109 tag := names.NewActionTag(action.ID()) 110 actionData := context.NewActionData(name, &tag, params, cancel) 111 ctx, err := f.contextFactory.ActionContext(actionData) 112 if err != nil { 113 return nil, charmrunner.NewBadActionError(name, err.Error()) 114 } 115 runner := f.newProcessRunner(ctx, f.paths, f.remoteExecutor) 116 return runner, nil 117 } 118 119 func getCharm(charmPath string) (charm.Charm, error) { 120 ch, err := charm.ReadCharm(charmPath) 121 if err != nil { 122 return nil, err 123 } 124 return ch, nil 125 }