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  }