executor¶
Package executor provides the execution engine for mooncake configuration steps.
Package executor implements the core execution engine for mooncake configuration plans.
The executor is responsible for:
- Loading and validating configuration plans
- Expanding steps (loops, includes, presets)
- Evaluating conditions (when, unless, creates)
- Dispatching actions to handlers
- Managing execution context and variables
- Tracking results and statistics
- Emitting events for observability
- Handling dry-run mode
- Supporting privilege escalation (sudo/become)
Architecture¶
The executor follows a pipeline architecture:
Each step goes through:
- Pre-execution: Check when/unless/creates, apply tags filter
- Variable processing: Merge step vars into context
- Loop expansion: Expand with_items/with_filetree into multiple executions
- Action execution: Dispatch to handler or legacy implementation
- Post-execution: Evaluate changed_when/failed_when, register results
- Event emission: Publish lifecycle events
Execution Context¶
ExecutionContext carries all state needed during execution:
- Variables: Step vars, global vars, facts, registered results
- Template: Jinja2-like template renderer
- Evaluator: Expression evaluator for conditions
- Logger: Structured logging (TUI or text)
- PathUtil: Path resolution and expansion
- EventPublisher: Event emission for observability
- Stats: Execution statistics (total, success, failed, changed, skipped)
Action Dispatch¶
Actions are dispatched through two paths:
- Handler-based (new): Look up handler in actions.Registry, call handler.Execute()
- Legacy: Direct executor methods (HandleShell, HandleFile, etc.)
The executor prefers handlers when available, falling back to legacy for non-migrated actions.
Idempotency¶
The executor enforces idempotency through:
- creates: Skip if path exists
- unless: Skip if command succeeds
- changed_when: Custom change detection
- Handler implementations: Built-in state checking
Dry\-Run Mode¶
When DryRun is true:
- No actual changes are made to the system
- Handlers log what would happen
- Template rendering still occurs (validates syntax)
- File existence checks are performed (read-only)
- Statistics track what would have changed
Error Handling¶
Errors are wrapped with context using custom error types:
- RenderError: Template rendering failures (field + cause)
- EvaluationError: Expression evaluation failures (expression + cause)
- CommandError: Command execution failures (command + exit code)
- FileOperationError: File operation failures (path + operation + cause)
- StepValidationError: Configuration validation failures
- SetupError: Infrastructure/environment setup failures
Use errors.Is() and errors.As() for programmatic error inspection.
Usage Example¶
// Load configuration
steps, err := config.ReadConfig("config.yml")
if err != nil {
return err
}
// Create executor
log := logger.NewTextLogger()
exec := NewExecutor(log)
// Execute with options
result, err := exec.Execute(config.Plan{Steps: steps}, ExecuteOptions{
DryRun: false,
Tags: []string{"setup", "deploy"},
Variables: map[string]interface{}{
"environment": "production",
},
})
// Check results
if !result.Success {
log.Errorf("Execution failed: %d failed steps", result.FailedSteps)
}
log.Infof("Summary: %d changed, %d unchanged, %d failed",
result.ChangedSteps, result.SuccessSteps-result.ChangedSteps, result.FailedSteps)
Index¶
- func AddGlobalVariables(variables map[string]interface{})
- func CheckIdempotencyConditions(step config.Step, ec *ExecutionContext) (bool, string, error)
- func CheckSkipConditions(step config.Step, ec *ExecutionContext) (bool, string, error)
- func DispatchStepAction(step config.Step, ec *ExecutionContext) error
- func ExecutePlan(p *plan.Plan, sudoPass string, dryRun bool, log logger.Logger, publisher events.Publisher) error
- func ExecuteStep(step config.Step, ec *ExecutionContext) error
- func ExecuteSteps(steps []config.Step, ec *ExecutionContext) error
- func GetStepDisplayName(step config.Step, ec *ExecutionContext) (string, bool)
- func HandleVars(step config.Step, ec *ExecutionContext) error
- func HandleWhenExpression(step config.Step, ec *ExecutionContext) (bool, error)
- func MarkStepFailed(result *Result, step config.Step, ec *ExecutionContext)
- func ParseFileMode(modeStr string, defaultMode os.FileMode) os.FileMode
- func ShouldSkipByTags(step config.Step, ec *ExecutionContext) bool
- func Start(StartConfig StartConfig, log logger.Logger, publisher events.Publisher) error
- type AssertionError
- func (e *AssertionError) Error() string
- func (e *AssertionError) Unwrap() error
- type CommandError
- func (e *CommandError) Error() string
- func (e *CommandError) Unwrap() error
- type EvaluationError
- func (e *EvaluationError) Error() string
- func (e *EvaluationError) Unwrap() error
- type ExecutionContext
- func (ec *ExecutionContext) Clone() ExecutionContext
- func (ec *ExecutionContext) EmitEvent(eventType events.EventType, data interface{})
- func (ec *ExecutionContext) GetCurrentStepID() string
- func (ec *ExecutionContext) GetEvaluator() expression.Evaluator
- func (ec *ExecutionContext) GetEventPublisher() events.Publisher
- func (ec *ExecutionContext) GetLogger() logger.Logger
- func (ec *ExecutionContext) GetTemplate() template.Renderer
- func (ec *ExecutionContext) GetVariables() map[string]interface{}
- func (ec *ExecutionContext) HandleDryRun(logFn func(*dryRunLogger)) bool
- func (ec *ExecutionContext) IsDryRun() bool
- type ExecutionStats
- func NewExecutionStats() *ExecutionStats
- type FileOperationError
- func (e *FileOperationError) Error() string
- func (e *FileOperationError) Unwrap() error
- type RenderError
- func (e *RenderError) Error() string
- func (e *RenderError) Unwrap() error
- type Result
- func NewResult() *Result
- func (r *Result) RegisterTo(variables map[string]interface{}, name string)
- func (r *Result) SetChanged(changed bool)
- func (r *Result) SetData(data map[string]interface{})
- func (r *Result) SetFailed(failed bool)
- func (r *Result) SetStderr(stderr string)
- func (r *Result) SetStdout(stdout string)
- func (r *Result) Status() string
- func (r *Result) ToMap() map[string]interface{}
- type SetupError
- func (e *SetupError) Error() string
- func (e *SetupError) Unwrap() error
- type StartConfig
- type StepValidationError
- func (e *StepValidationError) Error() string
func AddGlobalVariables¶
AddGlobalVariables injects system facts into the variables map. This makes facts like ansible_os_family, ansible_distribution, etc. available during planning.
func CheckIdempotencyConditions¶
CheckIdempotencyConditions evaluates creates and unless conditions for shell steps. Returns (shouldSkip bool, reason string, error)
INTERNAL: This function is exported for testing purposes only and is not part of the public API. It may change or be removed in future versions without notice.
func CheckSkipConditions¶
CheckSkipConditions evaluates whether a step should be skipped based on conditional expressions and tag filters.
It first evaluates the step's "when" condition (if present), which is an expression that must evaluate to true for the step to execute. If the condition evaluates to false, the step is skipped with reason "when".
Next, it checks if the step should be skipped based on tag filtering. If the execution context has a tags filter and the step's tags don't match, it's skipped with reason "tags".
Returns:
- shouldSkip: true if the step should be skipped
- skipReason: "when" or "tags" indicating why the step was skipped (empty if not skipped)
- error: any error encountered while evaluating conditions
INTERNAL: This function is exported for testing purposes only and is not part of the public API. It may change or be removed in future versions without notice.
func DispatchStepAction¶
DispatchStepAction executes the appropriate handler based on step type. All actions are now handled through the actions registry.
INTERNAL: This function is exported for testing purposes only and is not part of the public API. It may change or be removed in future versions without notice.
func ExecutePlan¶
func ExecutePlan(p *plan.Plan, sudoPass string, dryRun bool, log logger.Logger, publisher events.Publisher) error
ExecutePlan executes a pre-compiled plan. Emits events through the provided publisher for all execution progress.
func ExecuteStep¶
ExecuteStep executes a single configuration step within the given execution context.
func ExecuteSteps¶
ExecuteSteps executes a sequence of configuration steps within the given execution context.
func GetStepDisplayName¶
GetStepDisplayName determines the display name to show for a step in logs and output.
The function follows a priority order to determine the name:
- If executing within a with_filetree loop, uses action + destination path
- If executing within a with_items loop, uses the string representation of the item
- Otherwise, uses the step's configured Name field
Returns:
- displayName: the name to display for this step
- hasName: true if a name was found, false if the step is anonymous
INTERNAL: This function is exported for testing purposes only and is not part of the public API. It may change or be removed in future versions without notice.
func HandleVars¶
HandleVars processes the vars field of a step, rendering templates and merging into the execution context.
INTERNAL: This function is exported for testing purposes only and is not part of the public API. It may change or be removed in future versions without notice.
func HandleWhenExpression¶
HandleWhenExpression evaluates the when condition and returns whether the step should be skipped. Returns (shouldSkip bool, error).
INTERNAL: This function is exported for testing purposes only and is not part of the public API. It may change or be removed in future versions without notice.
func MarkStepFailed¶
MarkStepFailed marks a result as failed and registers it if needed. The caller is responsible for returning an appropriate error.
INTERNAL: This function is exported for testing purposes only and is not part of the public API. It may change or be removed in future versions without notice.
func ParseFileMode¶
ParseFileMode parses a mode string (e.g., "0644") into os.FileMode. Returns default mode if mode is empty or invalid.
INTERNAL: This function is exported for testing purposes only and is not part of the public API. It may change or be removed in future versions without notice.
func ShouldSkipByTags¶
ShouldSkipByTags determines if a step should be skipped based on tag filtering. Returns true if the step should be skipped, false otherwise.
INTERNAL: This function is exported for testing purposes only and is not part of the public API. It may change or be removed in future versions without notice.
func Start¶
Start begins execution of a mooncake configuration with the given settings. Always goes through the planner to expand loops, includes, and variables. Emits events through the provided publisher for all execution progress.
type AssertionError¶
AssertionError represents an assertion verification failure. Unlike other errors, assertions are expected to fail when conditions aren't met.
type AssertionError struct {
Type string // "command", "file", "http"
Expected string // What was expected
Actual string // What was found
Details string // Additional context (optional)
Cause error // Underlying error (optional)
}
func (*AssertionError) Error¶
func (*AssertionError) Unwrap¶
type CommandError¶
CommandError represents a command execution failure
type CommandError struct {
ExitCode int
Timeout bool
Duration string
Cause error // Optional underlying error (e.g., exec.ExitError, OS errors)
}
func (*CommandError) Error¶
func (*CommandError) Unwrap¶
type EvaluationError¶
EvaluationError represents an expression evaluation failure
func (*EvaluationError) Error¶
func (*EvaluationError) Unwrap¶
type ExecutionContext¶
ExecutionContext holds all state needed to execute a step or sequence of steps.
The context is designed to be copied when entering nested execution scopes (includes, loops). Most fields are copied by value, but certain fields use pointers to maintain shared state across the entire execution tree.
Field categories:
- Configuration: Variables, CurrentDir, CurrentFile (copied on nested contexts)
- Display state: Level, CurrentIndex, TotalSteps (modified for each scope)
- Execution settings: Logger, SudoPass, Tags, DryRun (shared across contexts)
- Global counters: Pointers that accumulate across all contexts
- Dependencies: Shared service instances
type ExecutionContext struct {
// Variables contains template variables available to steps.
// Copied on context copy so nested contexts can have their own variables (e.g., loop items).
Variables map[string]interface{}
// CurrentDir is the directory containing the current config file.
// Used for resolving relative paths in include, template src, etc.
CurrentDir string
// CurrentFile is the absolute path to the current config file being executed.
// Used for error messages and debugging.
CurrentFile string
// Level tracks nesting depth for display indentation.
// 0 = root config, increments by 1 for each include or loop level.
Level int
// CurrentIndex is the 0-based index of the current step within the current scope.
// Resets to 0 when entering includes or loops.
CurrentIndex int
// TotalSteps is the number of steps in the current execution scope.
// Updated when entering includes or loops to reflect the new scope size.
TotalSteps int
// Logger handles all output, configured with padding based on Level.
Logger logger.Logger
// SudoPass is the password used for steps with become: true.
// Empty string if not provided via --sudo-pass flag.
SudoPass string
// Tags filters which steps execute (empty = all steps execute).
// Steps without matching tags are skipped when this is non-empty.
Tags []string
// DryRun when true prevents any system changes (preview mode).
// Commands are not executed, files are not created, templates are not rendered.
DryRun bool
// Stats holds shared execution statistics counters.
// SHARED via pointer - all contexts update the same counters.
Stats *ExecutionStats
// Template renders template strings with variable substitution.
// SHARED across all contexts - same instance used everywhere.
Template template.Renderer
// Evaluator evaluates when condition expressions.
// SHARED across all contexts - same instance used everywhere.
Evaluator expression.Evaluator
// PathUtil expands paths with tilde and variable substitution.
// SHARED across all contexts - same instance used everywhere.
PathUtil *pathutil.PathExpander
// FileTree walks directory trees for with_filetree.
// SHARED across all contexts - same instance used everywhere.
FileTree *filetree.Walker
// Redactor redacts sensitive values (passwords) from log output.
// SHARED across all contexts - same instance used everywhere.
Redactor *security.Redactor
// EventPublisher publishes execution events to subscribers.
// SHARED across all contexts - same instance used everywhere.
EventPublisher events.Publisher
// CurrentStepID is the unique identifier for the currently executing step.
// Used for correlating events from the same step execution.
CurrentStepID string
// CurrentResult holds the result of the currently executing step.
// Handlers should set this to provide result data to event emission.
CurrentResult *Result
}
func (*ExecutionContext) Clone¶
Clone creates a new ExecutionContext for a nested execution scope (include or loop). Variables map is shallow copied, display fields are copied by value, and pointer fields remain shared across all contexts.
func (*ExecutionContext) EmitEvent¶
EmitEvent publishes an event to all subscribers
func (*ExecutionContext) GetCurrentStepID¶
GetCurrentStepID returns the current step ID.
func (*ExecutionContext) GetEvaluator¶
GetEvaluator returns the expression evaluator.
func (*ExecutionContext) GetEventPublisher¶
GetEventPublisher returns the event publisher.
func (*ExecutionContext) GetLogger¶
GetLogger returns the logger.
func (*ExecutionContext) GetTemplate¶
GetTemplate returns the template renderer.
func (*ExecutionContext) GetVariables¶
GetVariables returns the execution variables.
func (*ExecutionContext) HandleDryRun¶
HandleDryRun executes dry-run logging if in dry-run mode. Returns true if in dry-run mode (caller should return early). The logFn is called with a dryRunLogger to perform logging.
func (*ExecutionContext) IsDryRun¶
IsDryRun returns true if this is a dry-run execution.
type ExecutionStats¶
ExecutionStats holds shared statistics counters for execution tracking. All fields are pointers to enable shared state across nested execution contexts.
type ExecutionStats struct {
// Global tracks total non-skipped steps across the entire execution tree
Global *int
// Executed counts successfully completed steps
Executed *int
// Skipped counts steps skipped due to when conditions or tag filtering
Skipped *int
// Failed counts steps that failed with errors
Failed *int
}
func NewExecutionStats¶
NewExecutionStats creates a new ExecutionStats with all counters initialized to zero
type FileOperationError¶
FileOperationError represents a file operation failure
type FileOperationError struct {
Operation string // "create", "read", "write", "delete", "chmod", "chown", "link"
Path string
Cause error
}
func (*FileOperationError) Error¶
func (*FileOperationError) Unwrap¶
type RenderError¶
RenderError represents a template rendering failure
func (*RenderError) Error¶
func (*RenderError) Unwrap¶
type Result¶
Result represents the outcome of executing a step and can be registered to variables for use in subsequent steps via the "register" field.
Field usage varies by step type:
Shell steps:
- Stdout: captured standard output from the command
- Stderr: captured standard error from the command
- Rc: exit code (0 for success, non-zero for failure)
- Failed: true if Rc != 0
- Changed: always true (commands are assumed to make changes)
File steps (file with state: file or directory):
- Rc: 0 for success, 1 for failure
- Failed: true if file/directory operation failed
- Changed: true if file/directory was created or content modified
Template steps:
- Rc: 0 for success, 1 for failure
- Failed: true if template rendering or file write failed
- Changed: true if output file was created or content changed
Variable steps (vars, include_vars):
- All fields remain at default values (not currently used)
The Skipped field is reserved for future use but not currently set by any step type.
type Result struct {
// Stdout contains the standard output from shell commands.
// Only populated by shell steps.
Stdout string `json:"stdout"`
// Stderr contains the standard error from shell commands.
// Only populated by shell steps.
Stderr string `json:"stderr"`
// Rc is the return/exit code.
// For shell steps: the command's exit code (0 = success).
// For file/template steps: 0 for success, 1 for failure.
Rc int `json:"rc"`
// Failed indicates whether the step execution failed.
// Set to true when shell commands exit non-zero or when file/template operations error.
Failed bool `json:"failed"`
// Changed indicates whether the step made modifications to the system.
// Shell steps: always true (commands assumed to make changes).
// File steps: true if file/directory was created or modified.
// Template steps: true if output file was created or content changed.
Changed bool `json:"changed"`
// Skipped is reserved for future use to indicate skipped steps.
// Currently not set by any step type.
Skipped bool `json:"skipped"`
// Timing information
StartTime time.Time `json:"start_time,omitempty"`
EndTime time.Time `json:"end_time,omitempty"`
Duration time.Duration `json:"duration_ms,omitempty"` // Duration in time.Duration format
}
func NewResult¶
NewResult creates a new Result with default values.
func (*Result) RegisterTo¶
RegisterTo registers this result to the variables map under the given name. The result can be accessed using nested field syntax (e.g., "result.stdout", "result.rc") in templates and when conditions.
func (*Result) SetChanged¶
SetChanged marks whether the action made changes.
func (*Result) SetData¶
SetData sets custom result data. This merges the provided data into the result's ToMap output.
func (*Result) SetFailed¶
SetFailed marks the result as failed.
func (*Result) SetStderr¶
SetStderr sets the stderr output.
func (*Result) SetStdout¶
SetStdout sets the stdout output.
func (*Result) Status¶
Status returns a string representation of the result status.
func (*Result) ToMap¶
ToMap converts Result to a map for use in template variables.
type SetupError¶
SetupError represents infrastructure or configuration setup failures
type SetupError struct {
Component string // "become", "timeout", "sudo", "user", "group"
Issue string // What went wrong
Cause error // Underlying error (optional)
}
func (*SetupError) Error¶
func (*SetupError) Unwrap¶
type StartConfig¶
StartConfig contains configuration for starting a mooncake execution.
type StartConfig struct {
ConfigFilePath string
VarsFilePath string
SudoPass string // Sudo password provided directly (use SudoPassFile for better security)
SudoPassFile string
AskBecomePass bool
InsecureSudoPass bool
Tags []string
DryRun bool
// Artifact configuration
ArtifactsDir string
CaptureFullOutput bool
MaxOutputBytes int
MaxOutputLines int
}
type StepValidationError¶
StepValidationError represents step parameter validation failure during execution
func (*StepValidationError) Error¶
Generated by gomarkdoc